1 //===--- VarBypassDetector.cpp - Bypass jumps detector ------------*- C++ -*-=// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "VarBypassDetector.h" 10 11 #include "CodeGenModule.h" 12 #include "clang/AST/Decl.h" 13 #include "clang/AST/Expr.h" 14 #include "clang/AST/Stmt.h" 15 16 using namespace clang; 17 using namespace CodeGen; 18 19 /// Clear the object and pre-process for the given statement, usually function 20 /// body statement. 21 void VarBypassDetector::Init(CodeGenModule &CGM, const Stmt *Body) { 22 FromScopes.clear(); 23 ToScopes.clear(); 24 Bypasses.clear(); 25 Scopes = {{~0U, nullptr}}; 26 unsigned ParentScope = 0; 27 AlwaysBypassed = !BuildScopeInformation(CGM, Body, ParentScope); 28 if (!AlwaysBypassed) 29 Detect(); 30 } 31 32 /// Build scope information for a declaration that is part of a DeclStmt. 33 /// Returns false if we failed to build scope information and can't tell for 34 /// which vars are being bypassed. 35 bool VarBypassDetector::BuildScopeInformation(CodeGenModule &CGM, const Decl *D, 36 unsigned &ParentScope) { 37 const VarDecl *VD = dyn_cast<VarDecl>(D); 38 if (VD && VD->hasLocalStorage()) { 39 Scopes.push_back({ParentScope, VD}); 40 ParentScope = Scopes.size() - 1; 41 } 42 43 if (const VarDecl *VD = dyn_cast<VarDecl>(D)) 44 if (const Expr *Init = VD->getInit()) 45 return BuildScopeInformation(CGM, Init, ParentScope); 46 47 return true; 48 } 49 50 /// Walk through the statements, adding any labels or gotos to 51 /// LabelAndGotoScopes and recursively walking the AST as needed. 52 /// Returns false if we failed to build scope information and can't tell for 53 /// which vars are being bypassed. 54 bool VarBypassDetector::BuildScopeInformation(CodeGenModule &CGM, const Stmt *S, 55 unsigned &origParentScope) { 56 // If this is a statement, rather than an expression, scopes within it don't 57 // propagate out into the enclosing scope. Otherwise we have to worry about 58 // block literals, which have the lifetime of their enclosing statement. 59 unsigned independentParentScope = origParentScope; 60 unsigned &ParentScope = 61 ((isa<Expr>(S) && !isa<StmtExpr>(S)) ? origParentScope 62 : independentParentScope); 63 64 unsigned StmtsToSkip = 0u; 65 66 switch (S->getStmtClass()) { 67 case Stmt::IndirectGotoStmtClass: 68 return false; 69 70 case Stmt::SwitchStmtClass: 71 if (const Stmt *Init = cast<SwitchStmt>(S)->getInit()) { 72 if (!BuildScopeInformation(CGM, Init, ParentScope)) 73 return false; 74 ++StmtsToSkip; 75 } 76 if (const VarDecl *Var = cast<SwitchStmt>(S)->getConditionVariable()) { 77 if (!BuildScopeInformation(CGM, Var, ParentScope)) 78 return false; 79 ++StmtsToSkip; 80 } 81 [[fallthrough]]; 82 83 case Stmt::GotoStmtClass: 84 FromScopes.push_back({S, ParentScope}); 85 break; 86 87 case Stmt::DeclStmtClass: { 88 const DeclStmt *DS = cast<DeclStmt>(S); 89 for (auto *I : DS->decls()) 90 if (!BuildScopeInformation(CGM, I, origParentScope)) 91 return false; 92 return true; 93 } 94 95 case Stmt::CaseStmtClass: 96 case Stmt::DefaultStmtClass: 97 case Stmt::LabelStmtClass: 98 llvm_unreachable("the loop below handles labels and cases"); 99 break; 100 101 default: 102 break; 103 } 104 105 for (const Stmt *SubStmt : S->children()) { 106 if (!SubStmt) 107 continue; 108 if (StmtsToSkip) { 109 --StmtsToSkip; 110 continue; 111 } 112 113 // Cases, labels, and defaults aren't "scope parents". It's also 114 // important to handle these iteratively instead of recursively in 115 // order to avoid blowing out the stack. 116 while (true) { 117 const Stmt *Next; 118 if (const SwitchCase *SC = dyn_cast<SwitchCase>(SubStmt)) 119 Next = SC->getSubStmt(); 120 else if (const LabelStmt *LS = dyn_cast<LabelStmt>(SubStmt)) 121 Next = LS->getSubStmt(); 122 else 123 break; 124 125 ToScopes[SubStmt] = ParentScope; 126 SubStmt = Next; 127 } 128 129 // Recursively walk the AST. 130 bool Result; 131 CGM.runWithSufficientStackSpace(S->getEndLoc(), [&] { 132 Result = BuildScopeInformation(CGM, SubStmt, ParentScope); 133 }); 134 if (!Result) 135 return false; 136 } 137 return true; 138 } 139 140 /// Checks each jump and stores each variable declaration they bypass. 141 void VarBypassDetector::Detect() { 142 for (const auto &S : FromScopes) { 143 const Stmt *St = S.first; 144 unsigned from = S.second; 145 if (const GotoStmt *GS = dyn_cast<GotoStmt>(St)) { 146 if (const LabelStmt *LS = GS->getLabel()->getStmt()) 147 Detect(from, ToScopes[LS]); 148 } else if (const SwitchStmt *SS = dyn_cast<SwitchStmt>(St)) { 149 for (const SwitchCase *SC = SS->getSwitchCaseList(); SC; 150 SC = SC->getNextSwitchCase()) { 151 Detect(from, ToScopes[SC]); 152 } 153 } else { 154 llvm_unreachable("goto or switch was expected"); 155 } 156 } 157 } 158 159 /// Checks the jump and stores each variable declaration it bypasses. 160 void VarBypassDetector::Detect(unsigned From, unsigned To) { 161 while (From != To) { 162 if (From < To) { 163 assert(Scopes[To].first < To); 164 const auto &ScopeTo = Scopes[To]; 165 To = ScopeTo.first; 166 Bypasses.insert(ScopeTo.second); 167 } else { 168 assert(Scopes[From].first < From); 169 From = Scopes[From].first; 170 } 171 } 172 } 173