// // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/LazyValueInfo.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/ConstantRange.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Instructions.h" #include "revng/Model/IRHelpers.h" #include "revng/PromoteStackPointer/ComputeStackAccessesBoundsPass.h" #include "revng/PromoteStackPointer/InstrumentStackAccessesPass.h" #include "revng/Support/FunctionTags.h" #include "revng/Support/IRHelpers.h" using namespace llvm; static Logger<> Log("compute-stack-accesses-bounds"); template bool areAll(const R &Range) { return llvm::all_of(Range, [](auto *Object) { return isa(Object); }); } static ConstantRange getSCEVBoundaries(ScalarEvolution &SE, PostDominatorTree &PDT, Instruction *I) { const auto *SCEV = SE.getSCEV(I); auto FullSet = ConstantRange::getFull(SCEV->getType()->getIntegerBitWidth()); revng_log(Log, "getSCEVBoundaries on " << dumpToString(*SCEV)); LoggerIndent<> Indent(Log); auto *AddRec = dyn_cast(SCEV); if (AddRec == nullptr) return FullSet; // Only consider loop in loop simplify form auto *Loop = AddRec->getLoop(); if (not Loop->isLoopSimplifyForm()) return FullSet; // Only provide a result if the block post-dominates the header, which // implies the block sees one more value than the backedge taken count BasicBlock *Location = I->getParent(); if (not PDT.dominates(Location, Loop->getHeader())) return FullSet; // Collect operands // TODO: handle multiple AddRecs const auto *Start = AddRec->getStart(); const class SCEV *End = nullptr; const auto *Stride = AddRec->getStepRecurrence(SE); auto *BackedgeTakenCount = SE.getBackedgeTakenCount(Loop); if (not areAll(std::vector{ Start, BackedgeTakenCount, Stride })) { return FullSet; } BackedgeTakenCount = SE.getTruncateOrZeroExtend(BackedgeTakenCount, Stride->getType()); const llvm::SCEV *Size = SE.getMulExpr(Stride, BackedgeTakenCount); const llvm::SCEV *One = SE.getOne(Start->getType()); bool Increasing = cast(Stride)->getAPInt().isStrictlyPositive(); revng_log(Log, "Increasing: " << Increasing); if (Increasing) { // Compute end value llvm::SmallVector Addends = { Start, Size, One }; End = SE.getAddExpr(Addends); } else { End = SE.getAddExpr(Start, One); Start = SE.getAddExpr(Start, Size); } revng_log(Log, "Start:" << dumpToString(*Start) << " End: " << dumpToString(*End)); // Ensure bounds could be computed and are constant if (not areAll(std::vector{ Start, End })) return FullSet; APInt StartValue = cast(Start)->getAPInt(); APInt EndValue = cast(End)->getAPInt(); ConstantRange Result(StartValue, EndValue); if (EndValue.slt(StartValue)) return FullSet; revng_log(Log, "Result: " << dumpToString(Result)); return Result; } bool ComputeStackAccessesBoundsPass::runOnFunction(Function &F) { revng_log(Log, "Running on " << F.getName().str()); LoggerIndent<> Indent(Log); ScalarEvolution &SE = getAnalysis().getSE(); PostDominatorTree &PDT = getAnalysis() .getPostDomTree(); LazyValueInfo &LVI = getAnalysis().getLVI(); if (F.isDeclaration()) return false; for (Function &StackOffsetFunction : FunctionTags::StackOffsetMarker.functions(F.getParent())) { auto *FunctionType = StackOffsetFunction.getFunctionType(); auto *DifferenceType = cast(FunctionType->getParamType(1)); for (CallBase *Call : callersIn(&StackOffsetFunction, &F)) { auto *Undef = UndefValue::get(DifferenceType); auto ReplaceUseWithBound = [&](Use &Operand, bool Lower) { revng_log(Log, "ReplaceUseWithBound on: " << getName(Operand.get()) << " (Lower: <<" << Lower << ")"); auto *User = Operand.getUser(); auto *V = Operand.get(); const APInt *Bound = nullptr; // Get range from LVI // TODO: using LVI is inaccurate, it offers an upper (lower) bound not // an exact bound. // Note that this is unlike using SCEV, since we use it in a way // that ensures the upper bound is actually used. // Eventually we'll need to deal with this incoherence and either // restrict the usage of LVI or drop it entirely. const auto &BoundRange = LVI.getConstantRange(V, cast(User)); revng_log(Log, "LazyValueInfo says " << dumpToString(BoundRange)); if (not BoundRange.isFullSet() and not BoundRange.isWrappedSet()) { Bound = Lower ? &BoundRange.getLower() : &BoundRange.getUpper(); } if (auto *I = dyn_cast(V)) { // See what SCEV says auto SCEVRange = getSCEVBoundaries(SE, PDT, I); APInt SCEVBound; if (Lower) { SCEVBound = SCEVRange.getLower(); if (not SCEVBound.isMinSignedValue() and (Bound == nullptr or SCEVBound.sgt(*Bound))) { Bound = &SCEVBound; } } else { SCEVBound = SCEVRange.getUpper(); if (not SCEVBound.isMaxSignedValue() and (Bound == nullptr or SCEVBound.slt(*Bound))) { Bound = &SCEVBound; } } } Operand.set(Bound != nullptr ? ConstantInt::get(DifferenceType, *Bound) : Undef); }; ReplaceUseWithBound(Call->getArgOperandUse(1), true); ReplaceUseWithBound(Call->getArgOperandUse(2), false); } } return true; } void ComputeStackAccessesBoundsPass::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); AU.addRequired(); AU.setPreservesCFG(); } char ComputeStackAccessesBoundsPass::ID = 0; using RegisterCSAB = RegisterPass; static RegisterCSAB R("compute-stack-accesses-bounds", "Compute Stack Accesses Bounds Pass");