diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-08-02 17:33:11 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-08-02 17:33:11 +0000 |
commit | c7e70c433efc6953dc3888b9fbf9f3512d7da2b0 (patch) | |
tree | 27425930fc0c91650a7f3527fcac8e0f92907b90 /lib/Analysis/CFG.cpp | |
parent | 486754660bb926339aefcf012a3f848592babb8b (diff) |
Notes
Diffstat (limited to 'lib/Analysis/CFG.cpp')
-rw-r--r-- | lib/Analysis/CFG.cpp | 212 |
1 files changed, 135 insertions, 77 deletions
diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 8a3ab15458dd7..97829de7ace33 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -145,7 +145,7 @@ static bool areExprTypesCompatible(const Expr *E1, const Expr *E2) { namespace { class CFGBuilder; - + /// The CFG builder uses a recursive algorithm to build the CFG. When /// we process an expression, sometimes we know that we must add the /// subexpressions as block-level expressions. For example: @@ -279,7 +279,7 @@ public: private: BumpVectorContext ctx; - + /// Automatic variables in order of declaration. AutomaticVarsTy Vars; @@ -366,7 +366,7 @@ class TryResult { public: TryResult() = default; TryResult(bool b) : X(b ? 1 : 0) {} - + bool isTrue() const { return X == 1; } bool isFalse() const { return X == 0; } bool isKnown() const { return X >= 0; } @@ -492,11 +492,11 @@ class CFGBuilder { bool badCFG = false; const CFG::BuildOptions &BuildOpts; - + // State to track for building switch statements. bool switchExclusivelyCovered = false; Expr::EvalResult *switchCond = nullptr; - + CFG::BuildOptions::ForcedBlkExprs::value_type *cachedEntry = nullptr; const Stmt *lastLookup = nullptr; @@ -516,7 +516,7 @@ public: std::unique_ptr<CFG> buildCFG(const Decl *D, Stmt *Statement); bool alwaysAdd(const Stmt *stmt); - + private: // Visitors to walk an AST and construct the CFG. CFGBlock *VisitAddrLabelExpr(AddrLabelExpr *A, AddStmtChoice asc); @@ -569,6 +569,7 @@ private: CFGBlock *VisitObjCAtTryStmt(ObjCAtTryStmt *S); CFGBlock *VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); CFGBlock *VisitObjCForCollectionStmt(ObjCForCollectionStmt *S); + CFGBlock *VisitObjCMessageExpr(ObjCMessageExpr *E, AddStmtChoice asc); CFGBlock *VisitPseudoObjectExpr(PseudoObjectExpr *E); CFGBlock *VisitReturnStmt(ReturnStmt *R); CFGBlock *VisitSEHExceptStmt(SEHExceptStmt *S); @@ -683,6 +684,25 @@ private: void findConstructionContexts(const ConstructionContextLayer *Layer, Stmt *Child); + // Scan all arguments of a call expression for a construction context. + // These sorts of call expressions don't have a common superclass, + // hence strict duck-typing. + template <typename CallLikeExpr, + typename = typename std::enable_if< + std::is_same<CallLikeExpr, CallExpr>::value || + std::is_same<CallLikeExpr, CXXConstructExpr>::value || + std::is_same<CallLikeExpr, ObjCMessageExpr>::value>> + void findConstructionContextsForArguments(CallLikeExpr *E) { + for (unsigned i = 0, e = E->getNumArgs(); i != e; ++i) { + Expr *Arg = E->getArg(i); + if (Arg->getType()->getAsCXXRecordDecl() && !Arg->isGLValue()) + findConstructionContexts( + ConstructionContextLayer::create(cfg->getBumpVectorContext(), + ConstructionContextItem(E, i)), + Arg); + } + } + // Unset the construction context after consuming it. This is done immediately // after adding the CFGConstructor or CFGCXXRecordTypedCall element, so // there's no need to do this manually in every Visit... function. @@ -721,6 +741,19 @@ private: void addLocalScopeAndDtors(Stmt *S); + const ConstructionContext *retrieveAndCleanupConstructionContext(Expr *E) { + if (!BuildOpts.AddRichCXXConstructors) + return nullptr; + + const ConstructionContextLayer *Layer = ConstructionContextMap.lookup(E); + if (!Layer) + return nullptr; + + cleanupConstructionContext(E); + return ConstructionContext::createFromLayers(cfg->getBumpVectorContext(), + Layer); + } + // Interface to CFGBlock - adding CFGElements. void appendStmt(CFGBlock *B, const Stmt *S) { @@ -733,16 +766,10 @@ private: } void appendConstructor(CFGBlock *B, CXXConstructExpr *CE) { - if (BuildOpts.AddRichCXXConstructors) { - if (const ConstructionContextLayer *Layer = - ConstructionContextMap.lookup(CE)) { - cleanupConstructionContext(CE); - if (const auto *CC = ConstructionContext::createFromLayers( - cfg->getBumpVectorContext(), Layer)) { - B->appendConstructor(CE, CC, cfg->getBumpVectorContext()); - return; - } - } + if (const ConstructionContext *CC = + retrieveAndCleanupConstructionContext(CE)) { + B->appendConstructor(CE, CC, cfg->getBumpVectorContext()); + return; } // No valid construction context found. Fall back to statement. @@ -753,18 +780,10 @@ private: if (alwaysAdd(CE) && cachedEntry) cachedEntry->second = B; - if (BuildOpts.AddRichCXXConstructors) { - if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE, *Context)) { - if (const ConstructionContextLayer *Layer = - ConstructionContextMap.lookup(CE)) { - cleanupConstructionContext(CE); - if (const auto *CC = ConstructionContext::createFromLayers( - cfg->getBumpVectorContext(), Layer)) { - B->appendCXXRecordTypedCall(CE, CC, cfg->getBumpVectorContext()); - return; - } - } - } + if (const ConstructionContext *CC = + retrieveAndCleanupConstructionContext(CE)) { + B->appendCXXRecordTypedCall(CE, CC, cfg->getBumpVectorContext()); + return; } // No valid construction context found. Fall back to statement. @@ -787,6 +806,20 @@ private: B->appendMemberDtor(FD, cfg->getBumpVectorContext()); } + void appendObjCMessage(CFGBlock *B, ObjCMessageExpr *ME) { + if (alwaysAdd(ME) && cachedEntry) + cachedEntry->second = B; + + if (const ConstructionContext *CC = + retrieveAndCleanupConstructionContext(ME)) { + B->appendCXXRecordTypedCall(ME, CC, cfg->getBumpVectorContext()); + return; + } + + B->appendStmt(const_cast<ObjCMessageExpr *>(ME), + cfg->getBumpVectorContext()); + } + void appendTemporaryDtor(CFGBlock *B, CXXBindTemporaryExpr *E) { B->appendTemporaryDtor(E, cfg->getBumpVectorContext()); } @@ -1067,7 +1100,7 @@ private: bool tryEvaluate(Expr *S, Expr::EvalResult &outResult) { if (!BuildOpts.PruneTriviallyFalseEdges) return false; - return !S->isTypeDependent() && + return !S->isTypeDependent() && !S->isValueDependent() && S->EvaluateAsRValue(outResult, *Context); } @@ -1183,18 +1216,18 @@ inline bool AddStmtChoice::alwaysAdd(CFGBuilder &builder, bool CFGBuilder::alwaysAdd(const Stmt *stmt) { bool shouldAdd = BuildOpts.alwaysAdd(stmt); - + if (!BuildOpts.forcedBlkExprs) return shouldAdd; - if (lastLookup == stmt) { + if (lastLookup == stmt) { if (cachedEntry) { assert(cachedEntry->first == stmt); return true; } return shouldAdd; } - + lastLookup = stmt; // Perform the lookup! @@ -1215,7 +1248,7 @@ bool CFGBuilder::alwaysAdd(const Stmt *stmt) { cachedEntry = &*itr; return true; } - + // FIXME: Add support for dependent-sized array types in C++? // Does it even make sense to build a CFG for an uninstantiated template? static const VariableArrayType *FindVA(const Type *t) { @@ -1232,6 +1265,8 @@ static const VariableArrayType *FindVA(const Type *t) { void CFGBuilder::consumeConstructionContext( const ConstructionContextLayer *Layer, Expr *E) { + assert((isa<CXXConstructExpr>(E) || isa<CallExpr>(E) || + isa<ObjCMessageExpr>(E)) && "Expression cannot construct an object!"); if (const ConstructionContextLayer *PreviouslyStoredLayer = ConstructionContextMap.lookup(E)) { (void)PreviouslyStoredLayer; @@ -1252,8 +1287,8 @@ void CFGBuilder::findConstructionContexts( if (!Child) return; - auto withExtraLayer = [this, Layer](Stmt *S) { - return ConstructionContextLayer::create(cfg->getBumpVectorContext(), S, + auto withExtraLayer = [this, Layer](const ConstructionContextItem &Item) { + return ConstructionContextLayer::create(cfg->getBumpVectorContext(), Item, Layer); }; @@ -1275,10 +1310,11 @@ void CFGBuilder::findConstructionContexts( case Stmt::CallExprClass: case Stmt::CXXMemberCallExprClass: case Stmt::CXXOperatorCallExprClass: - case Stmt::UserDefinedLiteralClass: { - auto *CE = cast<CallExpr>(Child); - if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE, *Context)) - consumeConstructionContext(Layer, CE); + case Stmt::UserDefinedLiteralClass: + case Stmt::ObjCMessageExprClass: { + auto *E = cast<Expr>(Child); + if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(E)) + consumeConstructionContext(Layer, E); break; } case Stmt::ExprWithCleanupsClass: { @@ -1313,18 +1349,17 @@ void CFGBuilder::findConstructionContexts( // it indicates the beginning of a temporary object construction context, // so it shouldn't be found in the middle. However, if it is the beginning // of an elidable copy or move construction context, we need to include it. - if (const auto *CE = - dyn_cast_or_null<CXXConstructExpr>(Layer->getTriggerStmt())) { - if (CE->isElidable()) { - auto *MTE = cast<MaterializeTemporaryExpr>(Child); - findConstructionContexts(withExtraLayer(MTE), MTE->GetTemporaryExpr()); - } + if (Layer->getItem().getKind() == + ConstructionContextItem::ElidableConstructorKind) { + auto *MTE = cast<MaterializeTemporaryExpr>(Child); + findConstructionContexts(withExtraLayer(MTE), MTE->GetTemporaryExpr()); } break; } case Stmt::ConditionalOperatorClass: { auto *CO = cast<ConditionalOperator>(Child); - if (!dyn_cast_or_null<MaterializeTemporaryExpr>(Layer->getTriggerStmt())) { + if (Layer->getItem().getKind() != + ConstructionContextItem::MaterializationKind) { // If the object returned by the conditional operator is not going to be a // temporary object that needs to be immediately materialized, then // it must be C++17 with its mandatory copy elision. Do not yet promise @@ -1427,7 +1462,7 @@ std::unique_ptr<CFG> CFGBuilder::buildCFG(const Decl *D, Stmt *Statement) { // If there is no target block that contains label, then we are looking // at an incomplete AST. Handle this by not registering a successor. if (LI == LabelMap.end()) continue; - + addSuccessor(B, LI->second.block); } @@ -1513,7 +1548,7 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) { return Block; } -/// Retrieve the type of the temporary object whose lifetime was +/// Retrieve the type of the temporary object whose lifetime was /// extended by a local reference with the given initializer. static QualType getReferenceInitTemporaryType(const Expr *Init, bool *FoundMTE = nullptr) { @@ -1766,7 +1801,7 @@ LocalScope* CFGBuilder::createOrReuseLocalScope(LocalScope* Scope) { } /// addLocalScopeForStmt - Add LocalScope to local scopes tree for statement -/// that should create implicit scope (e.g. if/else substatements). +/// that should create implicit scope (e.g. if/else substatements). void CFGBuilder::addLocalScopeForStmt(Stmt *S) { if (!BuildOpts.AddImplicitDtors && !BuildOpts.AddLifetime && !BuildOpts.AddScopes) @@ -2101,6 +2136,9 @@ CFGBlock *CFGBuilder::Visit(Stmt * S, AddStmtChoice asc) { case Stmt::ObjCForCollectionStmtClass: return VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S)); + case Stmt::ObjCMessageExprClass: + return VisitObjCMessageExpr(cast<ObjCMessageExpr>(S), asc); + case Stmt::OpaqueValueExprClass: return Block; @@ -2383,12 +2421,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) { if (!boundType.isNull()) calleeType = boundType; } - // FIXME: Once actually implemented, this construction context layer should - // include the number of the argument as well. - for (auto Arg: C->arguments()) { - findConstructionContexts( - ConstructionContextLayer::create(cfg->getBumpVectorContext(), C), Arg); - } + findConstructionContextsForArguments(C); // If this is a call to a no-return function, this stops the block here. bool NoReturn = getFunctionExtInfo(*calleeType).getNoReturn(); @@ -2574,7 +2607,7 @@ CFGBlock *CFGBuilder::VisitConditionalOperator(AbstractConditionalOperator *C, // At least one of this or the above will be run. return addStmt(BCO->getCommon()); } - + return addStmt(condExpr); } @@ -2583,7 +2616,7 @@ CFGBlock *CFGBuilder::VisitDeclStmt(DeclStmt *DS) { // CFG entirely. if (isa<LabelDecl>(*DS->decl_begin())) return Block; - + // This case also handles static_asserts. if (DS->isSingleDecl()) return VisitDeclSubExpr(DS); @@ -3032,7 +3065,7 @@ CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) { } return LastBlock; } - + CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) { // Goto is a control-flow statement. Thus we stop processing the current // block and create a new one. @@ -3146,7 +3179,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { else if (badCFG) return nullptr; } - + // Because of short-circuit evaluation, the condition of the loop can span // multiple basic blocks. Thus we need the "Entry" and "Exit" blocks that // evaluate the condition. @@ -3188,8 +3221,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { const DeclStmt *DS = F->getConditionVariableDeclStmt(); assert(DS->isSingleDecl()); findConstructionContexts( - ConstructionContextLayer::create(cfg->getBumpVectorContext(), - const_cast<DeclStmt *>(DS)), + ConstructionContextLayer::create(cfg->getBumpVectorContext(), DS), Init); appendStmt(Block, DS); EntryConditionBlock = addStmt(Init); @@ -3214,7 +3246,7 @@ CFGBlock *CFGBuilder::VisitForStmt(ForStmt *F) { // Link up the loop-back block to the entry condition block. addSuccessor(TransitionBlock, EntryConditionBlock); - + // The condition block is the implicit successor for any code above the loop. Succ = EntryConditionBlock; @@ -3334,7 +3366,7 @@ CFGBlock *CFGBuilder::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { CFGBlock *LoopBackBlock = nullptr; Succ = LoopBackBlock = createBlock(); LoopBackBlock->setLoopTarget(S); - + BreakJumpTarget = JumpTarget(LoopSuccessor, ScopePos); ContinueJumpTarget = JumpTarget(Succ, ScopePos); @@ -3401,7 +3433,7 @@ CFGBlock *CFGBuilder::VisitPseudoObjectExpr(PseudoObjectExpr *E) { // Add the PseudoObject as the last thing. appendStmt(Block, E); - CFGBlock *lastBlock = Block; + CFGBlock *lastBlock = Block; // Before that, evaluate all of the semantics in order. In // CFG-land, that means appending them in reverse order. @@ -3580,6 +3612,16 @@ CFGBlock *CFGBuilder::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { return VisitStmt(S, AddStmtChoice::AlwaysAdd); } +CFGBlock *CFGBuilder::VisitObjCMessageExpr(ObjCMessageExpr *ME, + AddStmtChoice asc) { + findConstructionContextsForArguments(ME); + + autoCreateBlock(); + appendObjCMessage(Block, ME); + + return VisitChildren(ME); +} + CFGBlock *CFGBuilder::VisitCXXThrowExpr(CXXThrowExpr *T) { // If we were in the middle of a block we stop processing that block. if (badCFG) @@ -3734,7 +3776,7 @@ CFGBlock *CFGBuilder::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E, // VLA types have expressions that must be evaluated. CFGBlock *lastBlock = Block; - + if (E->isArgumentType()) { for (const VariableArrayType *VA =FindVA(E->getArgumentType().getTypePtr()); VA != nullptr; VA = FindVA(VA->getElementType().getTypePtr())) @@ -3863,7 +3905,7 @@ CFGBlock *CFGBuilder::VisitSwitchStmt(SwitchStmt *Terminator) { return LastBlock; } - + static bool shouldAddCase(bool &switchExclusivelyCovered, const Expr::EvalResult *switchCond, const CaseStmt *CS, @@ -3878,7 +3920,7 @@ static bool shouldAddCase(bool &switchExclusivelyCovered, // Evaluate the LHS of the case value. const llvm::APSInt &lhsInt = CS->getLHS()->EvaluateKnownConstInt(Ctx); const llvm::APSInt &condInt = switchCond->Val.getInt(); - + if (condInt == lhsInt) { addCase = true; switchExclusivelyCovered = true; @@ -3897,7 +3939,7 @@ static bool shouldAddCase(bool &switchExclusivelyCovered, else addCase = true; } - return addCase; + return addCase; } CFGBlock *CFGBuilder::VisitCaseStmt(CaseStmt *CS) { @@ -4069,7 +4111,7 @@ CFGBlock *CFGBuilder::VisitCXXCatchStmt(CXXCatchStmt *CS) { CFGBlock *CatchBlock = Block; if (!CatchBlock) CatchBlock = createBlock(); - + // CXXCatchStmt is more than just a label. They have semantic meaning // as well, as they implicitly "initialize" the catch variable. Add // it to the CFG as a CFGElement so that the control-flow of these @@ -4244,6 +4286,11 @@ CFGBlock *CFGBuilder::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E, CFGBlock *CFGBuilder::VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc) { + // If the constructor takes objects as arguments by value, we need to properly + // construct these objects. Construction contexts we find here aren't for the + // constructor C, they're for its arguments only. + findConstructionContextsForArguments(C); + autoCreateBlock(); appendConstructor(Block, C); @@ -4624,7 +4671,7 @@ CFGImplicitDtor::getDestructorDecl(ASTContext &astContext) const { const RecordType *recordType = ty->getAs<RecordType>(); const CXXRecordDecl *classDecl = cast<CXXRecordDecl>(recordType->getDecl()); - return classDecl->getDestructor(); + return classDecl->getDestructor(); } case CFGElement::DeleteDtor: { const CXXDeleteExpr *DE = castAs<CFGDeleteDtor>().getDeleteExpr(); @@ -4722,7 +4769,7 @@ public: for (CFG::const_iterator I = cfg->begin(), E = cfg->end(); I != E; ++I ) { unsigned j = 1; for (CFGBlock::const_iterator BI = (*I)->begin(), BEnd = (*I)->end() ; - BI != BEnd; ++BI, ++j ) { + BI != BEnd; ++BI, ++j ) { if (Optional<CFGStmt> SE = BI->getAs<CFGStmt>()) { const Stmt *stmt= SE->getStmt(); std::pair<unsigned, unsigned> P((*I)->getBlockID(), j); @@ -4961,7 +5008,7 @@ static void print_construction_context(raw_ostream &OS, OS << ", "; const auto *SICC = cast<SimpleConstructorInitializerConstructionContext>(CC); print_initializer(OS, Helper, SICC->getCXXCtorInitializer()); - break; + return; } case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: { OS << ", "; @@ -5012,6 +5059,17 @@ static void print_construction_context(raw_ostream &OS, Stmts.push_back(TOCC->getConstructorAfterElision()); break; } + case ConstructionContext::ArgumentKind: { + const auto *ACC = cast<ArgumentConstructionContext>(CC); + if (const Stmt *BTE = ACC->getCXXBindTemporaryExpr()) { + OS << ", "; + Helper.handledStmt(const_cast<Stmt *>(BTE), OS); + } + OS << ", "; + Helper.handledStmt(const_cast<Expr *>(ACC->getCallLikeExpr()), OS); + OS << "+" << ACC->getIndex(); + return; + } } for (auto I: Stmts) if (I) { @@ -5151,7 +5209,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg, // Print the header. if (ShowColors) OS.changeColor(raw_ostream::YELLOW, true); - + OS << "\n [B" << B.getBlockID(); if (&B == &cfg->getEntry()) @@ -5164,7 +5222,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg, OS << " (NORETURN)]\n"; else OS << "]\n"; - + if (ShowColors) OS.resetColor(); @@ -5235,7 +5293,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg, CFGBlockTerminatorPrint TPrinter(OS, &Helper, PP); TPrinter.print(B.getTerminator()); OS << '\n'; - + if (ShowColors) OS.resetColor(); } @@ -5254,7 +5312,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg, if (ShowColors) OS.changeColor(Color); - + for (CFGBlock::const_pred_iterator I = B.pred_begin(), E = B.pred_end(); I != E; ++I, ++i) { if (i % 10 == 8) @@ -5271,7 +5329,7 @@ static void print_block(raw_ostream &OS, const CFG* cfg, if (!Reachable) OS << "(Unreachable)"; } - + if (ShowColors) OS.resetColor(); |