void StatementList::makeIsect(StatementList &a, LocationSet &b) { if (this == &a) { // *this = *this isect b for (auto it = a.begin(); it != a.end();) { assert((*it)->isAssignment()); Assignment *as = static_cast<Assignment *>(*it); if (!b.contains(as->getLeft())) { it = m_list.erase(it); } else { it++; } } } else { // normal assignment clear(); for (Statement *stmt : a) { assert(stmt->isAssignment()); Assignment *as = static_cast<Assignment *>(stmt); if (b.contains(as->getLeft())) { append(as); } } } }
void LocationSetTest::testEmpty() { LocationSet set; QVERIFY(set.empty()); set.insert(Location::regOf(REG_PENT_EDI)); QVERIFY(!set.empty()); }
void LocationSetTest::testContains() { LocationSet set; QVERIFY(!set.contains(nullptr)); set.insert(Location::regOf(REG_PENT_ESI)); QVERIFY(set.contains(Location::regOf(REG_PENT_ESI))); QVERIFY(!set.contains(Location::regOf(REG_PENT_EDI))); }
void LocationSetTest::testInsert() { LocationSet set; set.insert(Location::regOf(REG_PENT_ESI)); QCOMPARE(set, LocationSet({ Location::regOf(REG_PENT_ESI) })); set.insert(Location::regOf(REG_PENT_ESI)); QCOMPARE(set, LocationSet({ Location::regOf(REG_PENT_ESI) })); set.insert(Location::regOf(REG_PENT_EDI)); QCOMPARE(set, LocationSet({ Location::regOf(REG_PENT_ESI), Location::regOf(REG_PENT_EDI) })); }
// Remove locations defined by any of the given set of statements // Used for killing in liveness sets void LocationSet::removeIfDefines(StatementSet& given) { StatementSet::iterator it; for (it = given.begin(); it != given.end(); ++it) { Statement* s = (Statement*)*it; LocationSet defs; s->getDefinitions(defs); LocationSet::iterator dd; for (dd = defs.begin(); dd != defs.end(); ++dd) lset.erase(*dd); } }
// Substitute s into all members of the set void LocationSet::substitute(Assign& a) { Exp* lhs = a.getLeft(); if (lhs == NULL) return; Exp* rhs = a.getRight(); if (rhs == NULL) return; // ? Will this ever happen? std::set<Exp*, lessExpStar>::iterator it; // Note: it's important not to change the pointer in the set of pointers to expressions, without removing and // inserting again. Otherwise, the set becomes out of order, and operations such as set comparison fail! // To avoid any funny behaviour when iterating the loop, we use the following two sets LocationSet removeSet; // These will be removed after the loop LocationSet removeAndDelete; // These will be removed then deleted LocationSet insertSet; // These will be inserted after the loop bool change; for (it = lset.begin(); it != lset.end(); it++) { Exp* loc = *it; Exp* replace; if (loc->search(lhs, replace)) { if (rhs->isTerminal()) { // This is no longer a location of interest (e.g. %pc) removeSet.insert(loc); continue; } loc = loc->clone()->searchReplaceAll(lhs, rhs, change); if (change) { loc = loc->simplifyArith(); loc = loc->simplify(); // If the result is no longer a register or memory (e.g. // r[28]-4), then delete this expression and insert any // components it uses (in the example, just r[28]) if (!loc->isRegOf() && !loc->isMemOf()) { // Note: can't delete the expression yet, because the // act of insertion into the remove set requires silent // calls to the compare function removeAndDelete.insert(*it); loc->addUsedLocs(insertSet); continue; } // Else we just want to replace it // Regardless of whether the top level expression pointer has // changed, remove and insert it from the set of pointers removeSet.insert(*it); // Note: remove the unmodified ptr insertSet.insert(loc); } } } makeDiff(removeSet); // Remove the items to be removed makeDiff(removeAndDelete); // These are to be removed as well makeUnion(insertSet); // Insert the items to be added // Now delete the expressions that are no longer needed std::set<Exp*, lessExpStar>::iterator dd; for (dd = removeAndDelete.lset.begin(); dd != removeAndDelete.lset.end(); dd++) delete *dd; // Plug that memory leak }
/*! * Whether a location set is a pointer type or not */ bool ObjTypeInfo::isNonPtrFieldObj(const LocationSet& ls) { if (isHeap() || isStaticObj()) return false; llvm::Type* ety = getLLVMType(); while (const ArrayType *AT= dyn_cast<ArrayType>(ety)) { ety = AT->getElementType(); } if (isa<StructType>(ety) || isa<ArrayType>(ety)) { bool hasIntersection = false; const vector<FieldInfo> &infovec = SymbolTableInfo::Symbolnfo()->getFlattenFieldInfoVec(ety); vector<FieldInfo>::const_iterator it = infovec.begin(); vector<FieldInfo>::const_iterator eit = infovec.end(); for (; it != eit; ++it) { const FieldInfo& fieldLS = *it; if (ls.intersects(LocationSet(fieldLS))) { hasIntersection = true; if (fieldLS.getFlattenElemTy()->isPointerTy()) return false; } } assert(hasIntersection && "cannot find field of specified offset"); return true; } else { if (isStaticObj() || isHeap()) { // TODO: Objects which cannot find proper field for a certain offset including // arguments in main(), static objects allocated before main and heap // objects. Right now they're considered to have infinite fields and we // treat each field as pointers conservatively. // Try to model static and heap objects more accurately in the future. return false; } else { // TODO: Using new memory model (locMM) may create objects with spurious offset // as we simply return new offset by mod operation without checking its // correctness in LocSymTableInfo::getModulusOffset(). So the following // assertion may fail. Try to refine the new memory model. assert(ls.getOffset() == 0 && "cannot get a field from a non-struct type"); return (hasPtrObj() == false); } } }
// Special intersection method: this := a intersect b void StatementList::makeIsect(StatementList& a, LocationSet& b) { slist.clear(); for (iterator it = a.slist.begin(); it != a.slist.end(); ++it) { Assignment* as = (Assignment*)*it; if (b.exists(as->getLeft())) slist.push_back(as); } }
bool LocationSet::operator ==(const LocationSet &o) const { // We want to compare the locations, not the pointers if (size() != o.size()) return false; for (auto it1 = lset.cbegin(), it2 = o.lset.cbegin(); it1 != lset.cend(); ++it1, ++it2) { if (!(**it1 == **it2)) return false; } return true; }
// Special intersection method: this := a intersect b void StatementList::makeIsect(const StatementList &a, const LocationSet &b) { slist.clear(); for (const auto &s : a.slist) { auto as = (Assignment *)s; if (b.exists(as->getLeft())) slist.push_back(as); } }
bool LocationSet::operator==(const LocationSet& o) const { // We want to compare the locations, not the pointers if (size() != o.size()) return false; std::set<Exp*, lessExpStar>::const_iterator it1, it2; for (it1 = lset.begin(), it2 = o.lset.begin(); it1 != lset.end(); it1++, it2++) { if (!(**it1 == **it2)) return false; } return true; }
void LocationSetTest::testRemove() { LocationSet set; set.remove(nullptr); QVERIFY(set.empty()); set.insert(Location::regOf(REG_PENT_ESI)); set.remove(Location::regOf(REG_PENT_ESI)); QVERIFY(set.empty()); set.insert(Location::regOf(REG_PENT_ESI)); set.insert(Location::regOf(REG_PENT_EDI)); set.remove(Location::regOf(REG_PENT_EDI)); QCOMPARE(set, LocationSet({ Location::regOf(REG_PENT_ESI) })); // removing element that does not exist set.remove(Location::regOf(REG_PENT_EDI)); QCOMPARE(set, LocationSet({ Location::regOf(REG_PENT_ESI) })); }
void LocationSetTest::testContainsImplicit() { LocationSet set; QVERIFY(!set.containsImplicit(nullptr)); set.insert(Location::regOf(REG_PENT_ESI)); QVERIFY(!set.containsImplicit(Location::regOf(REG_PENT_ESI))); QVERIFY(!set.containsImplicit(Location::regOf(REG_PENT_EDI))); set.insert(RefExp::get(Location::regOf(REG_PENT_EDI), nullptr)); QVERIFY(set.containsImplicit(Location::regOf(REG_PENT_EDI))); QVERIFY(!set.containsImplicit(Location::regOf(REG_PENT_ESI))); }
void LocationSetTest::testSize() { LocationSet set; QVERIFY(set.size() == 0); set.insert(Location::regOf(REG_PENT_ESI)); QVERIFY(set.size() == 1); set.insert(Location::regOf(REG_PENT_EDI)); QVERIFY(set.size() == 2); }
void LocationSetTest::testClear() { LocationSet set; set.clear(); QVERIFY(set.empty()); set.insert(Location::regOf(REG_PENT_ESI)); set.clear(); QVERIFY(set.empty()); }
/*! * Get modulus offset given the type information */ LocationSet SymbolTableInfo::getModulusOffset(ObjTypeInfo* tyInfo, const LocationSet& ls) { /// if the offset is negative, it's possible that we're looking for an obj node out of range /// of current struct. Make the offset positive so we can still get a node within current /// struct to represent this obj. Size_t offset = ls.getOffset(); if(offset < 0) { wrnMsg("try to create a gep node with negative offset."); offset = abs(offset); } u32_t maxOffset = tyInfo->getMaxFieldOffsetLimit(); if (maxOffset != 0) offset = offset % maxOffset; else offset = 0; return LocationSet(offset); }
void LocationSetTest::testFindNS() { LocationSet set; QVERIFY(set.findNS(nullptr) == nullptr); set.insert(Location::regOf(REG_PENT_ESI)); SharedExp e = set.findNS(Location::regOf(REG_PENT_ESI)); QVERIFY(e == nullptr); set.insert(RefExp::get(Location::regOf(REG_PENT_EDI), nullptr)); e = set.findNS(Location::regOf(REG_PENT_EDI)); QVERIFY(e != nullptr); QCOMPARE(e->toString(), QString("r31{-}")); }
void LocationSetTest::testAddSubscript() { LocationSet set; set.addSubscript(nullptr); QCOMPARE(set, LocationSet()); set.insert(Location::regOf(REG_PENT_ECX)); set.addSubscript(nullptr); QCOMPARE(set, LocationSet({ RefExp::get(Location::regOf(REG_PENT_ECX), nullptr) })); set.insert(Location::regOf(REG_PENT_ECX)); Assign as(Location::regOf(REG_PENT_ECX), Location::regOf(REG_PENT_EDX)); as.setNumber(42); set.addSubscript(&as); QCOMPARE(set, LocationSet({ RefExp::get(Location::regOf(REG_PENT_ECX), nullptr), RefExp::get(Location::regOf(REG_PENT_ECX), &as) })); }
void BoolAssign::getDefinitions(LocationSet &defs, bool) const { defs.insert(getLeft()); }
// Helper function for UserProc::propagateStatements() // Works on basic block n; call from UserProc with n=0 (entry BB) // If an SSA location is in usedByDomPhi it means it is used in a phi that dominates its assignment // However, it could turn out that the phi is dead, in which case we don't want to keep the associated entries in // usedByDomPhi. So we maintain the map defdByPhi which maps locations defined at a phi to the phi statements. Every // time we see a use of a location in defdByPhi, we remove that map entry. At the end of the procedure we therefore have // only dead phi statements in the map, so we can delete the associated entries in defdByPhi and also remove the dead // phi statements. // We add to the set usedByDomPhi0 whenever we see a location referenced by a phi parameter. When we see a definition // for such a location, we remove it from the usedByDomPhi0 set (to save memory) and add it to the usedByDomPhi set. // For locations defined before they are used in a phi parameter, there will be no entry in usedByDomPhi, so we ignore // it. Remember that each location is defined only once, so that's the time to decide if it is dominated by a phi use or // not. void DataFlow::findLiveAtDomPhi(int n, LocationSet& usedByDomPhi, LocationSet& usedByDomPhi0, std::map<Exp*, PhiAssign*, lessExpStar>& defdByPhi) { // For each statement this BB BasicBlock::rtlit rit; StatementList::iterator sit; PBB bb = BBs[n]; Statement* S; for (S = bb->getFirstStmt(rit, sit); S; S = bb->getNextStmt(rit, sit)) { if (S->isPhi()) { // For each phi parameter, insert an entry into usedByDomPhi0 PhiAssign* pa = (PhiAssign*)S; PhiAssign::iterator it; for (it = pa->begin(); it != pa->end(); ++it) { if (it->e) { RefExp* re = new RefExp(it->e, it->def); usedByDomPhi0.insert(re); } } // Insert an entry into the defdByPhi map RefExp* wrappedLhs = new RefExp(pa->getLeft(), pa); defdByPhi[wrappedLhs] = pa; // Fall through to the below, because phi uses are also legitimate uses } LocationSet ls; S->addUsedLocs(ls); // Consider uses of this statement LocationSet::iterator it; for (it = ls.begin(); it != ls.end(); ++it) { // Remove this entry from the map, since it is not unused defdByPhi.erase(*it); } // Now process any definitions ls.clear(); S->getDefinitions(ls); for (it = ls.begin(); it != ls.end(); ++it) { RefExp* wrappedDef = new RefExp(*it, S); // If this definition is in the usedByDomPhi0 set, then it is in fact dominated by a phi use, so move it to // the final usedByDomPhi set if (usedByDomPhi0.find(wrappedDef) != usedByDomPhi0.end()) { usedByDomPhi0.remove(wrappedDef); usedByDomPhi.insert(wrappedDef); } } } // Visit each child in the dominator graph // Note: this is a linear search! // Note also that usedByDomPhi0 may have some irrelevant entries, but this will do no harm, and attempting to erase // the irrelevant ones would probably cost more than leaving them alone int sz = idom.size(); for (int c = 0; c < sz; ++c) { if (idom[c] != n) continue; // Recurse to the child findLiveAtDomPhi(c, usedByDomPhi, usedByDomPhi0, defdByPhi); } }
bool DataFlow::renameBlockVars(UserProc* proc, int n, bool clearStacks /* = false */ ) { if (++progress > 200) { std::cerr << 'r' << std::flush; progress = 0; } bool changed = false; // Need to clear the Stacks of old, renamed locations like m[esp-4] (these will be deleted, and will cause compare // failures in the Stacks, so it can't be correctly ordered and hence balanced etc, and will lead to segfaults) if (clearStacks) Stacks.clear(); // For each statement S in block n BasicBlock::rtlit rit; StatementList::iterator sit; PBB bb = BBs[n]; Statement* S; for (S = bb->getFirstStmt(rit, sit); S; S = bb->getNextStmt(rit, sit)) { // if S is not a phi function (per Appel) /* if (!S->isPhi()) */ { // For each use of some variable x in S (not just assignments) LocationSet locs; if (S->isPhi()) { PhiAssign* pa = (PhiAssign*)S; Exp* phiLeft = pa->getLeft(); if (phiLeft->isMemOf() || phiLeft->isRegOf()) phiLeft->getSubExp1()->addUsedLocs(locs); // A phi statement may use a location defined in a childless call, in which case its use collector // needs updating PhiAssign::iterator pp; for (pp = pa->begin(); pp != pa->end(); ++pp) { Statement* def = pp->def; if (def && def->isCall()) ((CallStatement*)def)->useBeforeDefine(phiLeft->clone()); } } else { // Not a phi assignment S->addUsedLocs(locs); } LocationSet::iterator xx; for (xx = locs.begin(); xx != locs.end(); xx++) { Exp* x = *xx; // Don't rename memOfs that are not renamable according to the current policy if (!canRename(x, proc)) continue; Statement* def = NULL; if (x->isSubscript()) { // Already subscripted? // No renaming required, but redo the usage analysis, in case this is a new return, and also because // we may have just removed all call livenesses // Update use information in calls, and in the proc (for parameters) Exp* base = ((RefExp*)x)->getSubExp1(); def = ((RefExp*)x)->getDef(); if (def && def->isCall()) { // Calls have UseCollectors for locations that are used before definition at the call ((CallStatement*)def)->useBeforeDefine(base->clone()); continue; } // Update use collector in the proc (for parameters) if (def == NULL) proc->useBeforeDefine(base->clone()); continue; // Don't re-rename the renamed variable } // Else x is not subscripted yet if (STACKS_EMPTY(x)) { if (!Stacks[defineAll].empty()) def = Stacks[defineAll].top(); else { // If the both stacks are empty, use a NULL definition. This will be changed into a pointer // to an implicit definition at the start of type analysis, but not until all the m[...] // have stopped changing their expressions (complicates implicit assignments considerably). def = NULL; // Update the collector at the start of the UserProc proc->useBeforeDefine(x->clone()); } } else def = Stacks[x].top(); if (def && def->isCall()) // Calls have UseCollectors for locations that are used before definition at the call ((CallStatement*)def)->useBeforeDefine(x->clone()); // Replace the use of x with x{def} in S changed = true; if (S->isPhi()) { Exp* phiLeft = ((PhiAssign*)S)->getLeft(); phiLeft->setSubExp1(phiLeft->getSubExp1()->expSubscriptVar(x, def /*, this*/)); } else { S->subscriptVar(x, def /*, this */); } } } // MVE: Check for Call and Return Statements; these have DefCollector objects that need to be updated // Do before the below, so CallStatements have not yet processed their defines if (S->isCall() || S->isReturn()) { DefCollector* col; if (S->isCall()) col = ((CallStatement*)S)->getDefCollector(); else col = ((ReturnStatement*)S)->getCollector(); col->updateDefs(Stacks, proc); } // For each definition of some variable a in S LocationSet defs; S->getDefinitions(defs); LocationSet::iterator dd; for (dd = defs.begin(); dd != defs.end(); dd++) { Exp* a = *dd; // Don't consider a if it cannot be renamed bool suitable = canRename(a, proc); if (suitable) { // Push i onto Stacks[a] // Note: we clone a because otherwise it could be an expression that gets deleted through various // modifications. This is necessary because we do several passes of this algorithm to sort out the // memory expressions Stacks[a->clone()].push(S); // Replace definition of a with definition of a_i in S (we don't do this) } // FIXME: MVE: do we need this awful hack? if (a->getOper() == opLocal) { Exp *a1 = S->getProc()->expFromSymbol(((Const*)a->getSubExp1())->getStr()); assert(a1); a = a1; // Stacks already has a definition for a (as just the bare local) if (suitable) { Stacks[a->clone()].push(S); } } } // Special processing for define-alls (presently, only childless calls). // But note that only everythings at the current memory level are defined! if (S->isCall() && ((CallStatement*)S)->isChildless() && !Boomerang::get()->assumeABI) { // S is a childless call (and we're not assuming ABI compliance) Stacks[defineAll]; // Ensure that there is an entry for defineAll std::map<Exp*, std::stack<Statement*>, lessExpStar>::iterator dd; for (dd = Stacks.begin(); dd != Stacks.end(); ++dd) { // if (dd->first->isMemDepth(memDepth)) dd->second.push(S); // Add a definition for all vars } } } // For each successor Y of block n std::vector<PBB>& outEdges = bb->getOutEdges(); unsigned numSucc = outEdges.size(); for (unsigned succ = 0; succ < numSucc; succ++) { PBB Ybb = outEdges[succ]; // Suppose n is the jth predecessor of Y int j = Ybb->whichPred(bb); // For each phi-function in Y Statement* S; for (S = Ybb->getFirstStmt(rit, sit); S; S = Ybb->getNextStmt(rit, sit)) { PhiAssign* pa = dynamic_cast<PhiAssign*>(S); // if S is not a phi function, then quit the loop (no more phi's) // Wrong: do not quit the loop: there's an optimisation that turns a PhiAssign into an ordinary Assign. // So continue, not break. if (!pa) continue; // Suppose the jth operand of the phi is a // For now, just get the LHS Exp* a = pa->getLeft(); // Only consider variables that can be renamed if (!canRename(a, proc)) continue; Statement* def; if (STACKS_EMPTY(a)) def = NULL; // No reaching definition else def = Stacks[a].top(); // "Replace jth operand with a_i" pa->putAt(j, def, a); } } // For each child X of n // Note: linear search! unsigned numBB = proc->getCFG()->getNumBBs(); for (unsigned X=0; X < numBB; X++) { if (idom[X] == n) renameBlockVars(proc, X); } // For each statement S in block n // NOTE: Because of the need to pop childless calls from the Stacks, it is important in my algorithm to process the // statments in the BB *backwards*. (It is not important in Appel's algorithm, since he always pushes a definition // for every variable defined on the Stacks). BasicBlock::rtlrit rrit; StatementList::reverse_iterator srit; for (S = bb->getLastStmt(rrit, srit); S; S = bb->getPrevStmt(rrit, srit)) { // For each definition of some variable a in S LocationSet defs; S->getDefinitions(defs); LocationSet::iterator dd; for (dd = defs.begin(); dd != defs.end(); dd++) { if (canRename(*dd, proc)) { // if ((*dd)->getMemDepth() == memDepth) std::map<Exp*, std::stack<Statement*>, lessExpStar>::iterator ss = Stacks.find(*dd); if (ss == Stacks.end()) { std::cerr << "Tried to pop " << *dd << " from Stacks; does not exist\n"; assert(0); } ss->second.pop(); } } // Pop all defs due to childless calls if (S->isCall() && ((CallStatement*)S)->isChildless()) { std::map<Exp*, std::stack<Statement*>, lessExpStar>::iterator sss; for (sss = Stacks.begin(); sss != Stacks.end(); ++sss) { if (!sss->second.empty() && sss->second.top() == S) { sss->second.pop(); } } } } return changed; }
bool DataFlow::placePhiFunctions(UserProc* proc) { // First free some memory no longer needed dfnum.resize(0); semi.resize(0); ancestor.resize(0); samedom.resize(0); vertex.resize(0); parent.resize(0); best.resize(0); bucket.resize(0); defsites.clear(); // Clear defsites map, defallsites.clear(); A_orig.clear(); // and A_orig, defStmts.clear(); // and the map from variable to defining Stmt bool change = false; // Set the sizes of needed vectors unsigned numBB = indices.size(); Cfg* cfg = proc->getCFG(); assert(numBB == cfg->getNumBBs()); A_orig.resize(numBB); // We need to create A_orig[n] for all n, the array of sets of locations defined at BB n // Recreate each call because propagation and other changes make old data invalid unsigned n; for (n=0; n < numBB; n++) { BasicBlock::rtlit rit; StatementList::iterator sit; PBB bb = BBs[n]; for (Statement* s = bb->getFirstStmt(rit, sit); s; s = bb->getNextStmt(rit, sit)) { LocationSet ls; LocationSet::iterator it; s->getDefinitions(ls); if (s->isCall() && ((CallStatement*)s)->isChildless()) // If this is a childless call defallsites.insert(n); // then this block defines every variable for (it = ls.begin(); it != ls.end(); it++) { if (canRename(*it, proc)) { A_orig[n].insert((*it)->clone()); defStmts[*it] = s; } } } } // For each node n for (n=0; n < numBB; n++) { // For each variable a in A_orig[n] std::set<Exp*, lessExpStar>& s = A_orig[n]; std::set<Exp*, lessExpStar>::iterator aa; for (aa = s.begin(); aa != s.end(); aa++) { Exp* a = *aa; defsites[a].insert(n); } } // For each variable a (in defsites, i.e. defined anywhere) std::map<Exp*, std::set<int>, lessExpStar>::iterator mm; for (mm = defsites.begin(); mm != defsites.end(); mm++) { Exp* a = (*mm).first; // *mm is pair<Exp*, set<int>> // Special processing for define-alls // for each n in defallsites std::set<int>::iterator da; for (da = defallsites.begin(); da != defallsites.end(); ++da) defsites[a].insert(*da); // W <- defsites[a]; std::set<int> W = defsites[a]; // set copy // While W not empty while (W.size()) { // Remove some node n from W int n = *W.begin(); // Copy first element W.erase(W.begin()); // Remove first element // for each y in DF[n] std::set<int>::iterator yy; std::set<int>& DFn = DF[n]; for (yy = DFn.begin(); yy != DFn.end(); yy++) { int y = *yy; // if y not element of A_phi[a] std::set<int>& s = A_phi[a]; if (s.find(y) == s.end()) { // Insert trivial phi function for a at top of block y: a := phi() change = true; Statement* as = new PhiAssign(a->clone()); PBB Ybb = BBs[y]; Ybb->prependStmt(as, proc); // A_phi[a] <- A_phi[a] U {y} s.insert(y); // if a !elementof A_orig[y] if (A_orig[y].find(a) == A_orig[y].end()) { // W <- W U {y} W.insert(y); } } } } } return change; } // end placePhiFunctions
void LocationSetTest::testFindDifferentRef() { LocationSet set; SharedExp result; QVERIFY(!set.findDifferentRef(nullptr, result)); set.insert(Location::regOf(REG_PENT_EAX)); QVERIFY(!set.findDifferentRef(RefExp::get(Location::regOf(REG_PENT_EAX), nullptr), result)); set.insert(RefExp::get(Location::regOf(REG_PENT_EAX), nullptr)); QVERIFY(!set.findDifferentRef(RefExp::get(Location::regOf(REG_PENT_EAX), nullptr), result)); Assign as1(Location::regOf(REG_PENT_ECX), Location::regOf(REG_PENT_EDX)); Assign as2(Location::regOf(REG_PENT_ECX), Location::regOf(REG_PENT_EDX)); as1.setNumber(10); as2.setNumber(20); set.insert(RefExp::get(Location::regOf(REG_PENT_ECX), &as1)); // no other ref QVERIFY(!set.findDifferentRef(RefExp::get(Location::regOf(REG_PENT_ECX), &as1), result)); set.insert(RefExp::get(Location::regOf(REG_PENT_ECX), &as2)); // return a different ref QVERIFY(set.findDifferentRef(RefExp::get(Location::regOf(REG_PENT_ECX), &as1), result)); QCOMPARE(result->toString(), QString("r25{20}")); // should work even when the ref is not in the set set.remove(RefExp::get(Location::regOf(REG_PENT_ECX), &as1)); QVERIFY(set.findDifferentRef(RefExp::get(Location::regOf(REG_PENT_ECX), &as1), result)); QCOMPARE(result->toString(), QString("r25{20}")); }