Predator  [unstable] git snapshot
symutil.hh
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2009-2011 Kamil Dudka <kdudka@redhat.com>
3  *
4  * This file is part of predator.
5  *
6  * predator is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * any later version.
10  *
11  * predator is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with predator. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef H_GUARD_SYMUTIL_H
21 #define H_GUARD_SYMUTIL_H
22 
23 /**
24  * @file symutil.hh
25  * some generic utilities working on top of a symbolic heap
26  * @todo API documentation
27  */
28 
29 #include "config.h"
30 
31 #include <cl/code_listener.h>
32 #include <cl/clutil.hh>
33 
34 #include "symheap.hh"
35 #include "util.hh"
36 
37 #include <set>
38 
39 #include <boost/foreach.hpp>
40 #include <boost/static_assert.hpp>
41 
42 inline TValId boolToVal(const bool b)
43 {
44  return (b)
45  ? VAL_TRUE
46  : VAL_FALSE;
47 }
48 
49 /// extract integral constant from the given value if possible, fail otherwise
50 bool numFromVal(IR::TInt *pDst, const SymHeapCore &, const TValId);
51 
52 /// extract integral range from the given value if possible, fail otherwise
53 bool rngFromVal(IR::Range *pDst, const SymHeapCore &, const TValId);
54 
55 /// extract either offset range, or integral range from the given value
56 bool anyRangeFromVal(IR::Range *pDst, const SymHeap &, const TValId);
57 
58 /// extract string literal from the given value if possible, fail otherwise
59 bool stringFromVal(const char **pDst, const SymHeap &, const TValId);
60 
61 void moveKnownValueToLeft(const SymHeap &sh, TValId &valA, TValId &valB);
62 
63 bool valInsideSafeRange(const SymHeapCore &sh, TValId val);
64 
65 bool canWriteDataPtrAt(const SymHeapCore &sh, TValId val);
66 
67 /// true if the given values are proven to be non-equal in non-abstract world
68 bool proveNeq(const SymHeap &sh, TValId v1, TValId v2);
69 
70 /// extract an integral range from an unwrapped CV_INT/CV_INT_RANGE custom value
71 const IR::Range& rngFromCustom(const CustomValue &);
72 
73 /// return size (in bytes) that we can safely write at the given addr
74 TSizeRange valSizeOfTarget(const SymHeapCore &, const TValId at);
75 
76 bool compareIntRanges(
77  bool *pDst,
78  const enum cl_binop_e code,
79  const IR::Range &range1,
80  const IR::Range &range2);
81 
82 /// known to work only with TFldId/TValId
83 template <class TMap>
84 typename TMap::mapped_type roMapLookup(
85  const TMap &roMap,
86  const typename TMap::key_type id)
87 {
88  if (id <= 0)
89  return id;
90 
91  typename TMap::const_iterator iter = roMap.find(id);
92  return (roMap.end() == iter)
93  ? static_cast<typename TMap::mapped_type>(-1)
94  : iter->second;
95 }
96 
97 bool translateValId(
98  TValId *pVal,
99  SymHeapCore &dst,
100  const SymHeapCore &src,
101  const TValMap &valMap);
102 
104  SymHeapCore &dst,
105  const SymHeapCore &src,
106  const TValId valProto);
107 
109  SymHeap &dst,
110  const TObjId dstObj,
111  const FldHandle &srcField)
112 {
113  // gather properties of the field in 'src'
114  const TOffset off = srcField.offset();
115  const TObjType clt = srcField.type();
116 
117  // use them to obtain the corresponding object in 'dst'
118  return FldHandle(dst, dstObj, clt, off);
119 }
120 
121 inline TValId valOfPtr(SymHeap &sh, TObjId obj, TOffset off)
122 {
123  const PtrHandle ptr(sh, obj, off);
124  return ptr.value();
125 }
126 
127 inline bool isAbstractObject(const SymHeap &sh, const TObjId obj)
128 {
129  const EObjKind kind = sh.objKind(obj);
130  return (OK_REGION != kind);
131 }
132 
133 inline bool isPossibleToDeref(const SymHeapCore &sh, const TValId val)
134 {
135  const EValueTarget code = sh.valTarget(val);
136  if (VT_RANGE == code)
137  // address with offset ranges are not allowed to be dreferenced for now
138  return false;
139 
140  const TObjId obj = sh.objByAddr(val);
141  return sh.isValid(obj);
142 }
143 
144 inline bool isVarAlive(SymHeap &sh, const CVar &cv)
145 {
146  const TObjId obj = sh.regionByVar(cv, /* createIfNeeded */ false);
147  return (OBJ_INVALID != obj);
148 }
149 
150 void initGlVar(SymHeap &sh, const CVar &cv);
151 
152 inline TObjId nextObj(SymHeap &sh, TObjId obj, TOffset offNext)
153 {
154  if (OBJ_INVALID == obj)
155  return OBJ_INVALID;
156 
157  const TValId valNext = valOfPtr(sh, obj, offNext);
158  return sh.objByAddr(valNext);
159 }
160 
161 inline bool areValProtosEqual(
162  const SymHeapCore &sh1,
163  const SymHeapCore &sh2,
164  const TValId v1,
165  const TValId v2)
166 {
167  if (v1 == v2)
168  // matches trivially
169  return true;
170 
171  if (v1 <= 0 || v2 <= 0)
172  // special values have to match
173  return false;
174 
175  const EValueTarget code1 = sh1.valTarget(v1);
176  const EValueTarget code2 = sh2.valTarget(v2);
177 
178  if (VT_UNKNOWN != code1 || VT_UNKNOWN != code2)
179  // for now, we handle only unknown values here
180  return false;
181 
182  CL_BREAK_IF(sh1.valOffset(v1) || sh2.valOffset(v2));
183 
184  // just compare kinds of unknown values
185  const EValueOrigin origin1 = sh1.valOrigin(v1);
186  const EValueOrigin origin2 = sh2.valOrigin(v2);
187  return (origin1 == origin2);
188 }
189 
190 inline bool areUniBlocksEqual(
191  const SymHeap &sh1,
192  const SymHeap &sh2,
193  const UniformBlock &bl1,
194  const UniformBlock &bl2)
195 {
196  if (bl1.off != bl2.off)
197  // offset mismatch
198  return false;
199 
200  if (bl1.size != bl2.size)
201  // size mismatch
202  return false;
203 
204  // compare value prototypes
205  return areValProtosEqual(sh1, sh2, bl1.tplValue, bl2.tplValue);
206 }
207 
208 template <class TDst, typename TInserter>
210  TDst &dst,
211  const SymHeap &sh,
212  TInserter ins)
213 {
214  TObjList vars;
215  sh.gatherObjects(vars, isProgramVar);
216 
217  BOOST_FOREACH(const TObjId obj, vars) {
218  if (OBJ_RETURN == obj)
219  continue;
220 
221  (dst.*ins)(sh.cVarByObject(obj));
222  }
223 }
224 
225 inline void gatherProgramVars(
226  TCVarList &dst,
227  const SymHeap &sh)
228 {
229 #if defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
230  // workaround for gcc-4.3.x parsing problem on std::vector::push_back()
231  void (TCVarList::*ins)(const CVar &&) = &TCVarList::push_back;
232 #else
233  void (TCVarList::*ins)(const CVar &) = &TCVarList::push_back;
234 #endif
235  gatherProgramVarsCore(dst, sh, ins);
236 }
237 
238 inline void gatherProgramVars(
239  TCVarSet &dst,
240  const SymHeap &sh)
241 {
242  typedef std::pair<TCVarSet::iterator, bool> TRet;
243  TRet (TCVarSet::*ins)(const CVar &) = &TCVarSet::insert;
244  gatherProgramVarsCore(dst, sh, ins);
245 }
246 
247 /// take the given visitor through all live pointers
248 template <class THeap, class TVisitor, typename TMethod>
249 bool /* complete */ traverseCore(
250  THeap &sh,
251  const TObjId obj,
252  TVisitor &visitor,
253  TMethod method)
254 {
255  // check that we got a valid object
256  CL_BREAK_IF(OBJ_INVALID == obj);
257 
258  // gather live fields using the requested method
259  FldList fields;
260  (sh.*method)(fields, obj);
261 
262  // guide the visitor through the fields
263  BOOST_FOREACH(const FldHandle &fld, fields)
264  if (!/* continue */visitor(fld))
265  return false;
266 
267  // all fields traversed successfully
268  return true;
269 }
270 
271 /// take the given visitor through all live pointers
272 template <class THeap, class TVisitor>
273 bool /* complete */ traverseLivePtrs(
274  THeap &sh,
275  const TObjId obj,
276  TVisitor &visitor)
277 {
278  return traverseCore(sh, obj, visitor, &SymHeap::gatherLivePointers);
279 }
280 
281 /// take the given visitor through all live objects
282 template <class THeap, class TVisitor>
283 bool /* complete */ traverseLiveFields(
284  THeap &sh,
285  const TObjId obj,
286  TVisitor &visitor)
287 {
288  return traverseCore(sh, obj, visitor, &SymHeap::gatherLiveFields);
289 }
290 
291 /// take the given visitor through all uniform blocks
292 template <class THeap, class TVisitor>
293 bool /* complete */ traverseUniformBlocks(
294  THeap &sh,
295  const TObjId obj,
296  TVisitor &visitor)
297 {
298  // check that we got a valid root object
299  CL_BREAK_IF(OBJ_INVALID == obj);
300 
301  TUniBlockMap bMap;
302  sh.gatherUniformBlocks(bMap, obj);
303  BOOST_FOREACH(TUniBlockMap::const_reference bItem, bMap) {
304  if (!visitor(sh, /* UniformBlock */ bItem.second))
305  // traversal cancelled by visitor
306  return false;
307  }
308 
309  // done
310  return true;
311 }
312 
313 /// take the given visitor through all live objects object-wise
314 template <unsigned N, class THeap, class TVisitor>
315 bool /* complete */ traverseLiveFieldsGeneric(
316  THeap *const heaps[N],
317  const TObjId objs[N],
318  TVisitor &visitor)
319 {
320  // collect all live objects from everywhere
321  typedef std::pair<TOffset, TObjType> TItem;
322  std::set<TItem> all;
323  for (unsigned i = 0; i < N; ++i) {
324  SymHeap &sh = *heaps[i];
325  const TObjId obj = objs[i];
326  if (OBJ_INVALID == obj)
327  continue;
328 
329  FldList fields;
330  sh.gatherLiveFields(fields, obj);
331  BOOST_FOREACH(const FldHandle &fld, fields) {
332  const TOffset off = fld.offset();
333  const TObjType clt = fld.type();
334  const TItem item(off, clt);
335  all.insert(item);
336  }
337  }
338 
339  // go through all live objects
340  BOOST_FOREACH(const TItem &item, all) {
341  const TOffset off = item.first;
342  const TObjType clt = item.second;
343 
344  FldHandle fields[N];
345  for (unsigned i = 0; i < N; ++i) {
346  SymHeap &sh = *heaps[i];
347  const TObjId obj = objs[i];
348  fields[i] = FldHandle(sh, obj, clt, off);
349  }
350 
351  if (!visitor(fields))
352  // traversal cancelled by visitor
353  return false;
354  }
355 
356  // done
357  return true;
358 }
359 
360 /// take the given visitor through all live objects object-wise
361 template <unsigned N, class THeap, class TVisitor>
362 bool /* complete */ traverseLiveFields(
363  THeap &sh,
364  const TObjId objs[N],
365  TVisitor &visitor)
366 {
367  THeap *heaps[N];
368  for (unsigned i = 0; i < N; ++i)
369  heaps[i] = &sh;
370 
371  return traverseLiveFieldsGeneric<N>(heaps, objs, visitor);
372 }
373 
374 /// (OBJ_INVALID != pointingFrom) means 'pointing from anywhere'
375 bool redirectRefs(
376  SymHeap &sh,
377  const TObjId pointingFrom,
378  const TObjId pointingTo,
379  const ETargetSpecifier pointingWith,
380  const TObjId redirectTo,
381  const ETargetSpecifier redirectWith,
382  const TOffset offHead = 0);
383 
385  SymHeap &sh,
386  const TObjSet &pointingNotFrom,
387  const TObjId pointingTo,
388  const TObjId redirectTo,
389  const ETargetSpecifier redirectWith,
390  bool (*tsFilter)(ETargetSpecifier) = 0);
391 
393  SymHeap &sh,
394  const TObjId ofObj,
395  const TObjId toObj);
396 
397 /// take the given visitor through all live program variables in all heaps
398 template <unsigned N_DST, unsigned N_SRC, class THeap, class TVisitor>
399 bool /* complete */ traverseProgramVarsGeneric(
400  THeap *const heaps[N_DST + N_SRC],
401  TVisitor &visitor,
402  const bool allowRecovery = false)
403 {
404  const unsigned N_TOTAL = N_DST + N_SRC;
405  BOOST_STATIC_ASSERT(N_DST < N_TOTAL);
406 
407  // start with all program variables of the first SRC heap
408  TCVarSet all;
409  gatherProgramVars(all, *heaps[/* src0 */ N_DST]);
410 #ifndef NDEBUG
411  bool tryingRecover = false;
412 #endif
413  // try to match variables from the other heaps if possible
414  for (unsigned i = /* src1 */ 1 + N_DST; i < N_TOTAL; ++i) {
415  const SymHeap &sh = *heaps[i];
416 
417  TObjList live;
418  sh.gatherObjects(live, isProgramVar);
419  BOOST_FOREACH(const TObjId obj, live) {
420  if (OBJ_RETURN == obj)
421  continue;
422 
423  const CVar cv(sh.cVarByObject(obj));
424  if (!insertOnce(all, cv))
425  continue;
426 
427  // variable mismatch in src heaps
428  if (!allowRecovery)
429  return false;
430 
431  if (/* gl var */ !cv.inst)
432  // asymmetric join of gl variables would break everything
433  return false;
434 #ifndef NDEBUG
435  tryingRecover = true;
436 #endif
437  }
438 
439  if (live.size() == all.size())
440  continue;
441 
442  // variable mismatch in src heaps
443  if (!allowRecovery)
444  return false;
445 #ifndef NDEBUG
446  tryingRecover = true;
447 #endif
448  }
449 
450  // go through all program variables
451  BOOST_FOREACH(const CVar &cv, all) {
452  TObjId objs[N_TOTAL];
453  for (signed i = 0; i < (signed)N_TOTAL; ++i) {
454  SymHeap &sh = *heaps[i];
455 
456  const bool isDst = (i < (signed)N_DST);
457  const bool isAsym = !isDst && !isVarAlive(sh, cv);
458  if (isAsym && /* gl var */ !cv.inst)
459  // asymmetric join of gl variables would break everything
460  return false;
461 
462  const bool createIfNeeded = isDst || allowRecovery;
463  const TObjId reg = sh.regionByVar(cv, createIfNeeded);
464 
465  if (isAsym) {
466  // we have created a local variable in a _src_ heap
467  CL_BREAK_IF(!tryingRecover);
468 
469  const TValId valInval = sh.valCreate(VT_UNKNOWN, VO_UNKNOWN);
470  const TSizeRange size = sh.objSize(reg);
471  CL_BREAK_IF(!isSingular(size));
472 
473  const UniformBlock ub = {
474  /* off */ 0,
475  /* size */ size.lo,
476  /* tplValue */ valInval,
477  };
478 
479  // mark its contents explicitly as unknown, not (un)initialized
480  sh.writeUniformBlock(reg, ub);
481  }
482 
483  objs[i] = reg;
484  }
485 
486  if (!visitor(objs))
487  // traversal cancelled by visitor
488  return false;
489  }
490 
491  // done
492  return true;
493 }
494 
495 #endif /* H_GUARD_SYMUTIL_H */