ExpressionPtr AliasManager::getCanonical(ExpressionPtr e) { unsigned val = (e->getCanonHash() % MaxBuckets) + 1; BucketMapEntry &em = m_bucketMap[val]; ExpressionPtr c = em.find(e); if (!c) { add(em, e); c = e; e->setCanonPtr(ExpressionPtr()); } else { e->setCanonID(c->getCanonID()); e->setCanonPtr(c); } return c; }
void LiveDict::visit(ExpressionPtr e) { if (m_coalesce) { if (e->is(Expression::KindOfSimpleVariable)) { int id = e->getCanonID(); int rid = m_remap[id]; if (rid && rid != id) { SimpleVariablePtr sv(static_pointer_cast<SimpleVariable>(e)); SimpleVariablePtr rv(static_pointer_cast<SimpleVariable>(get(rid))); sv->coalesce(rv); } } return; } if (m_getVars != e->is(Expression::KindOfSimpleVariable)) return; if (m_am.insertForDict(e) && m_getVars) { record(e); } }
/* The classical use/def isnt quite enough here. There are two unusual issues: - the ref/non-ref issue. An assignment to a var which is referenced doesnt end its lifetime. In fact it could just be a "use" of the var. But it also counts as a def. - the destructor issue. variables which might need to be destroyed later are technically alive, but dont interfere with any other variables in the same state. They /do/ interfere with any truly "live" variables, however. These are "dying". So we end up defining, use, kill, def and dying. use : a read of the variable, an ordinary assignment if it could be referenced kill : an unset, a ref assignment, or, for non referenced vars, any assignment def : ref or normal assignment dying : a variable whose destructor is (partially/locally) anticipated. */ void LiveDict::updateAccess(ExpressionPtr e) { int cls = e->getExprClass(); if (cls & Expression::Store) { /* Handled when we see the lhs */ return; } int eid = e->getCanonID(); int context = e->getContext(); bool unset = false; bool store = false; if (context & Expression::LValue && context & Expression::UnsetContext) { unset = true; } else if (context & Expression::AssignmentLHS) { store = true; } if (e->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr sv(static_pointer_cast<SimpleVariable>(e)); bool use = false, kill = false, def = false; Symbol *sym = sv->getSymbol(); bool isReferenced = e->isReferencedValid() ? e->isReferenced() : sym && sym->isReferenced(); bool isNeeded = e->isNeededValid() ? e->isNeeded() : sym && sym->isNeeded(); if (unset) { kill = true; } else if (store) { if (context & Expression::RefAssignmentLHS || (!m_am.hasWildRefs() && !isReferenced)) { kill = true; } def = true; } else if ((context & Expression::Declaration) == Expression::Declaration) { // a global declaration def = kill = true; } else if (context & (Expression::LValue| Expression::RefValue| Expression::DeepReference| Expression::UnsetContext| Expression::OprLValue)) { use = def = true; } else { use = true; } if (kill && (!sym || isNeeded || isReferenced) && !BitOps::get_bit(eid, m_altered) && !BitOps::get_bit(eid, m_available)) { BitOps::set_bit(eid, m_dying, true); } if (use && !BitOps::get_bit(eid, m_altered) && !BitOps::get_bit(eid, m_available)) { BitOps::set_bit(eid, m_anticipated, true); e->setAnticipated(); } if (kill) { BitOps::set_bit(eid, m_altered, true); BitOps::set_bit(eid, m_available, def); } else if (def) { BitOps::set_bit(eid, m_available, true); } if (!m_am.couldBeAliased(sv)) { return; } } else if (!e->is(Expression::KindOfDynamicVariable) && (unset || (context & Expression::RefAssignmentLHS))) { // An unset, or a reference assignment to anything other // than a simple or dynamic variable can never affect a simple // variable (outside of pseudoMain). return; } if (store || cls & (Expression::Load|Expression::Call)) { bool mod = store || (cls & Expression::Load && e->getContext() & (Expression::LValue| Expression::RefValue| Expression::UnsetContext| Expression::DeepReference| Expression::OprLValue)); ExpressionPtr cur = m_refs, prev; bool isLoad; int depth = 0, effects = 0; while (cur) { ExpressionPtr next = cur->getCanonLVal(); int cid = cur->getCanonID(); if (cid != eid && m_am.checkAnyInterf(e, cur, isLoad, depth, effects) != AliasManager::DisjointAccess) { if (mod) { BitOps::set_bit(cid, m_available, true); } if (!BitOps::get_bit(cid, m_altered) && !BitOps::get_bit(cid, m_available)) { BitOps::set_bit(cid, m_anticipated, true); } if (!prev) { m_refs = next; } else { prev->setCanonPtr(next); } } else { prev = cur; } cur = next; } } }
void ExprDict::getTypes(ExpressionPtr e, TypePtrIdxPairVec &types) { return getTypes(e->getCanonID(), types); }
TypePtr ExprDict::propagateType(ExpressionPtr e) { return m_avlTypes[e->getCanonID()]; }
ExpressionPtr ExprDict::propagate(ExpressionPtr e) { return m_avlExpr[e->getCanonID()]; }
void ExprDict::updateAccess(ExpressionPtr e) { int cls = e->getExprClass(); int eid = e->getCanonID(); e->clearAnticipated(); e->clearAvailable(); // bail on non-canonical expressions if (!isCanonicalStructure(eid)) { // but record we saw a type assertion belonging to this block m_avlTypeAsserts.push_back(eid); return; } if (m_anticipated && (cls & Expression::Update ? !BitOps::get_bit(eid, m_altered) : !e->getLocalEffects())) { /* Anticipated can be computed bottom up as we go. But note that we only know altered for Load/Store expressions. */ int i = e->getKidCount(); while (true) { if (!i--) { e->setAnticipated(); if (!e->hasContext(Expression::AssignmentLHS)) { setStructureOps(eid, m_anticipated, true); } break; } if (ExpressionPtr k = e->getNthExpr(i)) { if (!isCanonicalStructure(k->getCanonID())) continue; if (!k->isAnticipated()) { break; } } } } if (m_available) { /* Available has to be computed optimistically, because we dont yet know what is going to be altered between here and the end of the block So keep a list of the potentially-available accesses (avlAccess), and for each id, the last potentially-available expression (avlExpr). For each modifying expression that we process, we remove expressions from avlAccess, and at the end, we build up the available expressions bottom up. */ if ((cls & (Expression::Store|Expression::Call)) || (cls & Expression::Load && e->getContext() & (Expression::LValue| Expression::RefValue| Expression::UnsetContext| Expression::DeepReference))) { bool isLoad; int depth = 0, effects = 0; for (int i = 0, n = m_avlAccess.size(); i < n; ) { ExpressionRawPtr a = m_avlAccess[i]; if (m_am.checkAnyInterf(e, a, isLoad, depth, effects) != AliasManager::DisjointAccess) { int aid = a->getCanonID(); assert(isCanonicalStructure(aid)); if (eid != aid || cls == Expression::Load) { BitOps::set_bit(aid, m_altered, true); } if (!(cls & Expression::Store) || a != e->getStoreVariable()) { a->clearAvailable(); m_avlAccess[i] = m_avlAccess[--n]; m_avlAccess.resize(n); continue; } } i++; } } if (cls & Expression::Update || !e->getContainedEffects()) { int i = e->getKidCount(); while (true) { if (!i--) { e->setAvailable(); if (cls & Expression::Update) { m_avlAccess.push_back(e); } m_avlExpr[eid] = e; break; } if (ExpressionPtr k = e->getNthExpr(i)) { if (!isCanonicalStructure(k->getCanonID())) continue; if (!k->isAvailable()) { break; } } } } } if ((cls & (Expression::Store|Expression::Call)) || (cls & Expression::Load && e->getContext() & (Expression::LValue| Expression::RefValue| Expression::UnsetContext| Expression::DeepReference))) { ExpressionPtr cur = m_active, prev; bool isLoad; int depth = 0, effects = 0; while (cur) { ExpressionPtr next = cur->getCanonLVal(); int cid = cur->getCanonID(); assert(isCanonicalStructure(cid)); if ((cid != eid || cls == Expression::Load) && (BitOps::get_bit(cid, m_altered) || m_am.checkAnyInterf(e, cur, isLoad, depth, effects) != AliasManager::DisjointAccess)) { BitOps::set_bit(cid, m_altered, true); if (!prev) { m_active = next; } else { prev->setCanonPtr(next); } } else { prev = cur; } cur = next; } } }
void ExprDict::visit(ExpressionPtr e) { assert(m_canonIdMap.size() == m_canonTypeMap.size()); if (m_am.insertForDict(e)) { // we've never seen e's structure before, so record it record(e); if (e->getCanonID() >= m_canonTypeMap.size()) { m_canonTypeMap.resize(e->getCanonID() + 1); m_canonIdMap.resize(e->getCanonID() + 1); } m_canonTypeMap[e->getCanonID()] = TypePtrIdxPair(extractTypeAssertion(e), e->getCanonID()); m_canonIdMap[e->getCanonID()] = e->getCanonID(); } else if (e->isTypeAssertion()) { TypePtrIdxPairVec types; assert(isCanonicalStructure(e->getCanonID())); getTypes(e->getCanonID(), types); TypePtrIdxPair entry; if (containsAssertion(e->getAssertedType(), types, entry)) { e->setCanonID(entry.second); } else { // new type assertion seen, record it int oldId = e->getCanonID(); assert(isCanonicalStructure(oldId)); record(e); // insert it into the list if (e->getCanonID() >= m_canonTypeMap.size()) { m_canonTypeMap.resize(e->getCanonID() + 1); m_canonIdMap.resize(e->getCanonID() + 1); } m_canonIdMap[e->getCanonID()] = oldId; TypePtrIdxPair &head = m_canonTypeMap[oldId]; int oldSecond = head.second; head.second = e->getCanonID(); m_canonTypeMap[e->getCanonID()] = TypePtrIdxPair(e->getAssertedType(), oldSecond); } } }
void RefDict::updateAccess(ExpressionPtr e) { always_assert(!e->getScope()->inPseudoMain()); int eid = e->getCanonID(); int context = e->getContext(); if (first_pass) { if (!e->is(Expression::KindOfSimpleVariable) && !e->is(Expression::KindOfDynamicVariable)) return; e->clearAvailable(); e->clearReferencedValid(); e->clearReferenced(); SimpleVariablePtr ptr(dynamic_pointer_cast<SimpleVariable>(e)); if (ptr && (ptr->isSuperGlobal() || ptr->isThis())) return; if (e->is(Expression::KindOfSimpleVariable)) { if (BitOps::get_bit(eid, m_referenced)) { e->setReferenced(); } else if (!BitOps::get_bit(eid, m_killed)) { // use as a temp place holder e->setAvailable(); } } } // let the first pass information propagate for both passes, since // we need it in both contexts if (context & Expression::RefAssignmentLHS || context & Expression::RefValue || context & Expression::RefParameter || ((context & Expression::Declaration) == Expression::Declaration)) { if (e->is(Expression::KindOfSimpleVariable)) { BitOps::set_bit(eid, m_referenced, true); BitOps::set_bit(eid, m_killed, false); } else { // for dynamic variables, we must assume the worst BitOps::set(size(), m_referenced, -1); BitOps::set(size(), m_killed, 0); } } else if (e->is(Expression::KindOfSimpleVariable) && context & Expression::LValue && context & Expression::UnsetContext) { BitOps::set_bit(eid, m_referenced, false); BitOps::set_bit(eid, m_killed, true); } if (first_pass) return; // now we're on the second pass if (context & Expression::AssignmentLHS || context & Expression::OprLValue) { // we dealt with this node as a store expression return; } int cls = e->getExprClass(); bool isRhsNeeded = false; bool canKill = false; ExpressionPtr lhs; ExpressionPtr rhs; if (cls & Expression::Store) { // we care about two cases here switch (e->getKindOf()) { case Expression::KindOfAssignmentExpression: // $x = ... { AssignmentExpressionPtr assign( static_pointer_cast<AssignmentExpression>(e)); lhs = assign->getVariable(); rhs = assign->getValue(); isRhsNeeded = Expression::CheckNeededRHS(rhs); canKill = true; } break; case Expression::KindOfBinaryOpExpression: // $x += ... { BinaryOpExpressionPtr binop( static_pointer_cast<BinaryOpExpression>(e)); if (binop->getOp() == T_PLUS_EQUAL) { lhs = binop->getExp1(); rhs = binop->getExp2(); isRhsNeeded = Expression::CheckNeededRHS(rhs); } } break; default: break; } } bool isLhsSimpleVar = false; bool isLhsDynamic = false; bool isRefd = false; if (lhs) { isLhsSimpleVar = lhs->is(Expression::KindOfSimpleVariable); // TODO: can a variable only be simple or dynamic? // If so, this is un-necessary isLhsDynamic = lhs->is(Expression::KindOfDynamicVariable); if (isLhsSimpleVar) { // clean up the LHS AST lhs->clearAvailable(); lhs->clearNeeded(); lhs->clearNeededValid(); if (BitOps::get_bit(lhs->getCanonID(), m_obj)) { lhs->setNeeded(); lhs->setNeededValid(); } else if (!BitOps::get_bit(lhs->getCanonID(), m_noobj)) { lhs->setAvailable(); } } if (lhs->isReferencedValid() && lhs->isReferenced() && isRhsNeeded) { // could we possibly have modified another referenced variable? isRefd = true; } if (isLhsSimpleVar && isRhsNeeded) { // we see this case: // $x = new ... // so we mark $x as being needed BitOps::set_bit(lhs->getCanonID(), m_obj, true); BitOps::set_bit(lhs->getCanonID(), m_noobj, false); } else if (isLhsSimpleVar && canKill && !isRhsNeeded) { // we saw an assignment that was of the form // $x = <primitive> // we can now set $x to be not an object BitOps::set_bit(lhs->getCanonID(), m_obj, false); BitOps::set_bit(lhs->getCanonID(), m_noobj, true); } } if (isLhsDynamic && isRhsNeeded) { // in this case, we must set EVERY variable to contain an object BitOps::set(size(), m_obj, -1 /* true for each bit */); BitOps::set(size(), m_noobj, 0 /* false for each bit */); // we're done, since it can be no worse (no more conservative) than this return; } // do we see a load which could cause the value of this expr to be changed? // for example: // function foo(&$x) { $x = 10; } // $x = 30; // foo($x); /* <-- this is what we care about */ if ((cls & (Expression::Load|Expression::Call)) && (context & (Expression::RefValue|Expression::DeepReference))) { isRefd = true; } // we want to propagate this information to other simple vars we see if (e->is(Expression::KindOfSimpleVariable)) { // clean up the AST e->clearAvailable(); e->clearNeeded(); e->clearNeededValid(); SimpleVariablePtr svp(static_pointer_cast<SimpleVariable>(e)); if (svp->isSuperGlobal() || svp->isThis()) return; // update the AST to *before* the modification if (BitOps::get_bit(eid, m_obj)) { e->setNeeded(); e->setNeededValid(); } else if (!BitOps::get_bit(eid, m_noobj)) { // use as a temp place holder e->setAvailable(); } if (context & Expression::LValue && context & Expression::UnsetContext) { always_assert(!isRefd); // unset($x); BitOps::set_bit(eid, m_obj, false); BitOps::set_bit(eid, m_noobj, true); } else if (isRefd || ((context & Expression::Declaration) == Expression::Declaration)) { // if a simple variable has isRefd, then we need to mark it // as potentially containing an object. // also, if the simple variable is in global context // then we also mark it as potentially containing an object BitOps::set_bit(eid, m_obj, true); BitOps::set_bit(eid, m_noobj, false); } } if (isRefd) { // do a scan for every simple variable referenced value // in the dictionary and mark it as potentially // containing an object (in the bit vector) for (int i = size(); i--; ) { if (ExpressionPtr e = get(i)) { always_assert(e->is(Expression::KindOfSimpleVariable)); always_assert(((unsigned int)i) == e->getCanonID()); if (BitOps::get_bit(i, m_referenced)) { BitOps::set_bit(i, m_obj, true); BitOps::set_bit(i, m_noobj, false); } } } } }
ExpressionPtr AliasManager::canonicalizeNode(ExpressionPtr e) { e->setCanonPtr(ExpressionPtr()); e->setCanonID(0); switch (e->getKindOf()) { case Expression::KindOfObjectMethodExpression: case Expression::KindOfDynamicFunctionCall: case Expression::KindOfSimpleFunctionCall: case Expression::KindOfNewObjectExpression: add(m_bucketMap[0], e); break; case Expression::KindOfListAssignment: add(m_bucketMap[0], e); break; case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr ae = spc(AssignmentExpression,e); if (e->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); return ae->getValue(); } ExpressionPtr rep; int interf = findInterf(ae->getVariable(), false, rep); if (interf == SameAccess) { switch (rep->getKindOf()) { default: break; case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr a = spc(AssignmentExpression, rep); ExpressionPtr value = a->getValue(); if (a->getValue()->getContext() & Expression::RefValue) { break; } } case Expression::KindOfUnaryOpExpression: case Expression::KindOfBinaryOpExpression: rep->setContext(Expression::DeadStore); break; } } add(m_bucketMap[0], e); break; } case Expression::KindOfConstantExpression: case Expression::KindOfSimpleVariable: case Expression::KindOfDynamicVariable: case Expression::KindOfArrayElementExpression: case Expression::KindOfObjectPropertyExpression: case Expression::KindOfStaticMemberExpression: if (!(e->getContext() & (Expression::AssignmentLHS| Expression::DeepAssignmentLHS| Expression::OprLValue))) { if (!(e->getContext() & (Expression::LValue| Expression::RefValue| Expression::RefParameter| Expression::UnsetContext))) { ExpressionPtr rep; int interf = findInterf(e, true, rep); if (interf == SameAccess) { if (rep->getKindOf() == e->getKindOf()) { e->setCanonID(rep->getCanonID()); e->setCanonPtr(rep); return ExpressionPtr(); } if (rep->getKindOf() == Expression::KindOfAssignmentExpression) { ExpressionPtr rhs = spc(AssignmentExpression,rep)->getValue(); if (rhs->is(Expression::KindOfScalarExpression)) { rhs = rhs->clone(); getCanonical(rhs); return rhs; } e->setCanonPtr(rhs); } } } add(m_bucketMap[0], e); } else { getCanonical(e); } break; case Expression::KindOfBinaryOpExpression: { BinaryOpExpressionPtr bop = spc(BinaryOpExpression, e); int rop = getOpForAssignmentOp(bop->getOp()); if (rop) { ExpressionPtr lhs = bop->getExp1(); ExpressionPtr rep; if (bop->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); ExpressionPtr rhs = bop->getExp2()->clone(); lhs = lhs->clone(); lhs->clearContext(Expression::LValue); lhs->clearContext(Expression::NoLValueWrapper); lhs->clearContext(Expression::OprLValue); rep = ExpressionPtr (new BinaryOpExpression(e->getLocation(), Expression::KindOfBinaryOpExpression, lhs, rhs, rop)); } else { ExpressionPtr alt; int interf = findInterf(lhs, true, alt); if (interf == SameAccess && alt->is(Expression::KindOfAssignmentExpression)) { ExpressionPtr op0 = spc(AssignmentExpression,alt)->getValue(); if (op0->is(Expression::KindOfScalarExpression)) { ExpressionPtr op1 = bop->getExp2(); ExpressionPtr rhs (new BinaryOpExpression(e->getLocation(), Expression::KindOfBinaryOpExpression, op0->clone(), op1->clone(), rop)); lhs = lhs->clone(); lhs->clearContext(Expression::OprLValue); rep = ExpressionPtr (new AssignmentExpression(e->getLocation(), Expression::KindOfAssignmentExpression, lhs, rhs, false)); } } } if (rep) { ExpressionPtr c = canonicalizeRecur(rep); return c ? c : rep; } add(m_bucketMap[0], e); } else { getCanonical(e); } break; } case Expression::KindOfUnaryOpExpression: { UnaryOpExpressionPtr uop = spc(UnaryOpExpression, e); switch (uop->getOp()) { case T_INC: case T_DEC: if (uop->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); ExpressionPtr val = uop->getExpression()->clone(); val->clearContext(Expression::LValue); val->clearContext(Expression::NoLValueWrapper); val->clearContext(Expression::OprLValue); if (uop->getFront()) { ExpressionPtr inc (new ScalarExpression(uop->getLocation(), Expression::KindOfScalarExpression, T_LNUMBER, string("1"))); val = ExpressionPtr (new BinaryOpExpression(uop->getLocation(), Expression::KindOfBinaryOpExpression, val, inc, uop->getOp() == T_INC ? '+' : '-')); } ExpressionPtr r = canonicalizeRecur(val); return r ? r : val; } add(m_bucketMap[0], e); break; default: getCanonical(e); break; } break; } default: getCanonical(e); break; } return ExpressionPtr(); }