forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			168 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- VarBypassDetector.h - Bypass jumps detector --------------*- C++ -*-=//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "VarBypassDetector.h"
 | |
| 
 | |
| #include "clang/AST/Decl.h"
 | |
| #include "clang/AST/Expr.h"
 | |
| #include "clang/AST/Stmt.h"
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace CodeGen;
 | |
| 
 | |
| /// Clear the object and pre-process for the given statement, usually function
 | |
| /// body statement.
 | |
| void VarBypassDetector::Init(const Stmt *Body) {
 | |
|   FromScopes.clear();
 | |
|   ToScopes.clear();
 | |
|   Bypasses.clear();
 | |
|   Scopes = {{~0U, nullptr}};
 | |
|   unsigned ParentScope = 0;
 | |
|   AlwaysBypassed = !BuildScopeInformation(Body, ParentScope);
 | |
|   if (!AlwaysBypassed)
 | |
|     Detect();
 | |
| }
 | |
| 
 | |
| /// Build scope information for a declaration that is part of a DeclStmt.
 | |
| /// Returns false if we failed to build scope information and can't tell for
 | |
| /// which vars are being bypassed.
 | |
| bool VarBypassDetector::BuildScopeInformation(const Decl *D,
 | |
|                                               unsigned &ParentScope) {
 | |
|   const VarDecl *VD = dyn_cast<VarDecl>(D);
 | |
|   if (VD && VD->hasLocalStorage()) {
 | |
|     Scopes.push_back({ParentScope, VD});
 | |
|     ParentScope = Scopes.size() - 1;
 | |
|   }
 | |
| 
 | |
|   if (const VarDecl *VD = dyn_cast<VarDecl>(D))
 | |
|     if (const Expr *Init = VD->getInit())
 | |
|       return BuildScopeInformation(Init, ParentScope);
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /// Walk through the statements, adding any labels or gotos to
 | |
| /// LabelAndGotoScopes and recursively walking the AST as needed.
 | |
| /// Returns false if we failed to build scope information and can't tell for
 | |
| /// which vars are being bypassed.
 | |
| bool VarBypassDetector::BuildScopeInformation(const Stmt *S,
 | |
|                                               unsigned &origParentScope) {
 | |
|   // If this is a statement, rather than an expression, scopes within it don't
 | |
|   // propagate out into the enclosing scope. Otherwise we have to worry about
 | |
|   // block literals, which have the lifetime of their enclosing statement.
 | |
|   unsigned independentParentScope = origParentScope;
 | |
|   unsigned &ParentScope =
 | |
|       ((isa<Expr>(S) && !isa<StmtExpr>(S)) ? origParentScope
 | |
|                                            : independentParentScope);
 | |
| 
 | |
|   unsigned StmtsToSkip = 0u;
 | |
| 
 | |
|   switch (S->getStmtClass()) {
 | |
|   case Stmt::IndirectGotoStmtClass:
 | |
|     return false;
 | |
| 
 | |
|   case Stmt::SwitchStmtClass:
 | |
|     if (const Stmt *Init = cast<SwitchStmt>(S)->getInit()) {
 | |
|       if (!BuildScopeInformation(Init, ParentScope))
 | |
|         return false;
 | |
|       ++StmtsToSkip;
 | |
|     }
 | |
|     if (const VarDecl *Var = cast<SwitchStmt>(S)->getConditionVariable()) {
 | |
|       if (!BuildScopeInformation(Var, ParentScope))
 | |
|         return false;
 | |
|       ++StmtsToSkip;
 | |
|     }
 | |
|     LLVM_FALLTHROUGH;
 | |
| 
 | |
|   case Stmt::GotoStmtClass:
 | |
|     FromScopes.push_back({S, ParentScope});
 | |
|     break;
 | |
| 
 | |
|   case Stmt::DeclStmtClass: {
 | |
|     const DeclStmt *DS = cast<DeclStmt>(S);
 | |
|     for (auto *I : DS->decls())
 | |
|       if (!BuildScopeInformation(I, origParentScope))
 | |
|         return false;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   case Stmt::CaseStmtClass:
 | |
|   case Stmt::DefaultStmtClass:
 | |
|   case Stmt::LabelStmtClass:
 | |
|     llvm_unreachable("the loop below handles labels and cases");
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   for (const Stmt *SubStmt : S->children()) {
 | |
|     if (!SubStmt)
 | |
|       continue;
 | |
|     if (StmtsToSkip) {
 | |
|       --StmtsToSkip;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // Cases, labels, and defaults aren't "scope parents".  It's also
 | |
|     // important to handle these iteratively instead of recursively in
 | |
|     // order to avoid blowing out the stack.
 | |
|     while (true) {
 | |
|       const Stmt *Next;
 | |
|       if (const SwitchCase *SC = dyn_cast<SwitchCase>(SubStmt))
 | |
|         Next = SC->getSubStmt();
 | |
|       else if (const LabelStmt *LS = dyn_cast<LabelStmt>(SubStmt))
 | |
|         Next = LS->getSubStmt();
 | |
|       else
 | |
|         break;
 | |
| 
 | |
|       ToScopes[SubStmt] = ParentScope;
 | |
|       SubStmt = Next;
 | |
|     }
 | |
| 
 | |
|     // Recursively walk the AST.
 | |
|     if (!BuildScopeInformation(SubStmt, ParentScope))
 | |
|       return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /// Checks each jump and stores each variable declaration they bypass.
 | |
| void VarBypassDetector::Detect() {
 | |
|   for (const auto &S : FromScopes) {
 | |
|     const Stmt *St = S.first;
 | |
|     unsigned from = S.second;
 | |
|     if (const GotoStmt *GS = dyn_cast<GotoStmt>(St)) {
 | |
|       if (const LabelStmt *LS = GS->getLabel()->getStmt())
 | |
|         Detect(from, ToScopes[LS]);
 | |
|     } else if (const SwitchStmt *SS = dyn_cast<SwitchStmt>(St)) {
 | |
|       for (const SwitchCase *SC = SS->getSwitchCaseList(); SC;
 | |
|            SC = SC->getNextSwitchCase()) {
 | |
|         Detect(from, ToScopes[SC]);
 | |
|       }
 | |
|     } else {
 | |
|       llvm_unreachable("goto or switch was expected");
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// Checks the jump and stores each variable declaration it bypasses.
 | |
| void VarBypassDetector::Detect(unsigned From, unsigned To) {
 | |
|   while (From != To) {
 | |
|     if (From < To) {
 | |
|       assert(Scopes[To].first < To);
 | |
|       const auto &ScopeTo = Scopes[To];
 | |
|       To = ScopeTo.first;
 | |
|       Bypasses.insert(ScopeTo.second);
 | |
|     } else {
 | |
|       assert(Scopes[From].first < From);
 | |
|       From = Scopes[From].first;
 | |
|     }
 | |
|   }
 | |
| }
 |