klee
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Memory.cpp
Go to the documentation of this file.
1 //===-- Memory.cpp --------------------------------------------------------===//
2 //
3 // The KLEE Symbolic Virtual Machine
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "Common.h"
11 
12 #include "Memory.h"
13 
14 #include "Context.h"
15 #include "klee/Expr.h"
16 #include "klee/Solver.h"
17 #include "klee/util/BitArray.h"
18 
19 #include "ObjectHolder.h"
20 #include "MemoryManager.h"
21 
22 #if LLVM_VERSION_CODE >= LLVM_VERSION(3, 3)
23 #include <llvm/IR/Function.h>
24 #include <llvm/IR/Instruction.h>
25 #include <llvm/IR/Value.h>
26 #else
27 #include <llvm/Function.h>
28 #include <llvm/Instruction.h>
29 #include <llvm/Value.h>
30 #endif
31 
32 #include "llvm/Support/CommandLine.h"
33 #include "llvm/Support/raw_ostream.h"
34 
35 #include <cassert>
36 #include <sstream>
37 
38 using namespace llvm;
39 using namespace klee;
40 
41 namespace {
42  cl::opt<bool>
43  UseConstantArrays("use-constant-arrays",
44  cl::init(true));
45 }
46 
47 /***/
48 
49 ObjectHolder::ObjectHolder(const ObjectHolder &b) : os(b.os) {
50  if (os) ++os->refCount;
51 }
52 
54  if (os) ++os->refCount;
55 }
56 
58  if (os && --os->refCount==0) delete os;
59 }
60 
62  if (b.os) ++b.os->refCount;
63  if (os && --os->refCount==0) delete os;
64  os = b.os;
65  return *this;
66 }
67 
68 /***/
69 
70 int MemoryObject::counter = 0;
71 
73  if (parent)
74  parent->markFreed(this);
75 }
76 
77 void MemoryObject::getAllocInfo(std::string &result) const {
78  llvm::raw_string_ostream info(result);
79 
80  info << "MO" << id << "[" << size << "]";
81 
82  if (allocSite) {
83  info << " allocated at ";
84  if (const Instruction *i = dyn_cast<Instruction>(allocSite)) {
85  info << i->getParent()->getParent()->getName() << "():";
86  info << *i;
87  } else if (const GlobalValue *gv = dyn_cast<GlobalValue>(allocSite)) {
88  info << "global:" << gv->getName();
89  } else {
90  info << "value:" << *allocSite;
91  }
92  } else {
93  info << " (no allocation info)";
94  }
95 
96  info.flush();
97 }
98 
99 /***/
100 
102  : copyOnWriteOwner(0),
103  refCount(0),
104  object(mo),
105  concreteStore(new uint8_t[mo->size]),
106  concreteMask(0),
107  flushMask(0),
108  knownSymbolics(0),
109  updates(0, 0),
110  size(mo->size),
111  readOnly(false) {
112  mo->refCount++;
113  if (!UseConstantArrays) {
114  // FIXME: Leaked.
115  static unsigned id = 0;
116  const Array *array = new Array("tmp_arr" + llvm::utostr(++id), size);
117  updates = UpdateList(array, 0);
118  }
119  memset(concreteStore, 0, size);
120 }
121 
122 
124  : copyOnWriteOwner(0),
125  refCount(0),
126  object(mo),
127  concreteStore(new uint8_t[mo->size]),
128  concreteMask(0),
129  flushMask(0),
130  knownSymbolics(0),
131  updates(array, 0),
132  size(mo->size),
133  readOnly(false) {
134  mo->refCount++;
135  makeSymbolic();
136  memset(concreteStore, 0, size);
137 }
138 
140  : copyOnWriteOwner(0),
141  refCount(0),
142  object(os.object),
143  concreteStore(new uint8_t[os.size]),
144  concreteMask(os.concreteMask ? new BitArray(*os.concreteMask, os.size) : 0),
145  flushMask(os.flushMask ? new BitArray(*os.flushMask, os.size) : 0),
146  knownSymbolics(0),
147  updates(os.updates),
148  size(os.size),
149  readOnly(false) {
150  assert(!os.readOnly && "no need to copy read only object?");
151  if (object)
152  object->refCount++;
153 
154  if (os.knownSymbolics) {
156  for (unsigned i=0; i<size; i++)
157  knownSymbolics[i] = os.knownSymbolics[i];
158  }
159 
160  memcpy(concreteStore, os.concreteStore, size*sizeof(*concreteStore));
161 }
162 
164  if (concreteMask) delete concreteMask;
165  if (flushMask) delete flushMask;
166  if (knownSymbolics) delete[] knownSymbolics;
167  delete[] concreteStore;
168 
169  if (object)
170  {
171  assert(object->refCount > 0);
172  object->refCount--;
173  if (object->refCount == 0)
174  {
175  delete object;
176  }
177  }
178 }
179 
180 /***/
181 
183  // Constant arrays are created lazily.
184  if (!updates.root) {
185  // Collect the list of writes, with the oldest writes first.
186 
187  // FIXME: We should be able to do this more efficiently, we just need to be
188  // careful to get the interaction with the cache right. In particular we
189  // should avoid creating UpdateNode instances we never use.
190  unsigned NumWrites = updates.head ? updates.head->getSize() : 0;
191  std::vector< std::pair< ref<Expr>, ref<Expr> > > Writes(NumWrites);
192  const UpdateNode *un = updates.head;
193  for (unsigned i = NumWrites; i != 0; un = un->next) {
194  --i;
195  Writes[i] = std::make_pair(un->index, un->value);
196  }
197 
198  std::vector< ref<ConstantExpr> > Contents(size);
199 
200  // Initialize to zeros.
201  for (unsigned i = 0, e = size; i != e; ++i)
202  Contents[i] = ConstantExpr::create(0, Expr::Int8);
203 
204  // Pull off as many concrete writes as we can.
205  unsigned Begin = 0, End = Writes.size();
206  for (; Begin != End; ++Begin) {
207  // Push concrete writes into the constant array.
208  ConstantExpr *Index = dyn_cast<ConstantExpr>(Writes[Begin].first);
209  if (!Index)
210  break;
211 
212  ConstantExpr *Value = dyn_cast<ConstantExpr>(Writes[Begin].second);
213  if (!Value)
214  break;
215 
216  Contents[Index->getZExtValue()] = Value;
217  }
218 
219  // FIXME: We should unique these, there is no good reason to create multiple
220  // ones.
221 
222  // Start a new update list.
223  // FIXME: Leaked.
224  static unsigned id = 0;
225  const Array *array = new Array("const_arr" + llvm::utostr(++id), size,
226  &Contents[0],
227  &Contents[0] + Contents.size());
228  updates = UpdateList(array, 0);
229 
230  // Apply the remaining (non-constant) writes.
231  for (; Begin != End; ++Begin)
232  updates.extend(Writes[Begin].first, Writes[Begin].second);
233  }
234 
235  return updates;
236 }
237 
239  if (concreteMask) delete concreteMask;
240  if (flushMask) delete flushMask;
241  if (knownSymbolics) delete[] knownSymbolics;
242  concreteMask = 0;
243  flushMask = 0;
244  knownSymbolics = 0;
245 }
246 
248  assert(!updates.head &&
249  "XXX makeSymbolic of objects with symbolic values is unsupported");
250 
251  // XXX simplify this, can just delete various arrays I guess
252  for (unsigned i=0; i<size; i++) {
253  markByteSymbolic(i);
254  setKnownSymbolic(i, 0);
255  markByteFlushed(i);
256  }
257 }
258 
260  makeConcrete();
261  memset(concreteStore, 0, size);
262 }
263 
265  makeConcrete();
266  for (unsigned i=0; i<size; i++) {
267  // randomly selected by 256 sided die
268  concreteStore[i] = 0xAB;
269  }
270 }
271 
272 /*
273 Cache Invariants
274 --
275 isByteKnownSymbolic(i) => !isByteConcrete(i)
276 isByteConcrete(i) => !isByteKnownSymbolic(i)
277 !isByteFlushed(i) => (isByteConcrete(i) || isByteKnownSymbolic(i))
278  */
279 
281  unsigned *base_r,
282  unsigned *size_r) const {
283  *base_r = 0;
284  *size_r = size;
285 }
286 
287 void ObjectState::flushRangeForRead(unsigned rangeBase,
288  unsigned rangeSize) const {
289  if (!flushMask) flushMask = new BitArray(size, true);
290 
291  for (unsigned offset=rangeBase; offset<rangeBase+rangeSize; offset++) {
292  if (!isByteFlushed(offset)) {
293  if (isByteConcrete(offset)) {
296  } else {
297  assert(isByteKnownSymbolic(offset) && "invalid bit set in flushMask");
299  knownSymbolics[offset]);
300  }
301 
302  flushMask->unset(offset);
303  }
304  }
305 }
306 
307 void ObjectState::flushRangeForWrite(unsigned rangeBase,
308  unsigned rangeSize) {
309  if (!flushMask) flushMask = new BitArray(size, true);
310 
311  for (unsigned offset=rangeBase; offset<rangeBase+rangeSize; offset++) {
312  if (!isByteFlushed(offset)) {
313  if (isByteConcrete(offset)) {
316  markByteSymbolic(offset);
317  } else {
318  assert(isByteKnownSymbolic(offset) && "invalid bit set in flushMask");
320  knownSymbolics[offset]);
321  setKnownSymbolic(offset, 0);
322  }
323 
324  flushMask->unset(offset);
325  } else {
326  // flushed bytes that are written over still need
327  // to be marked out
328  if (isByteConcrete(offset)) {
329  markByteSymbolic(offset);
330  } else if (isByteKnownSymbolic(offset)) {
331  setKnownSymbolic(offset, 0);
332  }
333  }
334  }
335 }
336 
337 bool ObjectState::isByteConcrete(unsigned offset) const {
338  return !concreteMask || concreteMask->get(offset);
339 }
340 
341 bool ObjectState::isByteFlushed(unsigned offset) const {
342  return flushMask && !flushMask->get(offset);
343 }
344 
345 bool ObjectState::isByteKnownSymbolic(unsigned offset) const {
346  return knownSymbolics && knownSymbolics[offset].get();
347 }
348 
349 void ObjectState::markByteConcrete(unsigned offset) {
350  if (concreteMask)
351  concreteMask->set(offset);
352 }
353 
354 void ObjectState::markByteSymbolic(unsigned offset) {
355  if (!concreteMask)
356  concreteMask = new BitArray(size, true);
357  concreteMask->unset(offset);
358 }
359 
360 void ObjectState::markByteUnflushed(unsigned offset) {
361  if (flushMask)
362  flushMask->set(offset);
363 }
364 
365 void ObjectState::markByteFlushed(unsigned offset) {
366  if (!flushMask) {
367  flushMask = new BitArray(size, false);
368  } else {
369  flushMask->unset(offset);
370  }
371 }
372 
373 void ObjectState::setKnownSymbolic(unsigned offset,
374  Expr *value /* can be null */) {
375  if (knownSymbolics) {
376  knownSymbolics[offset] = value;
377  } else {
378  if (value) {
380  knownSymbolics[offset] = value;
381  }
382  }
383 }
384 
385 /***/
386 
387 ref<Expr> ObjectState::read8(unsigned offset) const {
388  if (isByteConcrete(offset)) {
390  } else if (isByteKnownSymbolic(offset)) {
391  return knownSymbolics[offset];
392  } else {
393  assert(isByteFlushed(offset) && "unflushed byte without cache value");
394 
395  return ReadExpr::create(getUpdates(),
397  }
398 }
399 
401  assert(!isa<ConstantExpr>(offset) && "constant offset passed to symbolic read8");
402  unsigned base, size;
403  fastRangeCheckOffset(offset, &base, &size);
404  flushRangeForRead(base, size);
405 
406  if (size>4096) {
407  std::string allocInfo;
408  object->getAllocInfo(allocInfo);
409  klee_warning_once(0, "flushing %d bytes on read, may be slow and/or crash: %s",
410  size,
411  allocInfo.c_str());
412  }
413 
414  return ReadExpr::create(getUpdates(), ZExtExpr::create(offset, Expr::Int32));
415 }
416 
417 void ObjectState::write8(unsigned offset, uint8_t value) {
418  //assert(read_only == false && "writing to read-only object!");
419  concreteStore[offset] = value;
420  setKnownSymbolic(offset, 0);
421 
422  markByteConcrete(offset);
423  markByteUnflushed(offset);
424 }
425 
426 void ObjectState::write8(unsigned offset, ref<Expr> value) {
427  // can happen when ExtractExpr special cases
428  if (ConstantExpr *CE = dyn_cast<ConstantExpr>(value)) {
429  write8(offset, (uint8_t) CE->getZExtValue(8));
430  } else {
431  setKnownSymbolic(offset, value.get());
432 
433  markByteSymbolic(offset);
434  markByteUnflushed(offset);
435  }
436 }
437 
439  assert(!isa<ConstantExpr>(offset) && "constant offset passed to symbolic write8");
440  unsigned base, size;
441  fastRangeCheckOffset(offset, &base, &size);
442  flushRangeForWrite(base, size);
443 
444  if (size>4096) {
445  std::string allocInfo;
446  object->getAllocInfo(allocInfo);
447  klee_warning_once(0, "flushing %d bytes on read, may be slow and/or crash: %s",
448  size,
449  allocInfo.c_str());
450  }
451 
452  updates.extend(ZExtExpr::create(offset, Expr::Int32), value);
453 }
454 
455 /***/
456 
458  // Truncate offset to 32-bits.
459  offset = ZExtExpr::create(offset, Expr::Int32);
460 
461  // Check for reads at constant offsets.
462  if (ConstantExpr *CE = dyn_cast<ConstantExpr>(offset))
463  return read(CE->getZExtValue(32), width);
464 
465  // Treat bool specially, it is the only non-byte sized write we allow.
466  if (width == Expr::Bool)
467  return ExtractExpr::create(read8(offset), 0, Expr::Bool);
468 
469  // Otherwise, follow the slow general case.
470  unsigned NumBytes = width / 8;
471  assert(width == NumBytes * 8 && "Invalid write size!");
472  ref<Expr> Res(0);
473  for (unsigned i = 0; i != NumBytes; ++i) {
474  unsigned idx = Context::get().isLittleEndian() ? i : (NumBytes - i - 1);
475  ref<Expr> Byte = read8(AddExpr::create(offset,
477  Expr::Int32)));
478  Res = i ? ConcatExpr::create(Byte, Res) : Byte;
479  }
480 
481  return Res;
482 }
483 
484 ref<Expr> ObjectState::read(unsigned offset, Expr::Width width) const {
485  // Treat bool specially, it is the only non-byte sized write we allow.
486  if (width == Expr::Bool)
487  return ExtractExpr::create(read8(offset), 0, Expr::Bool);
488 
489  // Otherwise, follow the slow general case.
490  unsigned NumBytes = width / 8;
491  assert(width == NumBytes * 8 && "Invalid write size!");
492  ref<Expr> Res(0);
493  for (unsigned i = 0; i != NumBytes; ++i) {
494  unsigned idx = Context::get().isLittleEndian() ? i : (NumBytes - i - 1);
495  ref<Expr> Byte = read8(offset + idx);
496  Res = i ? ConcatExpr::create(Byte, Res) : Byte;
497  }
498 
499  return Res;
500 }
501 
503  // Truncate offset to 32-bits.
504  offset = ZExtExpr::create(offset, Expr::Int32);
505 
506  // Check for writes at constant offsets.
507  if (ConstantExpr *CE = dyn_cast<ConstantExpr>(offset)) {
508  write(CE->getZExtValue(32), value);
509  return;
510  }
511 
512  // Treat bool specially, it is the only non-byte sized write we allow.
513  Expr::Width w = value->getWidth();
514  if (w == Expr::Bool) {
515  write8(offset, ZExtExpr::create(value, Expr::Int8));
516  return;
517  }
518 
519  // Otherwise, follow the slow general case.
520  unsigned NumBytes = w / 8;
521  assert(w == NumBytes * 8 && "Invalid write size!");
522  for (unsigned i = 0; i != NumBytes; ++i) {
523  unsigned idx = Context::get().isLittleEndian() ? i : (NumBytes - i - 1);
524  write8(AddExpr::create(offset, ConstantExpr::create(idx, Expr::Int32)),
525  ExtractExpr::create(value, 8 * i, Expr::Int8));
526  }
527 }
528 
529 void ObjectState::write(unsigned offset, ref<Expr> value) {
530  // Check for writes of constant values.
531  if (ConstantExpr *CE = dyn_cast<ConstantExpr>(value)) {
532  Expr::Width w = CE->getWidth();
533  if (w <= 64) {
534  uint64_t val = CE->getZExtValue();
535  switch (w) {
536  default: assert(0 && "Invalid write size!");
537  case Expr::Bool:
538  case Expr::Int8: write8(offset, val); return;
539  case Expr::Int16: write16(offset, val); return;
540  case Expr::Int32: write32(offset, val); return;
541  case Expr::Int64: write64(offset, val); return;
542  }
543  }
544  }
545 
546  // Treat bool specially, it is the only non-byte sized write we allow.
547  Expr::Width w = value->getWidth();
548  if (w == Expr::Bool) {
549  write8(offset, ZExtExpr::create(value, Expr::Int8));
550  return;
551  }
552 
553  // Otherwise, follow the slow general case.
554  unsigned NumBytes = w / 8;
555  assert(w == NumBytes * 8 && "Invalid write size!");
556  for (unsigned i = 0; i != NumBytes; ++i) {
557  unsigned idx = Context::get().isLittleEndian() ? i : (NumBytes - i - 1);
558  write8(offset + idx, ExtractExpr::create(value, 8 * i, Expr::Int8));
559  }
560 }
561 
562 void ObjectState::write16(unsigned offset, uint16_t value) {
563  unsigned NumBytes = 2;
564  for (unsigned i = 0; i != NumBytes; ++i) {
565  unsigned idx = Context::get().isLittleEndian() ? i : (NumBytes - i - 1);
566  write8(offset + idx, (uint8_t) (value >> (8 * i)));
567  }
568 }
569 
570 void ObjectState::write32(unsigned offset, uint32_t value) {
571  unsigned NumBytes = 4;
572  for (unsigned i = 0; i != NumBytes; ++i) {
573  unsigned idx = Context::get().isLittleEndian() ? i : (NumBytes - i - 1);
574  write8(offset + idx, (uint8_t) (value >> (8 * i)));
575  }
576 }
577 
578 void ObjectState::write64(unsigned offset, uint64_t value) {
579  unsigned NumBytes = 8;
580  for (unsigned i = 0; i != NumBytes; ++i) {
581  unsigned idx = Context::get().isLittleEndian() ? i : (NumBytes - i - 1);
582  write8(offset + idx, (uint8_t) (value >> (8 * i)));
583  }
584 }
585 
587  llvm::errs() << "-- ObjectState --\n";
588  llvm::errs() << "\tMemoryObject ID: " << object->id << "\n";
589  llvm::errs() << "\tRoot Object: " << updates.root << "\n";
590  llvm::errs() << "\tSize: " << size << "\n";
591 
592  llvm::errs() << "\tBytes:\n";
593  for (unsigned i=0; i<size; i++) {
594  llvm::errs() << "\t\t["<<i<<"]"
595  << " concrete? " << isByteConcrete(i)
596  << " known-sym? " << isByteKnownSymbolic(i)
597  << " flushed? " << isByteFlushed(i) << " = ";
598  ref<Expr> e = read8(i);
599  llvm::errs() << e << "\n";
600  }
601 
602  llvm::errs() << "\tUpdates:\n";
603  for (const UpdateNode *un=updates.head; un; un=un->next) {
604  llvm::errs() << "\t\t[" << un->index << "] = " << un->value << "\n";
605  }
606 }
T * get() const
Definition: Ref.h:69
void void void void klee_warning_once(const void *id, const char *msg,...) __attribute__((format(printf
Definition: Common.cpp:90
void extend(const ref< Expr > &index, const ref< Expr > &value)
Definition: Updates.cpp:90
bool isLittleEndian() const
Definition: Context.h:38
ref< Expr > index
Definition: Expr.h:578
static ref< Expr > create(ref< Expr > e, unsigned bitOff, Width w)
Creates an ExtractExpr with the given bit offset and width.
Definition: Expr.cpp:610
const UpdateList & getUpdates() const
Definition: Memory.cpp:182
static ref< Expr > create(const UpdateList &updates, ref< Expr > i)
Definition: Expr.cpp:499
void markFreed(MemoryObject *mo)
MemoryManager * parent
Definition: Memory.h:56
bool get(unsigned idx)
Definition: BitArray.h:34
bool isByteFlushed(unsigned offset) const
Definition: Memory.cpp:341
void setKnownSymbolic(unsigned offset, Expr *value)
Definition: Memory.cpp:373
void unset(unsigned idx)
Definition: BitArray.h:36
static const Width Int16
Definition: Expr.h:99
static ref< Expr > create(const ref< Expr > &l, const ref< Expr > &r)
Definition: Expr.cpp:558
void fastRangeCheckOffset(ref< Expr > offset, unsigned *base_r, unsigned *size_r) const
Definition: Memory.cpp:280
ObjectHolder & operator=(const ObjectHolder &b)
Definition: Memory.cpp:61
Class representing a complete list of updates into an array.
Definition: Expr.h:655
uint64_t getZExtValue(unsigned bits=64) const
Definition: Expr.h:361
const llvm::Value * allocSite
Definition: Memory.h:61
ref< Expr > value
Definition: Expr.h:578
static const Width Int32
Definition: Expr.h:100
unsigned size
size in bytes
Definition: Memory.h:45
Class representing symbolic expressions.
Definition: Expr.h:88
static const Width Int8
Definition: Expr.h:98
const MemoryObject * object
Definition: Memory.h:156
bool isByteConcrete(unsigned offset) const
Definition: Memory.cpp:337
virtual Width getWidth() const =0
void write(unsigned offset, ref< Expr > value)
Definition: Memory.cpp:529
static const Width Int64
Definition: Expr.h:101
static const Context & get()
get - Return the global singleton instance of the Context.
Definition: Context.cpp:35
ObjectState(const MemoryObject *mo)
Definition: Memory.cpp:101
void markByteFlushed(unsigned offset)
Definition: Memory.cpp:365
void flushRangeForWrite(unsigned rangeBase, unsigned rangeSize)
Definition: Memory.cpp:307
const UpdateNode * head
pointer to the most recent update node
Definition: Expr.h:662
void initializeToZero()
Definition: Memory.cpp:259
static ref< ConstantExpr > create(uint64_t v, Width w)
Definition: Expr.h:412
void markByteConcrete(unsigned offset)
Definition: Memory.cpp:349
unsigned Width
The type of an expression is simply its width, in bits.
Definition: Expr.h:94
void makeConcrete()
Definition: Memory.cpp:238
BitArray * concreteMask
Definition: Memory.h:160
unsigned getSize() const
Definition: Expr.h:589
void set(unsigned idx)
Definition: BitArray.h:35
const UpdateNode * next
Definition: Expr.h:577
void markByteUnflushed(unsigned offset)
Definition: Memory.cpp:360
ref< Expr > * knownSymbolics
Definition: Memory.h:165
const Array * root
Definition: Expr.h:659
void write8(unsigned offset, uint8_t value)
Definition: Memory.cpp:417
Class representing a byte update of an array.
Definition: Expr.h:569
unsigned refCount
Definition: Memory.h:38
unsigned refCount
Definition: Memory.h:154
static const Width Bool
Definition: Expr.h:97
unsigned size
Definition: Memory.h:171
void write32(unsigned offset, uint32_t value)
Definition: Memory.cpp:570
void flushRangeForRead(unsigned rangeBase, unsigned rangeSize) const
Definition: Memory.cpp:287
void markByteSymbolic(unsigned offset)
Definition: Memory.cpp:354
bool isByteKnownSymbolic(unsigned offset) const
Definition: Memory.cpp:345
static int counter
Definition: Memory.h:37
uint8_t * concreteStore
Definition: Memory.h:158
void getAllocInfo(std::string &result) const
Get an identifying string for this allocation.
Definition: Memory.cpp:77
void write64(unsigned offset, uint64_t value)
Definition: Memory.cpp:578
ObjectState * os
Definition: ObjectHolder.h:17
UpdateList updates
Definition: Memory.h:168
ref< Expr > read8(unsigned offset) const
Definition: Memory.cpp:387
BitArray * flushMask
Definition: Memory.h:163
void write16(unsigned offset, uint16_t value)
Definition: Memory.cpp:562
ref< Expr > read(ref< Expr > offset, Expr::Width width) const
Definition: Memory.cpp:457
void makeSymbolic()
Definition: Memory.cpp:247
void initializeToRandom()
Definition: Memory.cpp:264