diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 15e63c3d91a32..8494bc197cb25 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -226,9 +226,8 @@ class Environment { /// Requirements: /// /// `Other` and `this` must use the same `DataflowAnalysisContext`. - LatticeJoinEffect join(const Environment &Other, - Environment::ValueModel &Model); - + Environment join(const Environment &Other, + Environment::ValueModel &Model) const; /// Widens the environment point-wise, using `PrevEnv` as needed to inform the /// approximation. diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index b2e67651626d5..70dbe6dbb7a24 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -526,14 +526,12 @@ LatticeJoinEffect Environment::widen(const Environment &PrevEnv, return Effect; } -LatticeJoinEffect Environment::join(const Environment &Other, - Environment::ValueModel &Model) { +Environment Environment::join(const Environment &Other, + Environment::ValueModel &Model) const { assert(DACtx == Other.DACtx); assert(ThisPointeeLoc == Other.ThisPointeeLoc); assert(CallStack == Other.CallStack); - auto Effect = LatticeJoinEffect::Unchanged; - Environment JoinedEnv(*DACtx); JoinedEnv.CallStack = CallStack; @@ -560,7 +558,6 @@ LatticeJoinEffect Environment::join(const Environment &Other, mergeDistinctValues(Func->getReturnType(), *ReturnVal, *this, *Other.ReturnVal, Other, JoinedEnv, Model)) { JoinedEnv.ReturnVal = MergedVal; - Effect = LatticeJoinEffect::Changed; } } @@ -574,19 +571,12 @@ LatticeJoinEffect Environment::join(const Environment &Other, // `DeclToLoc` and `Other.DeclToLoc` that map the same declaration to // different storage locations. JoinedEnv.DeclToLoc = intersectDenseMaps(DeclToLoc, Other.DeclToLoc); - if (DeclToLoc.size() != JoinedEnv.DeclToLoc.size()) - Effect = LatticeJoinEffect::Changed; JoinedEnv.ExprToLoc = intersectDenseMaps(ExprToLoc, Other.ExprToLoc); - if (ExprToLoc.size() != JoinedEnv.ExprToLoc.size()) - Effect = LatticeJoinEffect::Changed; JoinedEnv.MemberLocToStruct = intersectDenseMaps(MemberLocToStruct, Other.MemberLocToStruct); - if (MemberLocToStruct.size() != JoinedEnv.MemberLocToStruct.size()) - Effect = LatticeJoinEffect::Changed; - // FIXME: set `Effect` as needed. // FIXME: update join to detect backedges and simplify the flow condition // accordingly. JoinedEnv.FlowConditionToken = &DACtx->joinFlowConditions( @@ -613,15 +603,10 @@ LatticeJoinEffect Environment::join(const Environment &Other, mergeDistinctValues(Loc->getType(), *Val, *this, *It->second, Other, JoinedEnv, Model)) { JoinedEnv.LocToVal.insert({Loc, MergedVal}); - Effect = LatticeJoinEffect::Changed; } } - if (LocToVal.size() != JoinedEnv.LocToVal.size()) - Effect = LatticeJoinEffect::Changed; - *this = std::move(JoinedEnv); - - return Effect; + return JoinedEnv; } StorageLocation &Environment::createStorageLocation(QualType Type) { diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index 1f23b6cf238f7..2d49dc6f44fd4 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -221,6 +221,57 @@ class PrettyStackTraceCFGElement : public llvm::PrettyStackTraceEntry { const char *Message; }; +// Builds a joined TypeErasedDataflowAnalysisState from 0 or more sources, +// each of which may be an owned copy or an immutable reference. +// Avoids unneccesary copies of the environment. +class JoinedStateBuilder { + AnalysisContext ∾ + std::optional OwnedState; + const TypeErasedDataflowAnalysisState *CurrentState = nullptr; + +public: + JoinedStateBuilder(AnalysisContext &AC) : AC(AC) {} + + void addOwned(TypeErasedDataflowAnalysisState State) { + if (!CurrentState) { + OwnedState = std::move(State); + CurrentState = &*OwnedState; + } else if (!OwnedState) { + OwnedState.emplace(std::move(CurrentState->Lattice), + CurrentState->Env.join(State.Env, AC.Analysis)); + AC.Analysis.joinTypeErased(OwnedState->Lattice, State.Lattice); + } else { + OwnedState->Env = CurrentState->Env.join(State.Env, AC.Analysis); + AC.Analysis.joinTypeErased(OwnedState->Lattice, State.Lattice); + } + } + void addUnowned(const TypeErasedDataflowAnalysisState &State) { + if (!CurrentState) { + CurrentState = &State; + } else if (!OwnedState) { + OwnedState.emplace(CurrentState->Lattice, + CurrentState->Env.join(State.Env, AC.Analysis)); + AC.Analysis.joinTypeErased(OwnedState->Lattice, State.Lattice); + } else { + OwnedState->Env = CurrentState->Env.join(State.Env, AC.Analysis); + AC.Analysis.joinTypeErased(OwnedState->Lattice, State.Lattice); + } + } + TypeErasedDataflowAnalysisState take() && { + if (!OwnedState) { + if (CurrentState) + OwnedState.emplace(CurrentState->Lattice, CurrentState->Env.fork()); + else + // FIXME: Consider passing `Block` to Analysis.typeErasedInitialElement + // to enable building analyses like computation of dominators that + // initialize the state of each basic block differently. + OwnedState.emplace(AC.Analysis.typeErasedInitialElement(), + AC.InitEnv.fork()); + } + return std::move(*OwnedState); + } +}; + } // namespace /// Computes the input state for a given basic block by joining the output @@ -267,9 +318,7 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) { } } - std::optional MaybeState; - - auto &Analysis = AC.Analysis; + JoinedStateBuilder Builder(AC); for (const CFGBlock *Pred : Preds) { // Skip if the `Block` is unreachable or control flow cannot get past it. if (!Pred || Pred->hasNoReturnElement()) @@ -282,36 +331,30 @@ computeBlockInputState(const CFGBlock &Block, AnalysisContext &AC) { if (!MaybePredState) continue; - TypeErasedDataflowAnalysisState PredState = MaybePredState->fork(); - if (Analysis.builtinOptions()) { + if (AC.Analysis.builtinOptions()) { if (const Stmt *PredTerminatorStmt = Pred->getTerminatorStmt()) { + // We have a terminator: we need to mutate an environment to describe + // when the terminator is taken. Copy now. + TypeErasedDataflowAnalysisState Copy = MaybePredState->fork(); + const StmtToEnvMap StmtToEnv(AC.CFCtx, AC.BlockStates); auto [Cond, CondValue] = - TerminatorVisitor(StmtToEnv, PredState.Env, + TerminatorVisitor(StmtToEnv, Copy.Env, blockIndexInPredecessor(*Pred, Block)) .Visit(PredTerminatorStmt); if (Cond != nullptr) // FIXME: Call transferBranchTypeErased even if BuiltinTransferOpts // are not set. - Analysis.transferBranchTypeErased(CondValue, Cond, PredState.Lattice, - PredState.Env); + AC.Analysis.transferBranchTypeErased(CondValue, Cond, Copy.Lattice, + Copy.Env); + Builder.addOwned(std::move(Copy)); + continue; } } - - if (MaybeState) { - Analysis.joinTypeErased(MaybeState->Lattice, PredState.Lattice); - MaybeState->Env.join(PredState.Env, Analysis); - } else { - MaybeState = std::move(PredState); - } - } - if (!MaybeState) { - // FIXME: Consider passing `Block` to `Analysis.typeErasedInitialElement()` - // to enable building analyses like computation of dominators that - // initialize the state of each basic block differently. - MaybeState.emplace(Analysis.typeErasedInitialElement(), AC.InitEnv.fork()); + Builder.addUnowned(*MaybePredState); + continue; } - return std::move(*MaybeState); + return std::move(Builder).take(); } /// Built-in transfer function for `CFGStmt`.