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 &, TValId);
51 
52 /// extract integral range from the given value if possible, fail otherwise
53 bool rngFromVal(IR::Range *pDst, const SymHeapCore &, TValId);
54 
55 /// extract either offset range, or integral range from the given value
56 bool anyRangeFromVal(IR::Range *pDst, const SymHeap &, TValId);
57 
58 /// extract string literal from the given value if possible, fail otherwise
59 bool stringFromVal(std::string *pDst, const SymHeap &, 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
75 
76 /// true for TS_REGION and TS_FIRST
78 
79 /// true for TS_REGION and TS_LAST
81 
82 bool compareIntRanges(
83  bool *pDst,
84  enum cl_binop_e code,
85  const IR::Range &range1,
86  const IR::Range &range2);
87 
88 /// known to work only with TFldId/TValId
89 template <class TMap>
90 typename TMap::mapped_type roMapLookup(
91  const TMap &roMap,
92  const typename TMap::key_type id)
93 {
94  if (id <= 0)
95  return id;
96 
97  typename TMap::const_iterator iter = roMap.find(id);
98  return (roMap.end() == iter)
99  ? static_cast<typename TMap::mapped_type>(-1)
100  : iter->second;
101 }
102 
103 bool translateValId(
104  TValId *pVal,
105  SymHeapCore &dst,
106  const SymHeapCore &src,
107  const TValMap &valMap);
108 
110  SymHeapCore &dst,
111  const SymHeapCore &src,
112  TValId valProto);
113 
115  SymHeap &dst,
116  const TObjId dstObj,
117  const FldHandle &srcField)
118 {
119  // gather properties of the field in 'src'
120  const TOffset off = srcField.offset();
121  const TObjType clt = srcField.type();
122 
123  // use them to obtain the corresponding object in 'dst'
124  return FldHandle(dst, dstObj, clt, off);
125 }
126 
127 inline TValId valOfPtr(SymHeap &sh, TObjId obj, TOffset off)
128 {
129  const PtrHandle ptr(sh, obj, off);
130  return ptr.value();
131 }
132 
133 inline bool isAbstractObject(const SymHeap &sh, const TObjId obj)
134 {
135  const EObjKind kind = sh.objKind(obj);
136  return (OK_REGION != kind);
137 }
138 
139 inline bool isPossibleToDeref(const SymHeapCore &sh, const TValId val)
140 {
141  const EValueTarget code = sh.valTarget(val);
142  if (VT_RANGE == code)
143  // address with offset ranges are not allowed to be dreferenced for now
144  return false;
145 
146  const TObjId obj = sh.objByAddr(val);
147  return sh.isValid(obj);
148 }
149 
150 inline bool isVarAlive(SymHeap &sh, const CVar &cv)
151 {
152  const TObjId obj = sh.regionByVar(cv, /* createIfNeeded */ false);
153  return (OBJ_INVALID != obj);
154 }
155 
156 void initGlVar(SymHeap &sh, const CVar &cv);
157 
158 inline TObjId nextObj(SymHeap &sh, TObjId obj, TOffset offNext)
159 {
160  if (!sh.isValid(obj))
161  return OBJ_INVALID;
162 
163  const TValId valNext = valOfPtr(sh, obj, offNext);
164  return sh.objByAddr(valNext);
165 }
166 
167 inline bool areValProtosEqual(
168  const SymHeapCore &sh1,
169  const SymHeapCore &sh2,
170  const TValId v1,
171  const TValId v2)
172 {
173  if (v1 == v2)
174  // matches trivially
175  return true;
176 
177  if (v1 <= 0 || v2 <= 0)
178  // special values have to match
179  return false;
180 
181  const EValueTarget code1 = sh1.valTarget(v1);
182  const EValueTarget code2 = sh2.valTarget(v2);
183 
184  if (VT_UNKNOWN != code1 || VT_UNKNOWN != code2)
185  // for now, we handle only unknown values here
186  return false;
187 
188  CL_BREAK_IF(sh1.valOffset(v1) || sh2.valOffset(v2));
189 
190  // just compare kinds of unknown values
191  const EValueOrigin origin1 = sh1.valOrigin(v1);
192  const EValueOrigin origin2 = sh2.valOrigin(v2);
193  return (origin1 == origin2);
194 }
195 
196 inline bool areUniBlocksEqual(
197  const SymHeap &sh1,
198  const SymHeap &sh2,
199  const UniformBlock &bl1,
200  const UniformBlock &bl2)
201 {
202  if (bl1.off != bl2.off)
203  // offset mismatch
204  return false;
205 
206  if (bl1.size != bl2.size)
207  // size mismatch
208  return false;
209 
210  // compare value prototypes
211  return areValProtosEqual(sh1, sh2, bl1.tplValue, bl2.tplValue);
212 }
213 
214 template <class TDst, typename TInserter>
216  TDst &dst,
217  const SymHeap &sh,
218  TInserter ins)
219 {
220  TObjList vars;
221  sh.gatherObjects(vars, isProgramVar);
222 
223  BOOST_FOREACH(const TObjId obj, vars) {
224  if (OBJ_RETURN == obj)
225  continue;
226 
227  (dst.*ins)(sh.cVarByObject(obj));
228  }
229 }
230 
231 inline void gatherProgramVars(
232  TCVarList &dst,
233  const SymHeap &sh)
234 {
235 #if defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 3)
236  // workaround for gcc-4.3.x parsing problem on std::vector::push_back()
237  void (TCVarList::*ins)(const CVar &&) = &TCVarList::push_back;
238 #else
239  void (TCVarList::*ins)(const CVar &) = &TCVarList::push_back;
240 #endif
241  gatherProgramVarsCore(dst, sh, ins);
242 }
243 
244 inline void gatherProgramVars(
245  TCVarSet &dst,
246  const SymHeap &sh)
247 {
248  typedef std::pair<TCVarSet::iterator, bool> TRet;
249  TRet (TCVarSet::*ins)(const CVar &) = &TCVarSet::insert;
250  gatherProgramVarsCore(dst, sh, ins);
251 }
252 
253 /// take the given visitor through all live objects
254 template <class THeap, class TVisitor>
255 bool /* complete */ traverseLiveFields(
256  THeap &sh,
257  const TObjId obj,
258  TVisitor &visitor)
259 {
260  // check that we got a valid object
261  CL_BREAK_IF(OBJ_INVALID == obj);
262 
263  // gather live fields
264  FldList fields;
265  sh.gatherLiveFields(fields, obj);
266 
267  // guide the visitor through the fields
268  BOOST_FOREACH(const FldHandle &fld, fields)
269  if (!/* continue */visitor(fld))
270  return false;
271 
272  // all fields traversed successfully
273  return true;
274 }
275 
276 /// take the given visitor through all uniform blocks
277 template <class THeap, class TVisitor>
278 bool /* complete */ traverseUniformBlocks(
279  THeap &sh,
280  const TObjId obj,
281  TVisitor &visitor)
282 {
283  // check that we got a valid root object
284  CL_BREAK_IF(OBJ_INVALID == obj);
285 
286  TUniBlockMap bMap;
287  sh.gatherUniformBlocks(bMap, obj);
288  BOOST_FOREACH(TUniBlockMap::const_reference bItem, bMap) {
289  if (!visitor(sh, /* UniformBlock */ bItem.second))
290  // traversal cancelled by visitor
291  return false;
292  }
293 
294  // done
295  return true;
296 }
297 
298 /// take the given visitor through all live objects object-wise
299 template <unsigned N, class THeap, class TVisitor>
300 bool /* complete */ traverseLiveFieldsGeneric(
301  THeap *const heaps[N],
302  const TObjId objs[N],
303  TVisitor &visitor)
304 {
305  // collect all live objects from everywhere
306  typedef std::pair<TOffset, TObjType> TItem;
307  std::set<TItem> all;
308  for (unsigned i = 0; i < N; ++i) {
309  SymHeap &sh = *heaps[i];
310  const TObjId obj = objs[i];
311  if (OBJ_INVALID == obj)
312  continue;
313 
314  FldList fields;
315  sh.gatherLiveFields(fields, obj);
316  BOOST_FOREACH(const FldHandle &fld, fields) {
317  const TOffset off = fld.offset();
318  const TObjType clt = fld.type();
319  const TItem item(off, clt);
320  all.insert(item);
321  }
322  }
323 
324  // go through all live objects
325  BOOST_FOREACH(const TItem &item, all) {
326  const TOffset off = item.first;
327  const TObjType clt = item.second;
328 
329  FldHandle fields[N];
330  for (unsigned i = 0; i < N; ++i) {
331  SymHeap &sh = *heaps[i];
332  const TObjId obj = objs[i];
333  fields[i] = FldHandle(sh, obj, clt, off);
334  }
335 
336  if (!visitor(fields))
337  // traversal cancelled by visitor
338  return false;
339  }
340 
341  // done
342  return true;
343 }
344 
345 /// take the given visitor through all live objects object-wise
346 template <unsigned N, class THeap, class TVisitor>
347 bool /* complete */ traverseLiveFields(
348  THeap &sh,
349  const TObjId objs[N],
350  TVisitor &visitor)
351 {
352  THeap *heaps[N];
353  for (unsigned i = 0; i < N; ++i)
354  heaps[i] = &sh;
355 
356  return traverseLiveFieldsGeneric<N>(heaps, objs, visitor);
357 }
358 
359 /// (OBJ_INVALID != pointingFrom) means 'pointing from anywhere'
360 bool redirectRefs(
361  SymHeap &sh,
362  TObjId pointingFrom,
363  TObjId pointingTo,
364  ETargetSpecifier pointingWith,
365  TObjId redirectTo,
366  ETargetSpecifier redirectWith,
367  TOffset offHead = 0);
368 
370  SymHeap &sh,
371  const TObjSet &pointingNotFrom,
372  TObjId pointingTo,
373  TObjId redirectTo,
374  ETargetSpecifier redirectWith,
375  bool (*tsFilter)(ETargetSpecifier) = 0);
376 
378  SymHeap &sh,
379  TObjId ofObj,
380  TObjId toObj);
381 
382 /// take the given visitor through all live program variables in all heaps
383 template <unsigned N_DST, unsigned N_SRC, class THeap, class TVisitor>
384 bool /* complete */ traverseProgramVarsGeneric(
385  THeap *const heaps[N_DST + N_SRC],
386  TVisitor &visitor,
387  const bool allowRecovery = false)
388 {
389  const unsigned N_TOTAL = N_DST + N_SRC;
390  BOOST_STATIC_ASSERT(N_DST < N_TOTAL);
391 
392  // start with all program variables of the first SRC heap
393  TCVarSet all;
394  gatherProgramVars(all, *heaps[/* src0 */ N_DST]);
395 #ifndef NDEBUG
396  bool tryingRecover = false;
397 #endif
398  // try to match variables from the other heaps if possible
399  for (unsigned i = /* src1 */ 1 + N_DST; i < N_TOTAL; ++i) {
400  const SymHeap &sh = *heaps[i];
401 
402  TObjList live;
403  sh.gatherObjects(live, isProgramVar);
404  BOOST_FOREACH(const TObjId obj, live) {
405  if (OBJ_RETURN == obj)
406  continue;
407 
408  const CVar cv(sh.cVarByObject(obj));
409  if (!insertOnce(all, cv))
410  continue;
411 
412  // variable mismatch in src heaps
413  if (!allowRecovery)
414  return false;
415 
416  if (/* gl var */ !cv.inst)
417  // asymmetric join of gl variables would break everything
418  return false;
419 #ifndef NDEBUG
420  tryingRecover = true;
421 #endif
422  }
423 
424  if (live.size() == all.size())
425  continue;
426 
427  // variable mismatch in src heaps
428  if (!allowRecovery)
429  return false;
430 #ifndef NDEBUG
431  tryingRecover = true;
432 #endif
433  }
434 
435  // go through all program variables
436  BOOST_FOREACH(const CVar &cv, all) {
437  TObjId objs[N_TOTAL];
438  for (signed i = 0; i < (signed)N_TOTAL; ++i) {
439  SymHeap &sh = *heaps[i];
440 
441  const bool isDst = (i < (signed)N_DST);
442  const bool isAsym = !isDst && !isVarAlive(sh, cv);
443  if (isAsym && /* gl var */ !cv.inst)
444  // asymmetric join of gl variables would break everything
445  return false;
446 
447  const bool createIfNeeded = isDst || allowRecovery;
448  const TObjId reg = sh.regionByVar(cv, createIfNeeded);
449 
450  if (isAsym) {
451  // we have created a local variable in a _src_ heap
452  CL_BREAK_IF(!tryingRecover);
453 
454  const TValId valInval = sh.valCreate(VT_UNKNOWN, VO_UNKNOWN);
455  const TSizeRange size = sh.objSize(reg);
456  CL_BREAK_IF(!isSingular(size));
457 
458  const UniformBlock ub = {
459  /* off */ 0,
460  /* size */ size.lo,
461  /* tplValue */ valInval,
462  };
463 
464  // mark its contents explicitly as unknown, not (un)initialized
465  sh.writeUniformBlock(reg, ub);
466  }
467 
468  objs[i] = reg;
469  }
470 
471  if (!visitor(objs))
472  // traversal cancelled by visitor
473  return false;
474  }
475 
476  // done
477  return true;
478 }
479 
480 #endif /* H_GUARD_SYMUTIL_H */