diff options
Diffstat (limited to 'clang/lib/Analysis/UninitializedValues.cpp')
-rw-r--r-- | clang/lib/Analysis/UninitializedValues.cpp | 142 |
1 files changed, 75 insertions, 67 deletions
diff --git a/clang/lib/Analysis/UninitializedValues.cpp b/clang/lib/Analysis/UninitializedValues.cpp index 8a233d4a44f10..67cd39728c350 100644 --- a/clang/lib/Analysis/UninitializedValues.cpp +++ b/clang/lib/Analysis/UninitializedValues.cpp @@ -24,6 +24,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/DomainSpecific/ObjCNoReturn.h" +#include "clang/Analysis/FlowSensitive/DataflowWorklist.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" @@ -213,68 +214,6 @@ ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) { } //------------------------------------------------------------------------====// -// Worklist: worklist for dataflow analysis. -//====------------------------------------------------------------------------// - -namespace { - -class DataflowWorklist { - PostOrderCFGView::iterator PO_I, PO_E; - SmallVector<const CFGBlock *, 20> worklist; - llvm::BitVector enqueuedBlocks; - -public: - DataflowWorklist(const CFG &cfg, PostOrderCFGView &view) - : PO_I(view.begin()), PO_E(view.end()), - enqueuedBlocks(cfg.getNumBlockIDs(), true) { - // Treat the first block as already analyzed. - if (PO_I != PO_E) { - assert(*PO_I == &cfg.getEntry()); - enqueuedBlocks[(*PO_I)->getBlockID()] = false; - ++PO_I; - } - } - - void enqueueSuccessors(const CFGBlock *block); - const CFGBlock *dequeue(); -}; - -} // namespace - -void DataflowWorklist::enqueueSuccessors(const CFGBlock *block) { - for (CFGBlock::const_succ_iterator I = block->succ_begin(), - E = block->succ_end(); I != E; ++I) { - const CFGBlock *Successor = *I; - if (!Successor || enqueuedBlocks[Successor->getBlockID()]) - continue; - worklist.push_back(Successor); - enqueuedBlocks[Successor->getBlockID()] = true; - } -} - -const CFGBlock *DataflowWorklist::dequeue() { - const CFGBlock *B = nullptr; - - // First dequeue from the worklist. This can represent - // updates along backedges that we want propagated as quickly as possible. - if (!worklist.empty()) - B = worklist.pop_back_val(); - - // Next dequeue from the initial reverse post order. This is the - // theoretical ideal in the presence of no back edges. - else if (PO_I != PO_E) { - B = *PO_I; - ++PO_I; - } - else - return nullptr; - - assert(enqueuedBlocks[B->getBlockID()] == true); - enqueuedBlocks[B->getBlockID()] = false; - return B; -} - -//------------------------------------------------------------------------====// // Classification of DeclRefExprs as use or initialization. //====------------------------------------------------------------------------// @@ -329,6 +268,7 @@ public: Init, Use, SelfInit, + ConstRefUse, Ignore }; @@ -465,6 +405,15 @@ static bool isPointerToConst(const QualType &QT) { return QT->isAnyPointerType() && QT->getPointeeType().isConstQualified(); } +static bool hasTrivialBody(CallExpr *CE) { + if (FunctionDecl *FD = CE->getDirectCallee()) { + if (FunctionTemplateDecl *FTD = FD->getPrimaryTemplate()) + return FTD->getTemplatedDecl()->hasTrivialBody(); + return FD->hasTrivialBody(); + } + return false; +} + void ClassifyRefs::VisitCallExpr(CallExpr *CE) { // Classify arguments to std::move as used. if (CE->isCallToStdMove()) { @@ -473,15 +422,17 @@ void ClassifyRefs::VisitCallExpr(CallExpr *CE) { classify(CE->getArg(0), Use); return; } - - // If a value is passed by const pointer or by const reference to a function, + bool isTrivialBody = hasTrivialBody(CE); + // If a value is passed by const pointer to a function, // we should not assume that it is initialized by the call, and we // conservatively do not assume that it is used. + // If a value is passed by const reference to a function, + // it should already be initialized. for (CallExpr::arg_iterator I = CE->arg_begin(), E = CE->arg_end(); I != E; ++I) { if ((*I)->isGLValue()) { if ((*I)->getType().isConstQualified()) - classify((*I), Ignore); + classify((*I), isTrivialBody ? Ignore : ConstRefUse); } else if (isPointerToConst((*I)->getType())) { const Expr *Ex = stripCasts(DC->getParentASTContext(), *I); const auto *UO = dyn_cast<UnaryOperator>(Ex); @@ -530,12 +481,14 @@ public: handler(handler) {} void reportUse(const Expr *ex, const VarDecl *vd); + void reportConstRefUse(const Expr *ex, const VarDecl *vd); void VisitBinaryOperator(BinaryOperator *bo); void VisitBlockExpr(BlockExpr *be); void VisitCallExpr(CallExpr *ce); void VisitDeclRefExpr(DeclRefExpr *dr); void VisitDeclStmt(DeclStmt *ds); + void VisitGCCAsmStmt(GCCAsmStmt *as); void VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS); void VisitObjCMessageExpr(ObjCMessageExpr *ME); void VisitOMPExecutableDirective(OMPExecutableDirective *ED); @@ -636,6 +589,28 @@ public: continue; } + if (AtPredExit == MayUninitialized) { + // If the predecessor's terminator is an "asm goto" that initializes + // the variable, then it won't be counted as "initialized" on the + // non-fallthrough paths. + CFGTerminator term = Pred->getTerminator(); + if (const auto *as = dyn_cast_or_null<GCCAsmStmt>(term.getStmt())) { + const CFGBlock *fallthrough = *Pred->succ_begin(); + if (as->isAsmGoto() && + llvm::any_of(as->outputs(), [&](const Expr *output) { + return vd == findVar(output).getDecl() && + llvm::any_of(as->labels(), + [&](const AddrLabelExpr *label) { + return label->getLabel()->getStmt() == B->Label && + B != fallthrough; + }); + })) { + Use.setUninitAfterDecl(); + continue; + } + } + } + unsigned &SV = SuccsVisited[Pred->getBlockID()]; if (!SV) { // When visiting the first successor of a block, mark all NULL @@ -705,6 +680,12 @@ void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) { handler.handleUseOfUninitVariable(vd, getUninitUse(ex, vd, v)); } +void TransferFunctions::reportConstRefUse(const Expr *ex, const VarDecl *vd) { + Value v = vals[vd]; + if (isAlwaysUninit(v)) + handler.handleConstRefUseOfUninitVariable(vd, getUninitUse(ex, vd, v)); +} + void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) { // This represents an initialization of the 'element' value. if (const auto *DS = dyn_cast<DeclStmt>(FS->getElement())) { @@ -772,7 +753,10 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) { vals[cast<VarDecl>(dr->getDecl())] = Initialized; break; case ClassifyRefs::SelfInit: - handler.handleSelfInit(cast<VarDecl>(dr->getDecl())); + handler.handleSelfInit(cast<VarDecl>(dr->getDecl())); + break; + case ClassifyRefs::ConstRefUse: + reportConstRefUse(dr, cast<VarDecl>(dr->getDecl())); break; } } @@ -821,6 +805,20 @@ void TransferFunctions::VisitDeclStmt(DeclStmt *DS) { } } +void TransferFunctions::VisitGCCAsmStmt(GCCAsmStmt *as) { + // An "asm goto" statement is a terminator that may initialize some variables. + if (!as->isAsmGoto()) + return; + + for (const Expr *o : as->outputs()) + if (const VarDecl *VD = findVar(o).getDecl()) + if (vals[VD] != Initialized) + // If the variable isn't initialized by the time we get here, then we + // mark it as potentially uninitialized for those cases where it's used + // on an indirect path, where it's not guaranteed to be defined. + vals[VD] = MayUninitialized; +} + void TransferFunctions::VisitObjCMessageExpr(ObjCMessageExpr *ME) { // If the Objective-C message expression is an implicit no-return that // is not modeled in the CFG, set the tracked dataflow values to Unknown. @@ -858,6 +856,10 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg, if (Optional<CFGStmt> cs = I.getAs<CFGStmt>()) tf.Visit(const_cast<Stmt *>(cs->getStmt())); } + CFGTerminator terminator = block->getTerminator(); + if (auto *as = dyn_cast_or_null<GCCAsmStmt>(terminator.getStmt())) + if (as->isAsmGoto()) + tf.Visit(as); return vals.updateValueVectorWithScratch(block); } @@ -887,6 +889,12 @@ struct PruneBlocksHandler : public UninitVariablesHandler { hadAnyUse = true; } + void handleConstRefUseOfUninitVariable(const VarDecl *vd, + const UninitUse &use) override { + hadUse[currentBlock] = true; + hadAnyUse = true; + } + /// Called when the uninitialized variable analysis detects the /// idiom 'int x = x'. All other uses of 'x' within the initializer /// are handled by handleUseOfUninitVariable. @@ -924,7 +932,7 @@ void clang::runUninitializedVariablesAnalysis( } // Proceed with the workist. - DataflowWorklist worklist(cfg, *ac.getAnalysis<PostOrderCFGView>()); + ForwardDataflowWorklist worklist(cfg, ac); llvm::BitVector previouslyVisited(cfg.getNumBlockIDs()); worklist.enqueueSuccessors(&cfg.getEntry()); llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false); |