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;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 |