clang: lib/Analysis/UninitializedValues.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

29#include "llvm/ADT/BitVector.h"

30#include "llvm/ADT/DenseMap.h"

31#include "llvm/ADT/PackedVector.h"

32#include "llvm/ADT/SmallBitVector.h"

33#include "llvm/ADT/SmallVector.h"

34#include "llvm/Support/Casting.h"

35#include

36#include

37#include

38

39using namespace clang;

40

41#define DEBUG_LOGGING 0

42

44

45

46 for (const auto *FD : RD->fields()) {

47 if (FD->isUnnamedBitField())

48 continue;

49 if (FD->isZeroSize(FD->getASTContext()))

50 continue;

51

52

53 if (const auto *FieldRD = FD->getType()->getAsRecordDecl();

55 return true;

56 }

57 return false;

58}

59

68 }

69 return false;

70}

71

72

73

74

75

76namespace {

77

78class DeclToIndex {

79 llvm::DenseMap<const VarDecl *, unsigned> map;

80

81public:

82 DeclToIndex() = default;

83

84

86

87

88 unsigned size() const { return map.size(); }

89

90

91 std::optional getValueIndex(const VarDecl *d) const;

92};

93

94}

95

96void DeclToIndex::computeMap(const DeclContext &dc) {

97 unsigned count = 0;

100 for ( ; I != E; ++I) {

103 map[vd] = count++;

104 }

105}

106

107std::optional DeclToIndex::getValueIndex(const VarDecl *d) const {

108 llvm::DenseMap<const VarDecl *, unsigned>::const_iterator I = map.find(d);

109 if (I == map.end())

110 return std::nullopt;

111 return I->second;

112}

113

114

115

116

117

118

119

124

127}

128

131}

132

133namespace {

134

135using ValueVector = llvm::PackedVector<Value, 2, llvm::SmallBitVector>;

136

137class CFGBlockValues {

138 const CFG &cfg;

140 ValueVector scratch;

141 DeclToIndex declToIndex;

142

143public:

144 CFGBlockValues(const CFG &cfg);

145

146 unsigned getNumEntries() const { return declToIndex.size(); }

147

148 void computeSetOfDeclarations(const DeclContext &dc);

149

150 ValueVector &getValueVector(const CFGBlock *block) {

152 }

153

154 void setAllScratchValues(Value V);

155 void mergeIntoScratch(ValueVector const &source, bool isFirst);

156 bool updateValueVectorWithScratch(const CFGBlock *block);

157

158 bool hasNoDeclarations() const {

159 return declToIndex.size() == 0;

160 }

161

162 void resetScratch();

163

164 ValueVector::reference operator[](const VarDecl *vd);

165

168 std::optional idx = declToIndex.getValueIndex(vd);

169 return getValueVector(block)[*idx];

170 }

171};

172

173}

174

175CFGBlockValues::CFGBlockValues(const CFG &c) : cfg(c), vals(0) {}

176

177void CFGBlockValues::computeSetOfDeclarations(const DeclContext &dc) {

178 declToIndex.computeMap(dc);

179 unsigned decls = declToIndex.size();

180 scratch.resize(decls);

182 if (!n)

183 return;

184 vals.resize(n);

185 for (auto &val : vals)

186 val.resize(decls);

187}

188

189#if DEBUG_LOGGING

190static void printVector(const CFGBlock *block, ValueVector &bv,

191 unsigned num) {

192 llvm::errs() << block->getBlockID() << " :";

193 for (const auto &i : bv)

194 llvm::errs() << ' ' << i;

195 llvm::errs() << " : " << num << '\n';

196}

197#endif

198

199void CFGBlockValues::setAllScratchValues(Value V) {

200 for (unsigned I = 0, E = scratch.size(); I != E; ++I)

201 scratch[I] = V;

202}

203

204void CFGBlockValues::mergeIntoScratch(ValueVector const &source,

205 bool isFirst) {

206 if (isFirst)

207 scratch = source;

208 else

209 scratch |= source;

210}

211

212bool CFGBlockValues::updateValueVectorWithScratch(const CFGBlock *block) {

213 ValueVector &dst = getValueVector(block);

214 bool changed = (dst != scratch);

215 if (changed)

216 dst = scratch;

217#if DEBUG_LOGGING

218 printVector(block, scratch, 0);

219#endif

220 return changed;

221}

222

223void CFGBlockValues::resetScratch() {

224 scratch.reset();

225}

226

227ValueVector::reference CFGBlockValues::operator[](const VarDecl *vd) {

228 return scratch[*declToIndex.getValueIndex(vd)];

229}

230

231

232

233

234

235namespace {

236

237class FindVarResult {

240

241public:

242 FindVarResult(const VarDecl *vd, const DeclRefExpr *dr) : vd(vd), dr(dr) {}

243

244 const DeclRefExpr *getDeclRefExpr() const { return dr; }

245 const VarDecl *getDecl() const { return vd; }

246};

247

248}

249

251 while (Ex) {

253 if (const auto *CE = dyn_cast(Ex)) {

254 if (CE->getCastKind() == CK_LValueBitCast) {

255 Ex = CE->getSubExpr();

256 continue;

257 }

258 }

259 break;

260 }

261 return Ex;

262}

263

264

265

267 if (const auto *DRE =

269 if (const auto *VD = dyn_cast(DRE->getDecl()))

271 return FindVarResult(VD, DRE);

272 return FindVarResult(nullptr, nullptr);

273}

274

275namespace {

276

277

278

279

280class ClassifyRefs : public StmtVisitor {

281public:

284 Use,

285 SelfInit,

286 ConstRefUse,

287 Ignore

288 };

289

290private:

292 llvm::DenseMap<const DeclRefExpr *, Class> Classification;

293

295 return ::isTrackedVar(VD, DC);

296 }

297

298 void classify(const Expr *E, Class C);

299

300public:

302

303 void VisitDeclStmt(DeclStmt *DS);

306 void VisitCallExpr(CallExpr *CE);

307 void VisitCastExpr(CastExpr *CE);

309

310 void operator()(Stmt *S) { Visit(S); }

311

313 llvm::DenseMap<const DeclRefExpr*, Class>::const_iterator I

314 = Classification.find(DRE);

315 if (I != Classification.end())

316 return I->second;

317

318 const auto *VD = dyn_cast(DRE->getDecl());

320 return Ignore;

321

323 }

324};

325

326}

327

330 return nullptr;

332 const auto *DRE =

334 if (DRE && DRE->getDecl() == VD)

335 return DRE;

336 }

337 return nullptr;

338}

339

340void ClassifyRefs::classify(const Expr *E, Class C) {

341

343 if (const auto *CO = dyn_cast(E)) {

344 classify(CO->getTrueExpr(), C);

345 classify(CO->getFalseExpr(), C);

346 return;

347 }

348

349 if (const auto *BCO = dyn_cast(E)) {

350 classify(BCO->getFalseExpr(), C);

351 return;

352 }

353

354 if (const auto *OVE = dyn_cast(E)) {

355 classify(OVE->getSourceExpr(), C);

356 return;

357 }

358

359 if (const auto *ME = dyn_cast(E)) {

360 if (const auto *VD = dyn_cast(ME->getMemberDecl())) {

361 if (!VD->isStaticDataMember())

362 classify(ME->getBase(), C);

363 }

364 return;

365 }

366

367 if (const auto *BO = dyn_cast(E)) {

368 switch (BO->getOpcode()) {

369 case BO_PtrMemD:

370 case BO_PtrMemI:

371 classify(BO->getLHS(), C);

372 return;

373 case BO_Comma:

374 classify(BO->getRHS(), C);

375 return;

376 default:

377 return;

378 }

379 }

380

381 FindVarResult Var = findVar(E, DC);

382 if (const DeclRefExpr *DRE = Var.getDeclRefExpr())

383 Classification[DRE] = std::max(Classification[DRE], C);

384}

385

386void ClassifyRefs::VisitDeclStmt(DeclStmt *DS) {

387 for (auto *DI : DS->decls()) {

388 auto *VD = dyn_cast(DI);

391 Classification[DRE] = SelfInit;

392 }

393}

394

395void ClassifyRefs::VisitBinaryOperator(BinaryOperator *BO) {

396

397

398

399

400

402 classify(BO->getLHS(), Use);

403 else if (BO->getOpcode() == BO_Assign || BO->getOpcode() == BO_Comma)

404 classify(BO->getLHS(), Ignore);

405}

406

407void ClassifyRefs::VisitUnaryOperator(UnaryOperator *UO) {

408

409

412}

413

416 classify(cast(S), Use);

417}

418

421}

422

426 return FTD->getTemplatedDecl()->hasTrivialBody();

427 return FD->hasTrivialBody();

428 }

429 return false;

430}

431

432void ClassifyRefs::VisitCallExpr(CallExpr *CE) {

433

435

437 classify(CE->getArg(0), Use);

438 return;

439 }

441

442

443

444

445

447 I != E; ++I) {

448 if ((*I)->isGLValue()) {

449 if ((*I)->getType().isConstQualified())

450 classify((*I), isTrivialBody ? Ignore : ConstRefUse);

453 const auto *UO = dyn_cast(Ex);

454 if (UO && UO->getOpcode() == UO_AddrOf)

456 classify(Ex, Ignore);

457 }

458 }

459}

460

461void ClassifyRefs::VisitCastExpr(CastExpr *CE) {

462 if (CE->getCastKind() == CK_LValueToRValue)

464 else if (const auto *CSE = dyn_cast(CE)) {

465 if (CSE->getType()->isVoidType()) {

466

467

468

469 classify(CSE->getSubExpr(), Ignore);

470 }

471 }

472}

473

474

475

476

477

478namespace {

479

480class TransferFunctions : public StmtVisitor {

481 CFGBlockValues &vals;

482 const CFG &cfg;

485 const ClassifyRefs &classification;

488

489public:

490 TransferFunctions(CFGBlockValues &vals, const CFG &cfg,

492 const ClassifyRefs &classification,

494 : vals(vals), cfg(cfg), block(block), ac(ac),

495 classification(classification), objCNoRet(ac.getASTContext()),

496 handler(handler) {}

497

498 void reportUse(const Expr *ex, const VarDecl *vd);

499 void reportConstRefUse(const Expr *ex, const VarDecl *vd);

500

502 void VisitBlockExpr(BlockExpr *be);

503 void VisitCallExpr(CallExpr *ce);

505 void VisitDeclStmt(DeclStmt *ds);

506 void VisitGCCAsmStmt(GCCAsmStmt *as);

510

512 return ::isTrackedVar(vd, cast(ac.getDecl()));

513 }

514

516 return ::findVar(ex, cast(ac.getDecl()));

517 }

518

521

524 return Use;

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

574 Queue.push_back(block);

575

576

577

579 while (!Queue.empty()) {

580 const CFGBlock *B = Queue.pop_back_val();

581

582

584 Use.setUninitAfterCall();

585

587 I != E; ++I) {

589 if (!Pred)

590 continue;

591

592 Value AtPredExit = vals.getValue(Pred, B, vd);

594

595 continue;

597 vals.getValue(B, nullptr, vd) == Uninitialized) {

598

599

600

601

602

603 Use.setUninitAfterDecl();

604 continue;

605 }

606

607 unsigned &SV = SuccsVisited[Pred->getBlockID()];

608 if (!SV) {

609

610

613 SI != SE; ++SI)

614 if (!*SI)

615 ++SV;

616 }

617

619

620

621 Queue.push_back(Pred);

622 }

623 }

624

625

626

627 for (const auto *Block : cfg) {

630 if (SuccsVisited[BlockID] && SuccsVisited[BlockID] < Block->succ_size() &&

631 Term) {

632

633

634

640

641

642

643

644 if (isa(Term)) {

646 if (Label || !isa(Label))

647

648 continue;

651 Branch.Output = 0;

652 Use.addUninitBranch(Branch);

653 } else {

657 Use.addUninitBranch(Branch);

658 }

659 }

660 }

661 }

662 }

663

664 return Use;

665 }

666};

667

668}

669

670void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) {

674}

675

676void TransferFunctions::reportConstRefUse(const Expr *ex, const VarDecl *vd) {

680}

681

683

684 if (const auto *DS = dyn_cast(FS->getElement())) {

685 const auto *VD = cast(DS->getSingleDecl());

688 }

689}

690

691void TransferFunctions::VisitOMPExecutableDirective(

694 assert(S && "Expected non-null used-in-clause child.");

695 Visit(S);

696 }

699}

700

701void TransferFunctions::VisitBlockExpr(BlockExpr *be) {

703 for (const auto &I : bd->captures()) {

704 const VarDecl *vd = I.getVariable();

706 continue;

707 if (I.isByRef()) {

709 continue;

710 }

711 reportUse(be, vd);

712 }

713}

714

715void TransferFunctions::VisitCallExpr(CallExpr *ce) {

717 if (Callee->hasAttr()) {

718

719

720

721

722

724 }

725 else if (Callee->hasAttr()) {

726

727

728

729

730

731

732

733 vals.setAllScratchValues(Unknown);

734 }

735 }

736}

737

738void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {

739 switch (classification.get(dr)) {

740 case ClassifyRefs::Ignore:

741 break;

742 case ClassifyRefs::Use:

743 reportUse(dr, cast(dr->getDecl()));

744 break;

745 case ClassifyRefs::Init:

747 break;

748 case ClassifyRefs::SelfInit:

750 break;

751 case ClassifyRefs::ConstRefUse:

752 reportConstRefUse(dr, cast(dr->getDecl()));

753 break;

754 }

755}

756

757void TransferFunctions::VisitBinaryOperator(BinaryOperator *BO) {

758 if (BO->getOpcode() == BO_Assign) {

760 if (const VarDecl *VD = Var.getDecl())

762 }

763}

764

765void TransferFunctions::VisitDeclStmt(DeclStmt *DS) {

766 for (auto *DI : DS->decls()) {

767 auto *VD = dyn_cast(DI);

770

771

772

773

774

775

776

777

778

779

781 } else if (VD->getInit()) {

782

784 } else {

785

786

787

788

789

790

791

792

793

794

796 }

797 }

798 }

799}

800

801void TransferFunctions::VisitGCCAsmStmt(GCCAsmStmt *as) {

802

804 return;

805

809

810

811

812 while (const auto *UO = dyn_cast(Ex))

814

815

816

817

821 }

822}

823

824void TransferFunctions::VisitObjCMessageExpr(ObjCMessageExpr *ME) {

825

826

828 vals.setAllScratchValues(Unknown);

829 }

830}

831

832

833

834

835

838 const ClassifyRefs &classification,

839 llvm::BitVector &wasAnalyzed,

841 wasAnalyzed[block->getBlockID()] = true;

842 vals.resetScratch();

843

844 bool isFirst = true;

846 E = block->pred_end(); I != E; ++I) {

848 if (!pred)

849 continue;

850 if (wasAnalyzed[pred->getBlockID()]) {

851 vals.mergeIntoScratch(vals.getValueVector(pred), isFirst);

852 isFirst = false;

853 }

854 }

855

856 TransferFunctions tf(vals, cfg, block, ac, classification, handler);

857 for (const auto &I : *block) {

858 if (std::optional cs = I.getAs<CFGStmt>())

859 tf.Visit(const_cast<Stmt *>(cs->getStmt()));

860 }

862 if (auto *as = dyn_cast_or_null(terminator.getStmt()))

864 tf.Visit(as);

865 return vals.updateValueVectorWithScratch(block);

866}

867

868namespace {

869

870

871

872

873

875

876 llvm::BitVector hadUse;

877

878

879 bool hadAnyUse = false;

880

881

882 unsigned currentBlock = 0;

883

884 PruneBlocksHandler(unsigned numBlocks) : hadUse(numBlocks, false) {}

885

886 ~PruneBlocksHandler() override = default;

887

890 hadUse[currentBlock] = true;

891 hadAnyUse = true;

892 }

893

896 hadUse[currentBlock] = true;

897 hadAnyUse = true;

898 }

899

900

901

902

904 hadUse[currentBlock] = true;

905 hadAnyUse = true;

906 }

907};

908

909}

910

913 const CFG &cfg,

917 CFGBlockValues vals(cfg);

918 vals.computeSetOfDeclarations(dc);

919 if (vals.hasNoDeclarations())

920 return;

921

923

924

925 ClassifyRefs classification(ac);

927

928

930 ValueVector &vec = vals.getValueVector(&entry);

931 const unsigned n = vals.getNumEntries();

932 for (unsigned j = 0; j < n; ++j) {

934 }

935

936

938 llvm::BitVector previouslyVisited(cfg.getNumBlockIDs());

940 llvm::BitVector wasAnalyzed(cfg.getNumBlockIDs(), false);

943

945 PBH.currentBlock = block->getBlockID();

946

947

948 bool changed = runOnBlock(block, cfg, ac, vals,

949 classification, wasAnalyzed, PBH);

951 if (changed || !previouslyVisited[block->getBlockID()])

953 previouslyVisited[block->getBlockID()] = true;

954 }

955

956 if (!PBH.hadAnyUse)

957 return;

958

959

960 for (const auto *block : cfg)

961 if (PBH.hadUse[block->getBlockID()]) {

962 runOnBlock(block, cfg, ac, vals, classification, wasAnalyzed, handler);

964 }

965}

966

This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...

Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.

Defines the Objective-C statement AST node classes.

C Language Family Type Representation.

static bool isAlwaysUninit(const Value v)

static bool isTrackedVar(const VarDecl *vd, const DeclContext *dc)

static const Expr * stripCasts(ASTContext &C, const Expr *Ex)

static bool isUninitialized(const Value v)

static bool isPointerToConst(const QualType &QT)

static FindVarResult findVar(const Expr *E, const DeclContext *DC)

If E is an expression comprising a reference to a single variable, find that variable.

static bool hasTrivialBody(CallExpr *CE)

static bool recordIsNotEmpty(const RecordDecl *RD)

static const DeclRefExpr * getSelfInitExpr(VarDecl *VD)

static bool runOnBlock(const CFGBlock *block, const CFG &cfg, AnalysisDeclContext &ac, CFGBlockValues &vals, const ClassifyRefs &classification, llvm::BitVector &wasAnalyzed, UninitVariablesHandler &handler)

__device__ __2f16 float c

Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...

AnalysisDeclContext contains the context data for the function, method or block under analysis.

const Decl * getDecl() const

ASTContext & getASTContext() const

A builtin binary operation expression such as "x + y" or "x <= y".

static bool isCompoundAssignmentOp(Opcode Opc)

Represents a block literal declaration, which is like an unnamed FunctionDecl.

ArrayRef< Capture > captures() const

BlockExpr - Adaptor class for mixing a BlockDecl with expressions.

const BlockDecl * getBlockDecl() const

Represents a single basic block in a source-level CFG.

CFGTerminator getTerminator() const

succ_iterator succ_begin()

Stmt * getTerminatorStmt()

AdjacentBlocks::const_iterator const_pred_iterator

pred_iterator pred_begin()

unsigned getBlockID() const

AdjacentBlocks::const_iterator const_succ_iterator

unsigned succ_size() const

Represents CFGBlock terminator statement.

Represents a source-level, intra-procedural CFG that represents the control-flow of a Stmt.

void VisitBlockStmts(Callback &O) const

unsigned getNumBlockIDs() const

Returns the total number of BlockIDs allocated (which start at 0).

CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).

Expr * getArg(unsigned Arg)

getArg - Return the specified argument.

FunctionDecl * getDirectCallee()

If the callee is a FunctionDecl, return it. Otherwise return null.

bool isCallToStdMove() const

CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...

CastKind getCastKind() const

const CFGBlock * dequeue()

specific_decl_iterator - Iterates over a subrange of declarations stored in a DeclContext,...

DeclContext - This is used only as base class of specific decl types that can act as declaration cont...

ASTContext & getParentASTContext() const

decl_iterator decls_end() const

decl_iterator decls_begin() const

A reference to a declared variable, function, enum, etc.

DeclStmt - Adaptor class for mixing declarations with statements and expressions.

const Decl * getSingleDecl() const

Decl - This represents one declaration (or definition), e.g.

ASTContext & getASTContext() const LLVM_READONLY

bool isImplicit() const

isImplicit - Indicates whether the declaration was implicitly generated by the implementation.

DeclContext * getDeclContext()

This represents one expression.

Expr * IgnoreParenNoopCasts(const ASTContext &Ctx) LLVM_READONLY

Skip past any parentheses and casts which do not change the value (including ptr->int casts of the sa...

Expr * IgnoreParens() LLVM_READONLY

Skip past any parentheses which might surround this expression until reaching a fixed point.

Represents a function declaration or definition.

Declaration of a template function.

This represents a GCC inline-assembly statement extension.

This is a basic class for representing single OpenMP executable directive.

ArrayRef< OMPClause * > clauses() const

const Stmt * getStructuredBlock() const

Returns the AST node representing OpenMP structured-block of this OpenMP executable directive,...

bool isStandaloneDirective() const

Returns whether or not this is a Standalone directive.

static llvm::iterator_range< used_clauses_child_iterator > used_clauses_children(ArrayRef< OMPClause * > Clauses)

Represents Objective-C's collection statement.

An expression that sends a message to the given Objective-C object or class.

bool isImplicitNoReturn(const ObjCMessageExpr *ME)

Return true if the given message expression is known to never return.

A (possibly-)qualified type.

bool isConstQualified() const

Determine whether this type is const-qualified.

Represents a struct/union/class.

field_range fields() const

RetTy Visit(PTR(Stmt) S, ParamTys... P)

StmtVisitor - This class implements a simple visitor for Stmt subclasses.

Stmt - This represents one statement.

bool isScalarType() const

QualType getPointeeType() const

If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.

bool isVectorType() const

bool isRVVSizelessBuiltinType() const

Returns true for RVV scalable vector types.

bool isAnyPointerType() const

bool isRecordType() const

RecordDecl * getAsRecordDecl() const

Retrieves the RecordDecl this type refers to.

UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...

Expr * getSubExpr() const

static bool isIncrementDecrementOp(Opcode Op)

A use of a variable, which might be uninitialized.

@ Always

The use is always uninitialized.

virtual void handleUseOfUninitVariable(const VarDecl *vd, const UninitUse &use)

Called when the uninitialized variable is used at the given expression.

virtual void handleSelfInit(const VarDecl *vd)

Called when the uninitialized variable analysis detects the idiom 'int x = x'.

virtual ~UninitVariablesHandler()

virtual void handleConstRefUseOfUninitVariable(const VarDecl *vd, const UninitUse &use)

Called when the uninitialized variable is used as const refernce argument.

Represents a variable declaration or definition.

bool isInitCapture() const

Whether this variable is the implicit variable for a lambda init-capture.

bool hasGlobalStorage() const

Returns true for all variables that do not have local storage.

bool isExceptionVariable() const

Determine whether this variable is the exception variable in a C++ catch statememt or an Objective-C ...

const Expr * getInit() const

bool isLocalVarDecl() const

Returns true for local variable declarations other than parameters.

BlockID

The various types of blocks that can occur within a API notes file.

The JSON file list parser is used to communicate input to InstallAPI.

void runUninitializedVariablesAnalysis(const DeclContext &dc, const CFG &cfg, AnalysisDeclContext &ac, UninitVariablesHandler &handler, UninitVariablesAnalysisStats &stats)

U cast(CodeGen::Address addr)

@ Class

The "class" keyword introduces the elaborated-type-specifier.

A worklist implementation for forward dataflow analysis.

void enqueueSuccessors(const CFGBlock *Block)

Iterator for iterating over Stmt * arrays that contain only T *.

unsigned NumVariablesAnalyzed