svf-tools 1.0.728 → 1.0.730

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,606 @@
1
+ //===- ConsExeState.cpp ----Constant Execution State-------------------------//
2
+ //
3
+ // SVF: Static Value-Flow Analysis
4
+ //
5
+ // Copyright (C) <2013-2022> <Yulei Sui>
6
+ //
7
+
8
+ // This program is free software: you can redistribute it and/or modify
9
+ // it under the terms of the GNU Affero General Public License as published by
10
+ // the Free Software Foundation, either version 3 of the License, or
11
+ // (at your option) any later version.
12
+
13
+ // This program is distributed in the hope that it will be useful,
14
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ // GNU Affero General Public License for more details.
17
+
18
+ // You should have received a copy of the GNU Affero General Public License
19
+ // along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ //
21
+ //===----------------------------------------------------------------------===//
22
+
23
+ //
24
+ // Created by jiawei and xiao on 6/1/23.
25
+ //
26
+
27
+
28
+
29
+ #include "AbstractExecution/ConsExeState.h"
30
+ #include <iomanip>
31
+ #include "Util/Options.h"
32
+
33
+
34
+ using namespace SVF;
35
+ using namespace SVFUtil;
36
+
37
+
38
+ ConsExeState ConsExeState::globalConsES(initExeState());
39
+
40
+ /*!
41
+ * Copy operator
42
+ * @param rhs
43
+ * @return
44
+ */
45
+ ConsExeState &ConsExeState::operator=(const ConsExeState &rhs)
46
+ {
47
+ if (*this != rhs)
48
+ {
49
+ _varToVal = rhs.getVarToVal();
50
+ _locToVal = rhs.getLocToVal();
51
+ ExeState::operator=(rhs);
52
+ }
53
+ return *this;
54
+ }
55
+
56
+ /*!
57
+ * Move operator
58
+ * @param rhs
59
+ * @return
60
+ */
61
+ ConsExeState &ConsExeState::operator=(ConsExeState &&rhs) noexcept
62
+ {
63
+ if (this != &rhs)
64
+ {
65
+ _varToVal = SVFUtil::move(rhs._varToVal);
66
+ _locToVal = SVFUtil::move(rhs._locToVal);
67
+ ExeState::operator=(std::move(rhs));
68
+ }
69
+ return *this;
70
+ }
71
+
72
+ /*!
73
+ * Overloading Operator==
74
+ * @param rhs
75
+ * @return
76
+ */
77
+ bool ConsExeState::operator==(const ConsExeState &rhs) const
78
+ {
79
+ // if values of variables are not changed, fix-point is reached
80
+ return ExeState::operator==(rhs) && eqVarToValMap(_varToVal, rhs.getVarToVal()) &&
81
+ eqVarToValMap(_locToVal, rhs.getLocToVal());
82
+ }
83
+
84
+ /*!
85
+ * Overloading Operator<
86
+ * @param rhs
87
+ * @return
88
+ */
89
+ bool ConsExeState::operator<(const ConsExeState &rhs) const
90
+ {
91
+ // judge from path constraint
92
+ if (lessThanVarToValMap(_varToVal, rhs.getVarToVal()) || lessThanVarToValMap(_locToVal, rhs.getLocToVal()))
93
+ return true;
94
+ return false;
95
+ }
96
+
97
+ u32_t ConsExeState::hash() const
98
+ {
99
+ size_t bas = ExeState::hash();
100
+ size_t h = getVarToVal().size() * 2;
101
+ SVF::Hash<SVF::u32_t> hf;
102
+ for (const auto &t: getVarToVal())
103
+ {
104
+ h ^= hf(t.first) + 0x9e3779b9 + (h << 6) + (h >> 2);
105
+ h ^= hf(t.second.id()) + 0x9e3779b9 + (h << 6) + (h >> 2);
106
+ }
107
+
108
+ size_t h2 = getVarToVal().size() * 2;
109
+
110
+ for (const auto &t: getLocToVal())
111
+ {
112
+ h2 ^= hf(t.first) + 0x9e3779b9 + (h2 << 6) + (h2 >> 2);
113
+ h2 ^= hf(t.second.id()) + 0x9e3779b9 + (h2 << 6) + (h2 >> 2);
114
+ }
115
+ SVF::Hash<std::pair<SVF::u32_t, SVF::u32_t>> pairH;
116
+
117
+ return pairH(std::make_pair(bas, pairH(std::make_pair(h, h2))));
118
+ }
119
+
120
+ /*!
121
+ * Build global execution state
122
+ * @param globES
123
+ * @param vars
124
+ */
125
+ void ConsExeState::buildGlobES(ConsExeState &globES, Set<u32_t> &vars)
126
+ {
127
+ for (const auto &varId: vars)
128
+ {
129
+ SingleAbsValue &expr = globES[varId];
130
+ if (expr.is_numeral() && isVirtualMemAddress(expr.get_numeral_int()))
131
+ {
132
+ if (globES.inLocalLocToVal(expr))
133
+ {
134
+ store(expr, globES.load(expr));
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ /*!
141
+ * Merge rhs into this
142
+ * @param rhs
143
+ * @return
144
+ */
145
+ bool ConsExeState::joinWith(const SVF::ConsExeState &rhs)
146
+ {
147
+ bool changed = ExeState::joinWith(rhs);
148
+ for (const auto &rhsItem: rhs._varToVal)
149
+ {
150
+ auto it = _varToVal.find(rhsItem.first);
151
+ // Intersection - lhs and rhs have the same var id
152
+ if (it != getVarToVal().end())
153
+ {
154
+ if (it->second.isTop() || rhsItem.second.isTop())
155
+ {
156
+ if (assign(it->second, SingleAbsValue::topConstant()))
157
+ changed = true;
158
+ }
159
+ else if (it->second.isBottom())
160
+ {
161
+ if (assign(it->second, rhsItem.second))
162
+ changed = true;
163
+ }
164
+ else if (rhsItem.second.isBottom())
165
+ {
166
+
167
+ }
168
+ else
169
+ {
170
+ if (!eq(it->second, rhsItem.second))
171
+ {
172
+ if (assign(it->second, SingleAbsValue::topConstant()))
173
+ changed = true;
174
+ }
175
+ }
176
+ }
177
+ else
178
+ {
179
+ _varToVal.emplace(rhsItem.first, rhsItem.second);
180
+ changed = true;
181
+ }
182
+ }
183
+
184
+ for (const auto &rhsItem: rhs._locToVal)
185
+ {
186
+ auto it = _locToVal.find(rhsItem.first);
187
+ // Intersection - lhs and rhs have the same var id
188
+ if (it != getLocToVal().end())
189
+ {
190
+ if (it->second.isTop() || rhsItem.second.isTop())
191
+ {
192
+ if (assign(it->second, SingleAbsValue::topConstant()))
193
+ changed = true;
194
+ }
195
+ else if (it->second.isBottom())
196
+ {
197
+ if (assign(it->second, rhsItem.second))
198
+ changed = true;
199
+ }
200
+ else if (rhsItem.second.isBottom())
201
+ {
202
+
203
+ }
204
+ else
205
+ {
206
+ if (!eq(it->second, rhsItem.second))
207
+ {
208
+ if (assign(it->second, SingleAbsValue::topConstant()))
209
+ changed = true;
210
+ }
211
+ }
212
+ }
213
+ else
214
+ {
215
+ _locToVal.emplace(rhsItem.first, rhsItem.second);
216
+ changed = true;
217
+ }
218
+ }
219
+ return changed;
220
+ }
221
+
222
+ /*!
223
+ * We should build a summary for each actual params to boost precision.
224
+ * The summary of callee only contains the side-effects of callee
225
+ * (input obj value, return value, global value) without irrelevant caller information.
226
+ * We apply the summary to the exestate at each callsite.
227
+ * @param summary
228
+ */
229
+ void ConsExeState::applySummary(const ConsExeState &summary)
230
+ {
231
+ for (const auto &item: summary._varToVal)
232
+ {
233
+ _varToVal[item.first] = item.second;
234
+ }
235
+ for (const auto &item: summary._locToVal)
236
+ {
237
+ _locToVal[item.first] = item.second;
238
+ }
239
+ for (const auto &item: summary._varToVAddrs)
240
+ {
241
+ _varToVAddrs[item.first] = item.second;
242
+ }
243
+ for (const auto &item: summary._locToVAddrs)
244
+ {
245
+ _locToVAddrs[item.first] = item.second;
246
+ }
247
+ }
248
+
249
+ /*!
250
+ * Print values of all expressions
251
+ */
252
+ void ConsExeState::printExprValues() const
253
+ {
254
+ std::cout << "\n";
255
+ std::cout.flags(std::ios::left);
256
+ std::cout << "\t-----------------Var and Value-----------------\n";
257
+ for (const auto &item: getVarToVal())
258
+ {
259
+ std::stringstream exprName;
260
+ exprName << "\tVar" << item.first;
261
+ std::cout << std::setw(20) << exprName.str();
262
+ const SingleAbsValue &sim = item.second.simplify();
263
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
264
+ {
265
+ std::cout << "\t\t Value: " << std::hex << "0x" << z3Expr2NumValue(sim) << "\n";
266
+ }
267
+ else
268
+ {
269
+ std::cout << "\t\t Value: " << std::dec << sim << "\n";
270
+ }
271
+ }
272
+ std::cout << "\t-----------------------------------------------\n";
273
+ std::cout << "\t-----------------Loc and Value-----------------\n";
274
+ for (const auto &item: getLocToVal())
275
+ {
276
+ std::stringstream exprName;
277
+ exprName << "\tVar" << item.first;
278
+ std::cout << std::setw(20) << exprName.str();
279
+ const SingleAbsValue &sim = item.second.simplify();
280
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
281
+ {
282
+ std::cout << "\t\t Value: " << std::hex << "0x" << z3Expr2NumValue(sim) << "\n";
283
+ }
284
+ else
285
+ {
286
+ std::cout << "\t\t Value: " << std::dec << sim << "\n";
287
+ }
288
+ }
289
+ std::cout << "\t-----------------------------------------------\n";
290
+ }
291
+
292
+ void ConsExeState::printExprValues(std::ostream &oss) const
293
+ {
294
+ oss << "\n";
295
+ oss.flags(std::ios::left);
296
+ oss << "\t-----------------Var and Value-----------------\n";
297
+ for (const auto &item: getVarToVal())
298
+ {
299
+ std::stringstream exprName;
300
+ exprName << "\tVar" << item.first;
301
+ oss << std::setw(20) << exprName.str();
302
+ const SingleAbsValue &sim = item.second.simplify();
303
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
304
+ {
305
+ oss << "\t\t Value: " << std::hex << "0x" << z3Expr2NumValue(sim) << "\n";
306
+ }
307
+ else
308
+ {
309
+ oss << "\t\t Value: " << std::dec << sim << "\n";
310
+ }
311
+ }
312
+ oss << "\t-----------------------------------------------\n";
313
+ oss << "\t-----------------Loc and Value-----------------\n";
314
+ for (const auto &item: getLocToVal())
315
+ {
316
+ std::stringstream exprName;
317
+ exprName << "\tVar" << item.first;
318
+ oss << std::setw(20) << exprName.str();
319
+ const SingleAbsValue &sim = item.second.simplify();
320
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
321
+ {
322
+ oss << "\t\t Value: " << std::hex << "0x" << z3Expr2NumValue(sim) << "\n";
323
+ }
324
+ else
325
+ {
326
+ oss << "\t\t Value: " << std::dec << sim << "\n";
327
+ }
328
+ }
329
+ oss << "\t-----------------------------------------------\n";
330
+ }
331
+
332
+ std::string ConsExeState::pcToString() const
333
+ {
334
+ std::stringstream exprName;
335
+ exprName << "Path Constraint:\n";
336
+ return SVFUtil::move(exprName.str());
337
+ }
338
+
339
+ std::string ConsExeState::varToString(u32_t valId) const
340
+ {
341
+ std::stringstream exprName;
342
+ auto it = getVarToVal().find(valId);
343
+ if (it == getVarToVal().end())
344
+ {
345
+ auto it2 = globalConsES._varToVal.find(valId);
346
+ if (it2 == globalConsES._varToVal.end())
347
+ {
348
+ exprName << "Var not in varToVal!\n";
349
+ }
350
+ else
351
+ {
352
+ const SingleAbsValue &sim = it2->second.simplify();
353
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
354
+ {
355
+ exprName << "addr: " << std::dec << getInternalID(z3Expr2NumValue(sim)) << "\n";
356
+ }
357
+ else
358
+ {
359
+ if (sim.is_numeral())
360
+ exprName << std::dec << z3Expr2NumValue(sim) << "\n";
361
+ else
362
+ exprName << sim.to_string() << "\n";
363
+ }
364
+ }
365
+ }
366
+ else
367
+ {
368
+ const SingleAbsValue &sim = it->second.simplify();
369
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
370
+ {
371
+ exprName << "addr: " << std::dec << getInternalID(z3Expr2NumValue(sim)) << "\n";
372
+ }
373
+ else
374
+ {
375
+ if (sim.is_numeral())
376
+ exprName << std::dec << z3Expr2NumValue(sim) << "\n";
377
+ else
378
+ exprName << sim.to_string() << "\n";
379
+ }
380
+ }
381
+ return SVFUtil::move(exprName.str());
382
+ }
383
+
384
+ std::string ConsExeState::locToString(u32_t objId) const
385
+ {
386
+ std::stringstream exprName;
387
+ auto it = getLocToVal().find(objId);
388
+ if (it == getLocToVal().end())
389
+ {
390
+ exprName << "Obj not in locToVal!\n";
391
+ }
392
+ else
393
+ {
394
+ const SingleAbsValue &sim = it->second.simplify();
395
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
396
+ {
397
+ exprName << "addr: " << std::dec << getInternalID(z3Expr2NumValue(sim)) << "\n";
398
+ }
399
+ else
400
+ {
401
+ if (sim.is_numeral())
402
+ exprName << std::dec << z3Expr2NumValue(sim) << "\n";
403
+ else
404
+ exprName << sim.to_string() << "\n";
405
+ }
406
+ }
407
+ return SVFUtil::move(exprName.str());
408
+ }
409
+
410
+ std::string ConsExeState::toString() const
411
+ {
412
+ std::stringstream exprName;
413
+ exprName << pcToString();
414
+ exprName << "VarToVal:\n";
415
+ for (const auto &item: getVarToVal())
416
+ {
417
+ exprName << "Var" << std::to_string(item.first) << ":\n";
418
+ const SingleAbsValue &sim = item.second.simplify();
419
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
420
+ {
421
+ exprName << " \tValue" << std::dec << getInternalID(z3Expr2NumValue(sim)) << "\n";
422
+ }
423
+ else
424
+ {
425
+ exprName << " \tValue" << std::dec << z3Expr2NumValue(sim) << "\n";
426
+ }
427
+ }
428
+
429
+ exprName << "LocToVal:\n";
430
+ for (const auto &item: getLocToVal())
431
+ {
432
+ exprName << "Var" << std::to_string(item.first) << ":\n";
433
+ const SingleAbsValue &sim = item.second.simplify();
434
+ if (sim.is_numeral() && isVirtualMemAddress(z3Expr2NumValue(sim)))
435
+ {
436
+ exprName << " \tValue" << std::dec << getInternalID(z3Expr2NumValue(sim)) << "\n";
437
+ }
438
+ else
439
+ {
440
+ exprName << " \tValue" << std::dec << z3Expr2NumValue(sim) << "\n";
441
+ }
442
+ }
443
+ return SVFUtil::move(exprName.str());
444
+ }
445
+
446
+ bool ConsExeState::applySelect(u32_t res, u32_t cond, u32_t top, u32_t fop)
447
+ {
448
+ if (inVarToVal(top) && inVarToVal(fop) && inVarToVal(cond))
449
+ {
450
+ SingleAbsValue &tExpr = (*this)[top], &fExpr = (*this)[fop], &condExpr = (*this)[cond];
451
+
452
+ return assign((*this)[res], ite(condExpr == 1, tExpr, fExpr));
453
+ }
454
+ else if (inVarToAddrsTable(top) && inVarToAddrsTable(fop) && inVarToVal(cond))
455
+ {
456
+ SingleAbsValue &condExpr = (*this)[cond];
457
+ if (condExpr.is_numeral())
458
+ {
459
+ getVAddrs(res) = condExpr.is_zero() ? getVAddrs(fop) : getVAddrs(top);
460
+ }
461
+ }
462
+ return false;
463
+ }
464
+
465
+ bool ConsExeState::applyPhi(u32_t res, std::vector<u32_t> &ops)
466
+ {
467
+ for (u32_t i = 0; i < ops.size(); i++)
468
+ {
469
+ NodeID curId = ops[i];
470
+ if (inVarToVal(curId))
471
+ {
472
+ const SingleAbsValue &cur = (*this)[curId];
473
+ if (!inVarToVal(res))
474
+ {
475
+ (*this)[res] = cur;
476
+ }
477
+ else
478
+ {
479
+ (*this)[res].join_with(cur);
480
+ }
481
+ }
482
+ else if (inVarToAddrsTable(curId))
483
+ {
484
+ const VAddrs &cur = getVAddrs(curId);
485
+ if (!inVarToAddrsTable(res))
486
+ {
487
+ getVAddrs(res) = cur;
488
+ }
489
+ else
490
+ {
491
+ getVAddrs(res).join_with(cur);
492
+ }
493
+ }
494
+ }
495
+ return true;
496
+ }
497
+
498
+ s64_t ConsExeState::getNumber(u32_t lhs)
499
+ {
500
+ return z3Expr2NumValue((*this)[lhs]);
501
+ }
502
+
503
+ /*!
504
+ * Store value to location
505
+ * @param loc location, e.g., int_val(0x7f..01)
506
+ * @param value
507
+ */
508
+ bool ConsExeState::store(const SingleAbsValue &loc, const SingleAbsValue &value)
509
+ {
510
+ assert(loc.is_numeral() && "location must be numeral");
511
+ s32_t virAddr = z3Expr2NumValue(loc);
512
+ assert(isVirtualMemAddress(virAddr) && "Pointer operand is not a physical address?");
513
+ return store(getInternalID(virAddr), value);
514
+ }
515
+
516
+ /*!
517
+ * Load value at location
518
+ * @param loc location, e.g., int_val(0x7f..01)
519
+ * @return
520
+ */
521
+ SingleAbsValue ConsExeState::load(const SingleAbsValue &loc)
522
+ {
523
+ assert(loc.is_numeral() && "location must be numeral");
524
+ s32_t virAddr = z3Expr2NumValue(loc);
525
+ assert(isVirtualMemAddress(virAddr) && "Pointer operand is not a physical address?");
526
+ u32_t objId = getInternalID(virAddr);
527
+ assert(getInternalID(objId) == objId && "SVFVar idx overflow > 0x7f000000?");
528
+ return load(objId);
529
+ }
530
+
531
+
532
+ /*!
533
+ * Whether two var to value map is equivalent
534
+ * @param pre
535
+ * @param nxt
536
+ * @return
537
+ */
538
+ bool ConsExeState::eqVarToValMap(const VarToValMap &pre, const VarToValMap &nxt)
539
+ {
540
+ if (pre.size() != nxt.size()) return false;
541
+ for (const auto &item: nxt)
542
+ {
543
+ auto it = pre.find(item.first);
544
+ // return false if SVFVar not exists in rhs
545
+ if (it == pre.end())
546
+ return false;
547
+ if (!eq(it->second, item.second))
548
+ return false;
549
+ }
550
+ return true;
551
+ }
552
+
553
+ /*!
554
+ * Whether lhs is less than rhs
555
+ * @param lhs
556
+ * @param rhs
557
+ * @return
558
+ */
559
+ bool ConsExeState::lessThanVarToValMap(const VarToValMap &lhs, const VarToValMap &rhs)
560
+ {
561
+ if (lhs.size() != rhs.size()) return lhs.size() < rhs.size();
562
+ for (const auto &item: lhs)
563
+ {
564
+ auto it = rhs.find(item.first);
565
+ // lhs > rhs if SVFVar not exists in rhs
566
+ if (it == rhs.end())
567
+ return false;
568
+ // judge from expr id
569
+ if (!eq(item.second, it->second))
570
+ return item.second.id() < it->second.id();
571
+ }
572
+ return false;
573
+ }
574
+
575
+ SingleAbsValue ConsExeState::load(u32_t objId)
576
+ {
577
+ auto it = _locToVal.find(objId);
578
+ if (it != _locToVal.end())
579
+ {
580
+ return it->second;
581
+ }
582
+ else
583
+ {
584
+ auto globIt = globalConsES._locToVal.find(objId);
585
+ if (globIt != globalConsES._locToVal.end())
586
+ return globIt->second;
587
+ else
588
+ {
589
+ SVFUtil::writeWrnMsg("Null dereference");
590
+ return SingleAbsValue::topConstant();
591
+ }
592
+ }
593
+ }
594
+
595
+ bool ConsExeState::assign(SingleAbsValue &lhs, const SingleAbsValue &rhs)
596
+ {
597
+ if (!eq(lhs, rhs))
598
+ {
599
+ lhs = rhs;
600
+ return true;
601
+ }
602
+ else
603
+ {
604
+ return false;
605
+ }
606
+ }
@@ -37,18 +37,21 @@ bool ExeState::operator==(const ExeState &rhs) const
37
37
  return eqVarToVAddrs(_varToVAddrs, rhs._varToVAddrs) && eqVarToVAddrs(_locToVAddrs, rhs._locToVAddrs);
38
38
  }
39
39
 
40
- void ExeState::joinWith(const ExeState &other)
40
+ bool ExeState::joinWith(const ExeState &other)
41
41
  {
42
+ bool changed = false;
42
43
  for (auto it = other._varToVAddrs.begin(); it != other._varToVAddrs.end(); ++it)
43
44
  {
44
45
  auto key = it->first;
45
46
  auto oit = _varToVAddrs.find(key);
46
47
  if (oit != _varToVAddrs.end())
47
48
  {
48
- oit->second.join_with(it->second);
49
+ if(oit->second.join_with(it->second))
50
+ changed = true;
49
51
  }
50
52
  else
51
53
  {
54
+ changed = true;
52
55
  _varToVAddrs.emplace(key, it->second);
53
56
  }
54
57
  }
@@ -58,25 +61,29 @@ void ExeState::joinWith(const ExeState &other)
58
61
  auto oit = _locToVAddrs.find(key);
59
62
  if (oit != _locToVAddrs.end())
60
63
  {
61
- oit->second.join_with(it->second);
64
+ if(oit->second.join_with(it->second))
65
+ changed = true;
62
66
  }
63
67
  else
64
68
  {
69
+ changed = true;
65
70
  _locToVAddrs.emplace(key, it->second);
66
71
  }
67
72
  }
73
+ return changed;
68
74
  }
69
75
 
70
-
71
- void ExeState::meetWith(const ExeState &other)
76
+ bool ExeState::meetWith(const ExeState &other)
72
77
  {
78
+ bool changed = false;
73
79
  for (auto it = other._varToVAddrs.begin(); it != other._varToVAddrs.end(); ++it)
74
80
  {
75
81
  auto key = it->first;
76
82
  auto oit = _varToVAddrs.find(key);
77
83
  if (oit != _varToVAddrs.end())
78
84
  {
79
- oit->second.meet_with(it->second);
85
+ if(oit->second.meet_with(it->second))
86
+ changed = true;
80
87
  }
81
88
  }
82
89
  for (auto it = other._locToVAddrs.begin(); it != other._locToVAddrs.end(); ++it)
@@ -85,9 +92,11 @@ void ExeState::meetWith(const ExeState &other)
85
92
  auto oit = _locToVAddrs.find(key);
86
93
  if (oit != _locToVAddrs.end())
87
94
  {
88
- oit->second.meet_with(it->second);
95
+ if(oit->second.meet_with(it->second))
96
+ changed = true;
89
97
  }
90
98
  }
99
+ return changed;
91
100
  }
92
101
 
93
102
  u32_t ExeState::hash() const