bool QuantifiedConnective::eq(const PathExpressionImpl &o) const { auto optr = static_cast<const QuantifiedConnective*>(&o); for (auto qvars : util::zip(getQuantifiedVars(), optr->getQuantifiedVars())) { if (qvars.first != qvars.second) { return false; } } return getLhs() == optr->getLhs() && getRhs() == optr->getRhs(); }
bool Link::lt(const PathExpressionImpl &o) const { auto optr = static_cast<const Link*>(&o); if (!hasStencil() && optr->hasStencil()) { return true; } else if (hasStencil() && !optr->hasStencil()) { return false; } else if (hasStencil() && optr->hasStencil()) { if (getStencil() != optr->getStencil()) return getStencil() < optr->getStencil(); } return (getLhs().getSet() != optr->getLhs().getSet()) ? getLhs().getSet() < optr->getLhs().getSet() : getRhs().getSet() < optr->getRhs().getSet(); }
bool Link::eq(const PathExpressionImpl &o) const { auto optr = static_cast<const Link*>(&o); return this->getLhs().getSet() == optr->getLhs().getSet() && this->getRhs().getSet() == optr->getRhs().getSet() && this->hasStencil() == optr->hasStencil() && (!this->hasStencil() || this->getStencil() == optr->getStencil()); }
bool QuantifiedConnective::lt(const PathExpressionImpl &o) const { auto optr = static_cast<const QuantifiedConnective*>(&o); if (getLhs() != optr->getLhs()) { return getLhs() < optr->getLhs(); } else if (getRhs() != optr->getRhs()) { return getRhs() < optr->getRhs(); } else { if (getQuantifiedVars().size() != optr->getQuantifiedVars().size()) { return getQuantifiedVars().size() < optr->getQuantifiedVars().size(); } for (auto qv : util::zip(getQuantifiedVars(), optr->getQuantifiedVars())) { if (qv.first != qv.second) { return qv.first < qv.second; } } return false; } }
std::ostream& PhiInsn::printTo(std::ostream& stream) const { getLhs()->printTo(stream); stream << " = phi("; bool first = true; for (const auto& cur : getRhs()) { if (first) { first = false; } else stream << ","; cur->printTo(stream); } stream << ")"; return stream; }
/** * @brief Replaces @a var with @a expr in @a stmt. */ void replaceVarWithExprInStmt(ShPtr<Variable> var, ShPtr<Expression> expr, ShPtr<Statement> stmt) { // If stmt is not a variable-defining/assign statement, we can directly // replace the variable. if (!isVarDefOrAssignStmt(stmt)) { stmt->replace(var, expr); return; } // The statement is of the form // // someVar = rhs(stmt) // // If the left-hand side of the statement differs from var, we may also // directly replace the variable in the statement. if (getLhs(stmt) != var) { stmt->replace(var, expr); return; } // The statement is of the form // // var = rhs(stmt) // // We have to replace var only on the right-hand side of the statement. // If rhs(stmt) differs from vars, we may directly replace the variable in // the right-hand side of the statement. if (getRhs(stmt) != var) { getRhs(stmt)->replace(var, expr); return; } // The statement is of the form // // var = var // // so set the right-hand side using the appropriate methods instead of // using getRhs(stmt)->replace(), which wouldn't work in this case. if (auto assignStmt = cast<AssignStmt>(stmt)) { assignStmt->setRhs(expr); } else if (auto varDefStmt = cast<VarDefStmt>(stmt)) { varDefStmt->setInitializer(expr); } else { FAIL("this should never happen since stmt has to be either a VarDefStmt" " or AssignStmt; stmt is `" << stmt << "`"); } }
std::ostream& AssignInsn::printTo(std::ostream& stream) const { getLhs()->printTo(stream); stream << " = "; if (isAssign()) { getRhs1()->printTo(stream); } else if (isUnary()) { stream << op; getRhs1()->printTo(stream); } else { getRhs1()->printTo(stream); stream << op; getRhs2()->printTo(stream); } return stream; }
/** * @brief Tries to perform the optimization on the given statement. * * @par Preconditions * - @a stmt is either a VarDefStmt or AssignStmt */ void SimpleCopyPropagationOptimizer::tryOptimization(ShPtr<Statement> stmt) { ShPtr<Variable> lhsVar(cast<Variable>(getLhs(stmt))); ShPtr<Expression> rhs(getRhs(stmt)); if (!lhsVar || !rhs) { // There is nothing we can do in this case. return; } if (hasItem(triedVars, lhsVar)) { // We have already tried this variable. return; } triedVars.insert(lhsVar); if (!currFunc->hasLocalVar(lhsVar)) { // The left-hand side is not a local variable. return; } if (module->hasAssignedDebugName(lhsVar)) { // The left-hand side has assigned a name from debug information. return; } if (lhsVar->isExternal()) { // We do not want to optimize external variables (used in a volatile // load/store, see #1146). return; } if (va->mayBePointed(lhsVar)) { // The left-hand side may be used indirectly. return; } if (isa<ConstString>(rhs) || isa<ConstArray>(rhs) || isa<ConstStruct>(rhs)) { // The expression cannot be any of the above types. // TODO What about dropping this restriction? return; } if (lhsVar == rhs) { // Do not optimize self assigns, i.e. statements of the form `a = a;`. // This is done in other optimizations. return; } ShPtr<ValueData> stmtData(va->getValueData(stmt)); if (stmtData->hasAddressOps() || stmtData->hasDerefs() || stmtData->hasArrayAccesses() || stmtData->hasStructAccesses()) { // A forbidden construction is used. return; } // Try to perform the proper case of the optimization (see the class // description). if (stmtData->hasCalls()) { tryOptimizationCase1(stmt, lhsVar, rhs); } else { tryOptimizationCase2(stmt, lhsVar, rhs); } }
/** * @brief Computes the FuncInfo and returns it. */ ShPtr<OptimFuncInfo> OptimFuncInfoCFGTraversal::performComputation() { // First, we pre-compute varsAlwaysModifiedBeforeRead. The reason is that // their computation differs from the computation of the rest of the sets. precomputeAlwaysModifiedVarsBeforeRead(); // Every function's body is of the following form: // // (1) definitions of local variables, including assignments of global // variables into local variables // (2) other statements // // We store which variables are read/modified in (1). Then, we start the // traversal from (2). During the traversal, we check which variables are // read/modified and update funcInfo accordingly. The stored information // from (1) is used to compute the set of global variables which are read // in the function, but not modified. // // To give a specific example, consider the following code: // // def func(mango): // global orange // global plum // lychee = orange // achira = plum // orange = mango // plum = rand() // result = plum * apple + orange // orange = lychee // plum = achira // return result // // Here, even though the global variable orange is modified, its value // before calling func() is the same as after calling func(). Indeed, its // value is restored before the return statement. Hence, we may put it into // funcInfo->varsWithNeverChangedValue. // TODO Implement a more robust analysis. ShPtr<Statement> currStmt = traversedFunc->getBody(); while (isVarDefOrAssignStmt(currStmt)) { updateFuncInfo(currStmt); ShPtr<Expression> lhs(getLhs(currStmt)); ShPtr<Expression> rhs(getRhs(currStmt)); // If there is no right-hand side, it is a VarDefStmt with no // initializer, which we may skip. if (!rhs) { currStmt = currStmt->getSuccessor(); continue; } // If there are any function calls or dereferences, we have reached // (2). ShPtr<ValueData> currStmtData(va->getValueData(currStmt)); if (currStmtData->hasCalls() || currStmtData->hasDerefs()) { break; } // Check whether the statement is of the form localVar = globalVar. ShPtr<Variable> localVar(cast<Variable>(lhs)); ShPtr<Variable> globalVar(cast<Variable>(rhs)); if (!localVar || !globalVar || hasItem(globalVars, localVar) || !hasItem(globalVars, globalVar)) { // It is not of the abovementioned form, so skip it. currStmt = currStmt->getSuccessor(); continue; } storedGlobalVars[globalVar] = localVar; currStmt = currStmt->getSuccessor(); } // Perform the traversal only if we haven't reached the end of the function // yet. Since empty statements are not present in a CFG, skip them before // the traversal. if ((currStmt = skipEmptyStmts(currStmt))) { performTraversal(currStmt); } // We use the exit node of the CFG to check that every variable from // storedGlobalVars is retrieved its original value before every return. ShPtr<CFG::Node> exitNode(cfg->getExitNode()); // For every predecessor of the exit node... for (auto i = exitNode->pred_begin(), e = exitNode->pred_end(); i != e; ++i) { bool checkingShouldContinue = checkExitNodesPredecessor((*i)->getSrc()); if (!checkingShouldContinue) { break; } } // Update funcInfo using the remaining variables in storedGlobalVars. for (const auto &p : storedGlobalVars) { funcInfo->varsWithNeverChangedValue.insert(p.first); } // Update funcInfo->never{Read,Modified}Vars by global variables which are // untouched in this function. for (auto i = module->global_var_begin(), e = module->global_var_end(); i != e; ++i) { ShPtr<Variable> var((*i)->getVar()); if (!hasItem(funcInfo->mayBeReadVars, var) && !hasItem(funcInfo->mayBeModifiedVars, var)) { funcInfo->neverReadVars.insert(var); funcInfo->neverModifiedVars.insert(var); } } // If the cfg contains only a single non-{entry,exit} node, every // mayBe{Read,Modifed} variable can be turned into a always{Read,Modified} // variable. if (cfg->getNumberOfNodes() == 3) { addToSet(funcInfo->mayBeReadVars, funcInfo->alwaysReadVars); addToSet(funcInfo->mayBeModifiedVars, funcInfo->alwaysModifiedVars); } // Add all variables which are never read and never modified to // varsWithNeverChangedValue. VarSet neverReadAndModifedVars(setIntersection(funcInfo->neverReadVars, funcInfo->neverModifiedVars)); addToSet(neverReadAndModifedVars, funcInfo->varsWithNeverChangedValue); // Add all global variables are not read in this function into // varsAlwaysModifiedBeforeRead. addToSet(setDifference(globalVars, funcInfo->mayBeReadVars), funcInfo->varsAlwaysModifiedBeforeRead); return funcInfo; }