| //=- LiveVariables.cpp - Live Variable Analysis for Source CFGs ----------*-==// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file implements Live Variables analysis for source-level CFGs. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "clang/Analysis/Analyses/LiveVariables.h" | |
| #include "clang/AST/Stmt.h" | |
| #include "clang/AST/StmtVisitor.h" | |
| #include "clang/Analysis/AnalysisDeclContext.h" | |
| #include "clang/Analysis/CFG.h" | |
| #include "clang/Analysis/FlowSensitive/DataflowWorklist.h" | |
| #include "clang/Basic/SourceManager.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/DenseSet.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <optional> | |
| #include <vector> | |
| using namespace clang; | |
| namespace { | |
| class LiveVariablesImpl { | |
| public: | |
| template <typename T> using SetTy = LiveVariables::SetTy<T>; | |
| AnalysisDeclContext &analysisContext; | |
| SetTy<const Expr *>::Factory ESetFact; | |
| SetTy<const VarDecl *>::Factory DSetFact; | |
| SetTy<const BindingDecl *>::Factory BSetFact; | |
| llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues> blocksEndToLiveness; | |
| llvm::DenseMap<const CFGBlock *, LiveVariables::LivenessValues> blocksBeginToLiveness; | |
| llvm::DenseMap<const Stmt *, LiveVariables::LivenessValues> stmtsToLiveness; | |
| llvm::DenseSet<const DeclRefExpr *> inAssignment; | |
| const bool killAtAssign; | |
| LiveVariables::LivenessValues | |
| merge(LiveVariables::LivenessValues valsA, | |
| LiveVariables::LivenessValues valsB); | |
| LiveVariables::LivenessValues | |
| runOnBlock(const CFGBlock *block, LiveVariables::LivenessValues val, | |
| LiveVariables::Observer *obs = nullptr); | |
| void dumpBlockLiveness(const SourceManager& M); | |
| void dumpExprLiveness(const SourceManager& M); | |
| LiveVariablesImpl(AnalysisDeclContext &ac, bool KillAtAssign) | |
| : analysisContext(ac), killAtAssign(KillAtAssign) {} | |
| }; | |
| } // namespace | |
| static LiveVariablesImpl &getImpl(void *x) { | |
| return *((LiveVariablesImpl *) x); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Operations and queries on LivenessValues. | |
| //===----------------------------------------------------------------------===// | |
| bool LiveVariables::LivenessValues::isLive(const Expr *E) const { | |
| return liveExprs.contains(E); | |
| } | |
| bool LiveVariables::LivenessValues::isLive(const VarDecl *D) const { | |
| if (const auto *DD = dyn_cast<DecompositionDecl>(D)) { | |
| // Note: the only known case this condition is necessary, is when a bindig | |
| // to a tuple-like structure is created. The HoldingVar initializers have a | |
| // DeclRefExpr to the DecompositionDecl. | |
| if (liveDecls.contains(DD)) | |
| return true; | |
| for (const BindingDecl *BD : DD->bindings()) { | |
| if (liveBindings.contains(BD)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| return liveDecls.contains(D); | |
| } | |
| void LiveVariables::Observer::anchor() { } | |
| LiveVariables::LivenessValues | |
| LiveVariablesImpl::merge(LiveVariables::LivenessValues valsA, | |
| LiveVariables::LivenessValues valsB) { | |
| // Liveness at a merge point is the union of the successors' live sets. These | |
| // sets are not canonicalized; LivenessValues::operator== compares them | |
| // structurally. | |
| return LiveVariables::LivenessValues( | |
| ESetFact.unionSets(valsA.liveExprs, valsB.liveExprs), | |
| DSetFact.unionSets(valsA.liveDecls, valsB.liveDecls), | |
| BSetFact.unionSets(valsA.liveBindings, valsB.liveBindings)); | |
| } | |
| bool LiveVariables::LivenessValues::operator==(const LivenessValues &V) const { | |
| return liveExprs == V.liveExprs && liveDecls == V.liveDecls && | |
| liveBindings == V.liveBindings; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Query methods. | |
| //===----------------------------------------------------------------------===// | |
| static bool isAlwaysAlive(const VarDecl *D) { | |
| return D->hasGlobalStorage(); | |
| } | |
| bool LiveVariables::isLive(const CFGBlock *B, const VarDecl *D) { | |
| return isAlwaysAlive(D) || getImpl(impl).blocksEndToLiveness[B].isLive(D); | |
| } | |
| bool LiveVariables::isLive(const Stmt *S, const VarDecl *D) { | |
| return isAlwaysAlive(D) || getImpl(impl).stmtsToLiveness[S].isLive(D); | |
| } | |
| bool LiveVariables::isLive(const Stmt *Loc, const Expr *Val) { | |
| return getImpl(impl).stmtsToLiveness[Loc].isLive(Val); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Dataflow computation. | |
| //===----------------------------------------------------------------------===// | |
| namespace { | |
| class TransferFunctions : public StmtVisitor<TransferFunctions> { | |
| LiveVariablesImpl &LV; | |
| LiveVariables::LivenessValues &val; | |
| LiveVariables::Observer *observer; | |
| const CFGBlock *currentBlock; | |
| public: | |
| TransferFunctions(LiveVariablesImpl &im, | |
| LiveVariables::LivenessValues &Val, | |
| LiveVariables::Observer *Observer, | |
| const CFGBlock *CurrentBlock) | |
| : LV(im), val(Val), observer(Observer), currentBlock(CurrentBlock) {} | |
| void VisitBinaryOperator(BinaryOperator *BO); | |
| void VisitBlockExpr(BlockExpr *BE); | |
| void VisitDeclRefExpr(DeclRefExpr *DR); | |
| void VisitDeclStmt(DeclStmt *DS); | |
| void VisitObjCForCollectionStmt(ObjCForCollectionStmt *OS); | |
| void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE); | |
| void Visit(Stmt *S); | |
| }; | |
| } // namespace | |
| static const VariableArrayType *FindVA(QualType Ty) { | |
| const Type *ty = Ty.getTypePtr(); | |
| while (const ArrayType *VT = dyn_cast<ArrayType>(ty)) { | |
| if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(VT)) | |
| if (VAT->getSizeExpr()) | |
| return VAT; | |
| ty = VT->getElementType().getTypePtr(); | |
| } | |
| return nullptr; | |
| } | |
| static const Expr *LookThroughExpr(const Expr *E) { | |
| while (E) { | |
| E = E->IgnoreParens(); | |
| if (const FullExpr *FE = dyn_cast<FullExpr>(E)) { | |
| E = FE->getSubExpr(); | |
| continue; | |
| } | |
| if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) { | |
| E = OVE->getSourceExpr(); | |
| continue; | |
| } | |
| break; | |
| } | |
| return E; | |
| } | |
| static void AddLiveExpr(LiveVariables::SetTy<const Expr *> &Set, | |
| LiveVariables::SetTy<const Expr *>::Factory &F, | |
| const Expr *E) { | |
| Set = F.add(Set, LookThroughExpr(E)); | |
| } | |
| /// Add as a live expression all individual conditions in a logical expression. | |
| /// For example, for the expression: | |
| /// "(a < b) || (c && d && ((e || f) != (g && h)))" | |
| /// the following expressions will be added as live: | |
| /// "a < b", "c", "d", "((e || f) != (g && h))" | |
| static void | |
| AddAllConditionalTerms(LiveVariables::SetTy<const Expr *> &Set, | |
| LiveVariables::SetTy<const Expr *>::Factory &F, | |
| const Expr *Cond) { | |
| AddLiveExpr(Set, F, Cond); | |
| if (auto const *BO = dyn_cast<BinaryOperator>(Cond->IgnoreParens()); | |
| BO && BO->isLogicalOp()) { | |
| AddAllConditionalTerms(Set, F, BO->getLHS()); | |
| AddAllConditionalTerms(Set, F, BO->getRHS()); | |
| } | |
| } | |
| void TransferFunctions::Visit(Stmt *S) { | |
| if (observer) | |
| observer->observeStmt(S, currentBlock, val); | |
| StmtVisitor<TransferFunctions>::Visit(S); | |
| if (const auto *E = dyn_cast<Expr>(S)) { | |
| val.liveExprs = LV.ESetFact.remove(val.liveExprs, E); | |
| } | |
| // Mark all children expressions live. | |
| // The "normal" case will be handled by iterating over 'S->children()' but | |
| // before that we need this big 'switch' to handle the statement kinds where | |
| // 'S->children()' isn't the exactly equal to the set of child expressions | |
| // that we want to keep alive. (In some cases we need to skip some of the | |
| // children, in other cases there are unusual child expressions that do not | |
| // appear in 'S->children()'.) | |
| switch (S->getStmtClass()) { | |
| default: | |
| break; | |
| case Stmt::StmtExprClass: { | |
| // For statement expressions, look through the compound statement. | |
| S = cast<StmtExpr>(S)->getSubStmt(); | |
| break; | |
| } | |
| case Stmt::CXXMemberCallExprClass: { | |
| // Include the implicit "this" pointer as being live. | |
| CXXMemberCallExpr *CE = cast<CXXMemberCallExpr>(S); | |
| if (Expr *ImplicitObj = CE->getImplicitObjectArgument()) { | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, ImplicitObj); | |
| } | |
| break; | |
| } | |
| case Stmt::ObjCMessageExprClass: { | |
| // In calls to super, include the implicit "self" pointer as being live. | |
| ObjCMessageExpr *CE = cast<ObjCMessageExpr>(S); | |
| if (CE->getReceiverKind() == ObjCMessageExpr::SuperInstance) | |
| val.liveDecls = LV.DSetFact.add(val.liveDecls, | |
| LV.analysisContext.getSelfDecl()); | |
| break; | |
| } | |
| case Stmt::DeclStmtClass: { | |
| const DeclStmt *DS = cast<DeclStmt>(S); | |
| if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) { | |
| for (const VariableArrayType* VA = FindVA(VD->getType()); | |
| VA != nullptr; VA = FindVA(VA->getElementType())) { | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, VA->getSizeExpr()); | |
| } | |
| } | |
| break; | |
| } | |
| case Stmt::AttributedStmtClass: { | |
| // In an attributed statement, include the assumptions of the | |
| // [[assume(...)]] attributes as being live. | |
| AttributedStmt *AS = cast<AttributedStmt>(S); | |
| for (const auto *Attr : getSpecificAttrs<CXXAssumeAttr>(AS->getAttrs())) { | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, Attr->getAssumption()); | |
| } | |
| break; | |
| } | |
| case Stmt::PseudoObjectExprClass: { | |
| // A pseudo-object operation only directly consumes its result | |
| // expression. | |
| Expr *child = cast<PseudoObjectExpr>(S)->getResultExpr(); | |
| if (!child) return; | |
| if (OpaqueValueExpr *OV = dyn_cast<OpaqueValueExpr>(child)) | |
| child = OV->getSourceExpr(); | |
| child = child->IgnoreParens(); | |
| val.liveExprs = LV.ESetFact.add(val.liveExprs, child); | |
| return; | |
| } | |
| // FIXME: These cases eventually shouldn't be needed. | |
| case Stmt::ExprWithCleanupsClass: { | |
| S = cast<ExprWithCleanups>(S)->getSubExpr(); | |
| break; | |
| } | |
| case Stmt::CXXBindTemporaryExprClass: { | |
| S = cast<CXXBindTemporaryExpr>(S)->getSubExpr(); | |
| break; | |
| } | |
| case Stmt::UnaryExprOrTypeTraitExprClass: { | |
| // No need to unconditionally visit subexpressions. | |
| return; | |
| } | |
| case Stmt::IfStmtClass: { | |
| // If one of the branches is an expression rather than a compound | |
| // statement, it will be bad if we mark it as live at the terminator | |
| // of the if-statement (i.e., immediately after the condition expression). | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, cast<IfStmt>(S)->getCond()); | |
| return; | |
| } | |
| case Stmt::WhileStmtClass: { | |
| // If the loop body is an expression rather than a compound statement, | |
| // it will be bad if we mark it as live at the terminator of the loop | |
| // (i.e., immediately after the condition expression). | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, cast<WhileStmt>(S)->getCond()); | |
| return; | |
| } | |
| case Stmt::DoStmtClass: { | |
| // If the loop body is an expression rather than a compound statement, | |
| // it will be bad if we mark it as live at the terminator of the loop | |
| // (i.e., immediately after the condition expression). | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, cast<DoStmt>(S)->getCond()); | |
| return; | |
| } | |
| case Stmt::ForStmtClass: { | |
| // If the loop body is an expression rather than a compound statement, | |
| // it will be bad if we mark it as live at the terminator of the loop | |
| // (i.e., immediately after the condition expression). | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, cast<ForStmt>(S)->getCond()); | |
| return; | |
| } | |
| case Stmt::ConditionalOperatorClass: { | |
| // Keep not only direct children alive, but also all the short-circuited | |
| // parts of the condition. Short-circuiting evaluation may cause the | |
| // conditional operator evaluation to skip the evaluation of the entire | |
| // condtion expression, so the value of the entire condition expression is | |
| // never computed. | |
| // | |
| // This makes a difference when we compare exploded nodes coming from true | |
| // and false expressions with no side effects: the only difference in the | |
| // state is the value of (part of) the condition. | |
| // | |
| // BinaryConditionalOperatorClass ('x ?: y') is not affected because it | |
| // explicitly calculates the value of the entire condition expression (to | |
| // possibly use as a value for the "true expr") even if it is | |
| // short-circuited. | |
| auto const *CO = cast<ConditionalOperator>(S); | |
| AddAllConditionalTerms(val.liveExprs, LV.ESetFact, CO->getCond()); | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, CO->getTrueExpr()); | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, CO->getFalseExpr()); | |
| return; | |
| } | |
| case Stmt::BinaryOperatorClass: { | |
| auto const *BO = cast<BinaryOperator>(S); | |
| if (BO->isLogicalOp()) { | |
| AddAllConditionalTerms(val.liveExprs, LV.ESetFact, BO->getLHS()); | |
| AddAllConditionalTerms(val.liveExprs, LV.ESetFact, BO->getRHS()); | |
| return; | |
| } | |
| break; | |
| } | |
| } | |
| // Mark all child expressions live -- "normal" case. | |
| for (Stmt *Child : S->children()) { | |
| if (const auto *E = dyn_cast_or_null<Expr>(Child)) | |
| AddLiveExpr(val.liveExprs, LV.ESetFact, E); | |
| } | |
| } | |
| static bool writeShouldKill(const VarDecl *VD) { | |
| return VD && !VD->getType()->isReferenceType() && | |
| !isAlwaysAlive(VD); | |
| } | |
| void TransferFunctions::VisitBinaryOperator(BinaryOperator *B) { | |
| if (LV.killAtAssign && B->getOpcode() == BO_Assign) { | |
| if (const auto *DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParens())) { | |
| LV.inAssignment.insert(DR); | |
| } | |
| } | |
| if (B->isAssignmentOp()) { | |
| if (!LV.killAtAssign) | |
| return; | |
| // Assigning to a variable? | |
| Expr *LHS = B->getLHS()->IgnoreParens(); | |
| if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS)) { | |
| const Decl* D = DR->getDecl(); | |
| bool Killed = false; | |
| if (const BindingDecl* BD = dyn_cast<BindingDecl>(D)) { | |
| Killed = !BD->getType()->isReferenceType(); | |
| if (Killed) { | |
| if (const auto *HV = BD->getHoldingVar()) | |
| val.liveDecls = LV.DSetFact.remove(val.liveDecls, HV); | |
| val.liveBindings = LV.BSetFact.remove(val.liveBindings, BD); | |
| } | |
| } else if (const auto *VD = dyn_cast<VarDecl>(D)) { | |
| Killed = writeShouldKill(VD); | |
| if (Killed) | |
| val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); | |
| } | |
| } | |
| } | |
| } | |
| void TransferFunctions::VisitBlockExpr(BlockExpr *BE) { | |
| for (const VarDecl *VD : | |
| LV.analysisContext.getReferencedBlockVars(BE->getBlockDecl())) { | |
| if (isAlwaysAlive(VD)) | |
| continue; | |
| val.liveDecls = LV.DSetFact.add(val.liveDecls, VD); | |
| } | |
| } | |
| void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *DR) { | |
| const Decl* D = DR->getDecl(); | |
| bool InAssignment = LV.inAssignment.contains(DR); | |
| if (const auto *BD = dyn_cast<BindingDecl>(D)) { | |
| if (!InAssignment) { | |
| if (const auto *HV = BD->getHoldingVar()) | |
| val.liveDecls = LV.DSetFact.add(val.liveDecls, HV); | |
| val.liveBindings = LV.BSetFact.add(val.liveBindings, BD); | |
| } | |
| } else if (const auto *VD = dyn_cast<VarDecl>(D)) { | |
| if (!InAssignment && !isAlwaysAlive(VD)) | |
| val.liveDecls = LV.DSetFact.add(val.liveDecls, VD); | |
| } | |
| } | |
| void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { | |
| for (const auto *DI : DS->decls()) { | |
| if (const auto *DD = dyn_cast<DecompositionDecl>(DI)) { | |
| for (const auto *BD : DD->bindings()) { | |
| if (const auto *HV = BD->getHoldingVar()) | |
| val.liveDecls = LV.DSetFact.remove(val.liveDecls, HV); | |
| val.liveBindings = LV.BSetFact.remove(val.liveBindings, BD); | |
| } | |
| // When a bindig to a tuple-like structure is created, the HoldingVar | |
| // initializers have a DeclRefExpr to the DecompositionDecl. | |
| val.liveDecls = LV.DSetFact.remove(val.liveDecls, DD); | |
| } else if (const auto *VD = dyn_cast<VarDecl>(DI)) { | |
| if (!isAlwaysAlive(VD)) | |
| val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); | |
| } | |
| } | |
| } | |
| void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *OS) { | |
| // Kill the iteration variable. | |
| DeclRefExpr *DR = nullptr; | |
| const VarDecl *VD = nullptr; | |
| Stmt *element = OS->getElement(); | |
| if (DeclStmt *DS = dyn_cast<DeclStmt>(element)) { | |
| VD = cast<VarDecl>(DS->getSingleDecl()); | |
| } | |
| else if ((DR = dyn_cast<DeclRefExpr>(cast<Expr>(element)->IgnoreParens()))) { | |
| VD = cast<VarDecl>(DR->getDecl()); | |
| } | |
| if (VD) { | |
| val.liveDecls = LV.DSetFact.remove(val.liveDecls, VD); | |
| } | |
| } | |
| void TransferFunctions:: | |
| VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *UE) | |
| { | |
| // While sizeof(var) doesn't technically extend the liveness of 'var', it | |
| // does extent the liveness of metadata if 'var' is a VariableArrayType. | |
| // We handle that special case here. | |
| if (UE->getKind() != UETT_SizeOf || UE->isArgumentType()) | |
| return; | |
| const Expr *subEx = UE->getArgumentExpr(); | |
| if (subEx->getType()->isVariableArrayType()) { | |
| assert(subEx->isLValue()); | |
| val.liveExprs = LV.ESetFact.add(val.liveExprs, subEx->IgnoreParens()); | |
| } | |
| } | |
| LiveVariables::LivenessValues | |
| LiveVariablesImpl::runOnBlock(const CFGBlock *block, | |
| LiveVariables::LivenessValues val, | |
| LiveVariables::Observer *obs) { | |
| TransferFunctions TF(*this, val, obs, block); | |
| // Visit the terminator (if any). | |
| if (const Stmt *term = block->getTerminatorStmt()) | |
| TF.Visit(const_cast<Stmt*>(term)); | |
| // Apply the transfer function for all Stmts in the block. | |
| for (CFGBlock::const_reverse_iterator it = block->rbegin(), | |
| ei = block->rend(); it != ei; ++it) { | |
| const CFGElement &elem = *it; | |
| if (std::optional<CFGAutomaticObjDtor> Dtor = | |
| elem.getAs<CFGAutomaticObjDtor>()) { | |
| val.liveDecls = DSetFact.add(val.liveDecls, Dtor->getVarDecl()); | |
| continue; | |
| } | |
| if (!elem.getAs<CFGStmt>()) | |
| continue; | |
| const Stmt *S = elem.castAs<CFGStmt>().getStmt(); | |
| TF.Visit(const_cast<Stmt*>(S)); | |
| stmtsToLiveness[S] = val; | |
| } | |
| return val; | |
| } | |
| void LiveVariables::runOnAllBlocks(LiveVariables::Observer &obs) { | |
| const CFG *cfg = getImpl(impl).analysisContext.getCFG(); | |
| for (CFGBlock *B : *cfg) | |
| getImpl(impl).runOnBlock(B, getImpl(impl).blocksEndToLiveness[B], &obs); | |
| } | |
| LiveVariables::LiveVariables(void *im) : impl(im) {} | |
| LiveVariables::~LiveVariables() { | |
| delete (LiveVariablesImpl*) impl; | |
| } | |
| std::unique_ptr<LiveVariables> | |
| LiveVariables::computeLiveness(AnalysisDeclContext &AC, bool killAtAssign) { | |
| // No CFG? Bail out. | |
| CFG *cfg = AC.getCFG(); | |
| if (!cfg) | |
| return nullptr; | |
| // The analysis currently has scalability issues for very large CFGs. | |
| // Bail out if it looks too large. | |
| if (cfg->getNumBlockIDs() > 300000) | |
| return nullptr; | |
| LiveVariablesImpl *LV = new LiveVariablesImpl(AC, killAtAssign); | |
| // Construct the dataflow worklist. Enqueue the exit block as the | |
| // start of the analysis. | |
| BackwardDataflowWorklist worklist(*cfg, AC); | |
| llvm::BitVector everAnalyzedBlock(cfg->getNumBlockIDs()); | |
| // FIXME: we should enqueue using post order. | |
| for (const CFGBlock *B : cfg->nodes()) { | |
| worklist.enqueueBlock(B); | |
| } | |
| while (const CFGBlock *block = worklist.dequeue()) { | |
| // Determine if the block's end value has changed. If not, we | |
| // have nothing left to do for this block. | |
| LivenessValues &prevVal = LV->blocksEndToLiveness[block]; | |
| // Merge the values of all successor blocks. | |
| LivenessValues val; | |
| for (const CFGBlock *succ : block->succs()) { | |
| if (succ) { | |
| val = LV->merge(val, LV->blocksBeginToLiveness[succ]); | |
| } | |
| } | |
| if (!everAnalyzedBlock[block->getBlockID()]) | |
| everAnalyzedBlock[block->getBlockID()] = true; | |
| else if (prevVal == val) | |
| continue; | |
| prevVal = val; | |
| // Update the dataflow value for the start of this block. | |
| LV->blocksBeginToLiveness[block] = LV->runOnBlock(block, val); | |
| // Enqueue the value to the predecessors. | |
| worklist.enqueuePredecessors(block); | |
| } | |
| return std::unique_ptr<LiveVariables>(new LiveVariables(LV)); | |
| } | |
| void LiveVariables::dumpBlockLiveness(const SourceManager &M) { | |
| getImpl(impl).dumpBlockLiveness(M); | |
| } | |
| void LiveVariablesImpl::dumpBlockLiveness(const SourceManager &M) { | |
| std::vector<const CFGBlock *> vec; | |
| vec.reserve(blocksEndToLiveness.size()); | |
| llvm::append_range(vec, llvm::make_first_range(blocksEndToLiveness)); | |
| llvm::sort(vec, [](const CFGBlock *A, const CFGBlock *B) { | |
| return A->getBlockID() < B->getBlockID(); | |
| }); | |
| std::vector<const VarDecl*> declVec; | |
| for (const CFGBlock *block : vec) { | |
| llvm::errs() << "\n[ B" << block->getBlockID() | |
| << " (live variables at block exit) ]\n"; | |
| declVec.clear(); | |
| llvm::append_range(declVec, blocksEndToLiveness[block].liveDecls); | |
| llvm::sort(declVec, [](const Decl *A, const Decl *B) { | |
| return A->getBeginLoc() < B->getBeginLoc(); | |
| }); | |
| for (const VarDecl *VD : declVec) { | |
| llvm::errs() << " " << VD->getDeclName().getAsString() << " <"; | |
| VD->getLocation().print(llvm::errs(), M); | |
| llvm::errs() << ">\n"; | |
| } | |
| } | |
| llvm::errs() << "\n"; | |
| } | |
| void LiveVariables::dumpExprLiveness(const SourceManager &M) { | |
| getImpl(impl).dumpExprLiveness(M); | |
| } | |
| void LiveVariablesImpl::dumpExprLiveness(const SourceManager &M) { | |
| const ASTContext &Ctx = analysisContext.getASTContext(); | |
| auto ByIDs = [&Ctx](const Expr *L, const Expr *R) { | |
| return L->getID(Ctx) < R->getID(Ctx); | |
| }; | |
| // Don't iterate over blockEndsToLiveness directly because it's not sorted. | |
| for (const CFGBlock *B : *analysisContext.getCFG()) { | |
| llvm::errs() << "\n[ B" << B->getBlockID() | |
| << " (live expressions at block exit) ]\n"; | |
| std::vector<const Expr *> LiveExprs; | |
| llvm::append_range(LiveExprs, blocksEndToLiveness[B].liveExprs); | |
| llvm::sort(LiveExprs, ByIDs); | |
| for (const Expr *E : LiveExprs) { | |
| llvm::errs() << "\n"; | |
| E->dump(); | |
| } | |
| llvm::errs() << "\n"; | |
| } | |
| } | |
| const void *LiveVariables::getTag() { static int x; return &x; } | |
| const void *RelaxedLiveVariables::getTag() { static int x; return &x; } |