#include <algorithm>
#include <memory>
#include <system_error>
#include <utility>
#include <vector>
#include "clang/AST/DeclCXX.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Analysis/Analyses/PostOrderCFGView.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/DataflowWorklist.h"
#include "clang/Analysis/FlowSensitive/Transfer.h"
#include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
namespace clang {
namespace dataflow {
class StmtToEnvMapImpl : public StmtToEnvMap {
public:
StmtToEnvMapImpl(
const ControlFlowContext &CFCtx,
llvm::ArrayRef<llvm::Optional<TypeErasedDataflowAnalysisState>>
BlockToState)
: CFCtx(CFCtx), BlockToState(BlockToState) {}
const Environment *getEnvironment(const Stmt &S) const override {
auto BlockIt = CFCtx.getStmtToBlock().find(&ignoreCFGOmittedNodes(S));
assert(BlockIt != CFCtx.getStmtToBlock().end());
const auto &State = BlockToState[BlockIt->getSecond()->getBlockID()];
assert(State);
return &State.value().Env;
}
private:
const ControlFlowContext &CFCtx;
llvm::ArrayRef<llvm::Optional<TypeErasedDataflowAnalysisState>> BlockToState;
};
static int blockIndexInPredecessor(const CFGBlock &Pred,
const CFGBlock &Block) {
auto BlockPos = llvm::find_if(
Pred.succs(), [&Block](const CFGBlock::AdjacentBlock &Succ) {
return Succ && Succ->getBlockID() == Block.getBlockID();
});
return BlockPos - Pred.succ_begin();
}
class TerminatorVisitor : public ConstStmtVisitor<TerminatorVisitor> {
public:
TerminatorVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env,
int BlockSuccIdx, TransferOptions TransferOpts)
: StmtToEnv(StmtToEnv), Env(Env), BlockSuccIdx(BlockSuccIdx),
TransferOpts(TransferOpts) {}
void VisitIfStmt(const IfStmt *S) {
auto *Cond = S->getCond();
assert(Cond != nullptr);
extendFlowCondition(*Cond);
}
void VisitWhileStmt(const WhileStmt *S) {
auto *Cond = S->getCond();
assert(Cond != nullptr);
extendFlowCondition(*Cond);
}
void VisitDoStmt(const DoStmt *S) {
auto *Cond = S->getCond();
assert(Cond != nullptr);
extendFlowCondition(*Cond);
}
void VisitForStmt(const ForStmt *S) {
auto *Cond = S->getCond();
if (Cond != nullptr)
extendFlowCondition(*Cond);
}
void VisitBinaryOperator(const BinaryOperator *S) {
assert(S->getOpcode() == BO_LAnd || S->getOpcode() == BO_LOr);
auto *LHS = S->getLHS();
assert(LHS != nullptr);
extendFlowCondition(*LHS);
}
void VisitConditionalOperator(const ConditionalOperator *S) {
auto *Cond = S->getCond();
assert(Cond != nullptr);
extendFlowCondition(*Cond);
}
private:
void extendFlowCondition(const Expr &Cond) {
if (Env.getStorageLocation(Cond, SkipPast::None) == nullptr)
transfer(StmtToEnv, Cond, Env, TransferOpts);
auto *Val =
cast_or_null<BoolValue>(Env.getValue(Cond, SkipPast::Reference));
if (Val == nullptr) {
auto *Loc = Env.getStorageLocation(Cond, SkipPast::None);
if (Loc == nullptr) {
Loc = &Env.createStorageLocation(Cond);
Env.setStorageLocation(Cond, *Loc);
}
Val = &Env.makeAtomicBoolValue();
Env.setValue(*Loc, *Val);
}
if (BlockSuccIdx == 1)
Val = &Env.makeNot(*Val);
Env.addToFlowCondition(*Val);
}
const StmtToEnvMap &StmtToEnv;
Environment &Env;
int BlockSuccIdx;
TransferOptions TransferOpts;
};
static TypeErasedDataflowAnalysisState computeBlockInputState(
const ControlFlowContext &CFCtx,
std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>> &BlockStates,
const CFGBlock &Block, const Environment &InitEnv,
TypeErasedDataflowAnalysis &Analysis) {
llvm::DenseSet<const CFGBlock *> Preds;
Preds.insert(Block.pred_begin(), Block.pred_end());
if (Block.getTerminator().isTemporaryDtorsBranch()) {
if (Block.succ_begin()->getReachableBlock()->hasNoReturnElement()) {
auto StmtBlock = CFCtx.getStmtToBlock().find(Block.getTerminatorStmt());
assert(StmtBlock != CFCtx.getStmtToBlock().end());
Preds.erase(StmtBlock->getSecond());
}
}
llvm::Optional<TypeErasedDataflowAnalysisState> MaybeState;
bool ApplyBuiltinTransfer = Analysis.applyBuiltinTransfer();
for (const CFGBlock *Pred : Preds) {
if (!Pred || Pred->hasNoReturnElement())
continue;
const llvm::Optional<TypeErasedDataflowAnalysisState> &MaybePredState =
BlockStates[Pred->getBlockID()];
if (!MaybePredState)
continue;
TypeErasedDataflowAnalysisState PredState = MaybePredState.value();
if (ApplyBuiltinTransfer) {
if (const Stmt *PredTerminatorStmt = Pred->getTerminatorStmt()) {
const StmtToEnvMapImpl StmtToEnv(CFCtx, BlockStates);
TerminatorVisitor(StmtToEnv, PredState.Env,
blockIndexInPredecessor(*Pred, Block),
Analysis.builtinTransferOptions())
.Visit(PredTerminatorStmt);
}
}
if (MaybeState) {
Analysis.joinTypeErased(MaybeState->Lattice, PredState.Lattice);
MaybeState->Env.join(PredState.Env, Analysis);
} else {
MaybeState = std::move(PredState);
}
}
if (!MaybeState) {
MaybeState.emplace(Analysis.typeErasedInitialElement(), InitEnv);
}
return *MaybeState;
}
static void transferCFGStmt(
const ControlFlowContext &CFCtx,
llvm::ArrayRef<llvm::Optional<TypeErasedDataflowAnalysisState>> BlockStates,
const CFGStmt &CfgStmt, TypeErasedDataflowAnalysis &Analysis,
TypeErasedDataflowAnalysisState &State,
std::function<void(const CFGStmt &,
const TypeErasedDataflowAnalysisState &)>
HandleTransferredStmt) {
const Stmt *S = CfgStmt.getStmt();
assert(S != nullptr);
if (Analysis.applyBuiltinTransfer())
transfer(StmtToEnvMapImpl(CFCtx, BlockStates), *S, State.Env,
Analysis.builtinTransferOptions());
Analysis.transferTypeErased(S, State.Lattice, State.Env);
if (HandleTransferredStmt != nullptr)
HandleTransferredStmt(CfgStmt, State);
}
static void transferCFGInitializer(const CFGInitializer &CfgInit,
TypeErasedDataflowAnalysisState &State) {
const auto &ThisLoc = *cast<AggregateStorageLocation>(
State.Env.getThisPointeeStorageLocation());
const CXXCtorInitializer *Initializer = CfgInit.getInitializer();
assert(Initializer != nullptr);
const FieldDecl *Member = Initializer->getMember();
if (Member == nullptr)
return;
auto *InitStmt = Initializer->getInit();
assert(InitStmt != nullptr);
auto *InitStmtLoc =
State.Env.getStorageLocation(*InitStmt, SkipPast::Reference);
if (InitStmtLoc == nullptr)
return;
auto *InitStmtVal = State.Env.getValue(*InitStmtLoc);
if (InitStmtVal == nullptr)
return;
if (Member->getType()->isReferenceType()) {
auto &MemberLoc = ThisLoc.getChild(*Member);
State.Env.setValue(MemberLoc,
State.Env.takeOwnership(
std::make_unique<ReferenceValue>(*InitStmtLoc)));
} else {
auto &MemberLoc = ThisLoc.getChild(*Member);
State.Env.setValue(MemberLoc, *InitStmtVal);
}
}
TypeErasedDataflowAnalysisState transferBlock(
const ControlFlowContext &CFCtx,
std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>> &BlockStates,
const CFGBlock &Block, const Environment &InitEnv,
TypeErasedDataflowAnalysis &Analysis,
std::function<void(const CFGStmt &,
const TypeErasedDataflowAnalysisState &)>
HandleTransferredStmt) {
TypeErasedDataflowAnalysisState State =
computeBlockInputState(CFCtx, BlockStates, Block, InitEnv, Analysis);
for (const CFGElement &Element : Block) {
switch (Element.getKind()) {
case CFGElement::Statement:
transferCFGStmt(CFCtx, BlockStates, *Element.getAs<CFGStmt>(), Analysis,
State, HandleTransferredStmt);
break;
case CFGElement::Initializer:
if (Analysis.applyBuiltinTransfer())
transferCFGInitializer(*Element.getAs<CFGInitializer>(), State);
break;
default:
break;
}
}
return State;
}
llvm::Expected<std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>>>
runTypeErasedDataflowAnalysis(
const ControlFlowContext &CFCtx, TypeErasedDataflowAnalysis &Analysis,
const Environment &InitEnv,
std::function<void(const Stmt *, const TypeErasedDataflowAnalysisState &)>
PostVisitStmt) {
PostOrderCFGView POV(&CFCtx.getCFG());
ForwardDataflowWorklist Worklist(CFCtx.getCFG(), &POV);
std::vector<llvm::Optional<TypeErasedDataflowAnalysisState>> BlockStates;
BlockStates.resize(CFCtx.getCFG().size(), llvm::None);
const CFGBlock &Entry = CFCtx.getCFG().getEntry();
BlockStates[Entry.getBlockID()] = {Analysis.typeErasedInitialElement(),
InitEnv};
Worklist.enqueueSuccessors(&Entry);
static constexpr uint32_t MaxAverageVisitsPerBlock = 4;
static constexpr uint32_t AbsoluteMaxIterations = 1 << 16;
const uint32_t RelativeMaxIterations =
MaxAverageVisitsPerBlock * BlockStates.size();
const uint32_t MaxIterations =
std::min(RelativeMaxIterations, AbsoluteMaxIterations);
uint32_t Iterations = 0;
while (const CFGBlock *Block = Worklist.dequeue()) {
if (++Iterations > MaxIterations) {
return llvm::createStringError(std::errc::timed_out,
"maximum number of iterations reached");
}
const llvm::Optional<TypeErasedDataflowAnalysisState> &OldBlockState =
BlockStates[Block->getBlockID()];
TypeErasedDataflowAnalysisState NewBlockState =
transferBlock(CFCtx, BlockStates, *Block, InitEnv, Analysis);
if (OldBlockState &&
Analysis.isEqualTypeErased(OldBlockState.value().Lattice,
NewBlockState.Lattice) &&
OldBlockState->Env.equivalentTo(NewBlockState.Env, Analysis)) {
continue;
}
BlockStates[Block->getBlockID()] = std::move(NewBlockState);
if (Block->hasNoReturnElement())
continue;
Worklist.enqueueSuccessors(Block);
}
if (PostVisitStmt) {
for (const CFGBlock *Block : CFCtx.getCFG()) {
if (!BlockStates[Block->getBlockID()])
continue;
transferBlock(
CFCtx, BlockStates, *Block, InitEnv, Analysis,
[&PostVisitStmt](const clang::CFGStmt &Stmt,
const TypeErasedDataflowAnalysisState &State) {
PostVisitStmt(Stmt.getStmt(), State);
});
}
}
return BlockStates;
}
} }