| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,289 @@ | ||
| //===-- SafeStackColoring.cpp - SafeStack frame coloring -------*- C++ -*--===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "SafeStackColoring.h" | ||
|
|
||
| #include "llvm/ADT/DepthFirstIterator.h" | ||
| #include "llvm/IR/CFG.h" | ||
| #include "llvm/IR/Instructions.h" | ||
| #include "llvm/IR/IntrinsicInst.h" | ||
| #include "llvm/Support/Debug.h" | ||
|
|
||
| using namespace llvm; | ||
| using namespace llvm::safestack; | ||
|
|
||
| #define DEBUG_TYPE "safestackcoloring" | ||
|
|
||
| static cl::opt<bool> ClColoring("safe-stack-coloring", | ||
| cl::desc("enable safe stack coloring"), | ||
| cl::Hidden, cl::init(true)); | ||
|
|
||
| const StackColoring::LiveRange &StackColoring::getLiveRange(AllocaInst *AI) { | ||
| return LiveRanges[AllocaNumbering[AI]]; | ||
| } | ||
|
|
||
| bool StackColoring::readMarker(Instruction *I, bool *IsStart) { | ||
| auto *II = dyn_cast<IntrinsicInst>(I); | ||
| if (!II || (II->getIntrinsicID() != Intrinsic::lifetime_start && | ||
| II->getIntrinsicID() != Intrinsic::lifetime_end)) | ||
| return false; | ||
|
|
||
| *IsStart = II->getIntrinsicID() == Intrinsic::lifetime_start; | ||
| return true; | ||
| } | ||
|
|
||
| void StackColoring::removeAllMarkers() { | ||
| for (auto *I : Markers) { | ||
| auto *Op = dyn_cast<Instruction>(I->getOperand(1)); | ||
| I->eraseFromParent(); | ||
| // Remove the operand bitcast, too, if it has no more uses left. | ||
| if (Op && Op->use_empty()) | ||
| Op->eraseFromParent(); | ||
| } | ||
| } | ||
|
|
||
| void StackColoring::collectMarkers() { | ||
| InterestingAllocas.resize(NumAllocas); | ||
| DenseMap<BasicBlock *, SmallDenseMap<Instruction *, Marker>> BBMarkerSet; | ||
|
|
||
| // Compute the set of start/end markers per basic block. | ||
| for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) { | ||
| AllocaInst *AI = Allocas[AllocaNo]; | ||
| SmallVector<Instruction *, 8> WorkList; | ||
| WorkList.push_back(AI); | ||
| while (!WorkList.empty()) { | ||
| Instruction *I = WorkList.pop_back_val(); | ||
| for (User *U : I->users()) { | ||
| if (auto *BI = dyn_cast<BitCastInst>(U)) { | ||
| WorkList.push_back(BI); | ||
| continue; | ||
| } | ||
| auto *UI = dyn_cast<Instruction>(U); | ||
| if (!UI) | ||
| continue; | ||
| bool IsStart; | ||
| if (!readMarker(UI, &IsStart)) | ||
| continue; | ||
| if (IsStart) | ||
| InterestingAllocas.set(AllocaNo); | ||
| BBMarkerSet[UI->getParent()][UI] = {AllocaNo, IsStart}; | ||
| Markers.push_back(UI); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Compute instruction numbering. Only the following instructions are | ||
| // considered: | ||
| // * Basic block entries | ||
| // * Lifetime markers | ||
| // For each basic block, compute | ||
| // * the list of markers in the instruction order | ||
| // * the sets of allocas whose lifetime starts or ends in this BB | ||
| DEBUG(dbgs() << "Instructions:\n"); | ||
| unsigned InstNo = 0; | ||
| for (BasicBlock *BB : depth_first(&F)) { | ||
| DEBUG(dbgs() << " " << InstNo << ": BB " << BB->getName() << "\n"); | ||
| unsigned BBStart = InstNo++; | ||
|
|
||
| BlockLifetimeInfo &BlockInfo = BlockLiveness[BB]; | ||
| BlockInfo.Begin.resize(NumAllocas); | ||
| BlockInfo.End.resize(NumAllocas); | ||
| BlockInfo.LiveIn.resize(NumAllocas); | ||
| BlockInfo.LiveOut.resize(NumAllocas); | ||
|
|
||
| auto &BlockMarkerSet = BBMarkerSet[BB]; | ||
| if (BlockMarkerSet.empty()) { | ||
| unsigned BBEnd = InstNo; | ||
| BlockInstRange[BB] = std::make_pair(BBStart, BBEnd); | ||
| continue; | ||
| } | ||
|
|
||
| auto ProcessMarker = [&](Instruction *I, const Marker &M) { | ||
| DEBUG(dbgs() << " " << InstNo << ": " | ||
| << (M.IsStart ? "start " : "end ") << M.AllocaNo << ", " | ||
| << *I << "\n"); | ||
|
|
||
| BBMarkers[BB].push_back({InstNo, M}); | ||
|
|
||
| InstructionNumbering[I] = InstNo++; | ||
|
|
||
| if (M.IsStart) { | ||
| if (BlockInfo.End.test(M.AllocaNo)) | ||
| BlockInfo.End.reset(M.AllocaNo); | ||
| BlockInfo.Begin.set(M.AllocaNo); | ||
| } else { | ||
| if (BlockInfo.Begin.test(M.AllocaNo)) | ||
| BlockInfo.Begin.reset(M.AllocaNo); | ||
| BlockInfo.End.set(M.AllocaNo); | ||
| } | ||
| }; | ||
|
|
||
| if (BlockMarkerSet.size() == 1) { | ||
| ProcessMarker(BlockMarkerSet.begin()->getFirst(), | ||
| BlockMarkerSet.begin()->getSecond()); | ||
| } else { | ||
| // Scan the BB to determine the marker order. | ||
| for (Instruction &I : *BB) { | ||
| auto It = BlockMarkerSet.find(&I); | ||
| if (It == BlockMarkerSet.end()) | ||
| continue; | ||
| ProcessMarker(&I, It->getSecond()); | ||
| } | ||
| } | ||
|
|
||
| unsigned BBEnd = InstNo; | ||
| BlockInstRange[BB] = std::make_pair(BBStart, BBEnd); | ||
| } | ||
| NumInst = InstNo; | ||
| } | ||
|
|
||
| void StackColoring::calculateLocalLiveness() { | ||
| bool changed = true; | ||
| while (changed) { | ||
| changed = false; | ||
|
|
||
| for (BasicBlock *BB : depth_first(&F)) { | ||
| BlockLifetimeInfo &BlockInfo = BlockLiveness[BB]; | ||
|
|
||
| // Compute LiveIn by unioning together the LiveOut sets of all preds. | ||
| BitVector LocalLiveIn; | ||
| for (auto *PredBB : predecessors(BB)) { | ||
| LivenessMap::const_iterator I = BlockLiveness.find(PredBB); | ||
| assert(I != BlockLiveness.end() && "Predecessor not found"); | ||
| LocalLiveIn |= I->second.LiveOut; | ||
| } | ||
|
|
||
| // Compute LiveOut by subtracting out lifetimes that end in this | ||
| // block, then adding in lifetimes that begin in this block. If | ||
| // we have both BEGIN and END markers in the same basic block | ||
| // then we know that the BEGIN marker comes after the END, | ||
| // because we already handle the case where the BEGIN comes | ||
| // before the END when collecting the markers (and building the | ||
| // BEGIN/END vectors). | ||
| BitVector LocalLiveOut = LocalLiveIn; | ||
| LocalLiveOut.reset(BlockInfo.End); | ||
| LocalLiveOut |= BlockInfo.Begin; | ||
|
|
||
| // Update block LiveIn set, noting whether it has changed. | ||
| if (LocalLiveIn.test(BlockInfo.LiveIn)) { | ||
| changed = true; | ||
| BlockInfo.LiveIn |= LocalLiveIn; | ||
| } | ||
|
|
||
| // Update block LiveOut set, noting whether it has changed. | ||
| if (LocalLiveOut.test(BlockInfo.LiveOut)) { | ||
| changed = true; | ||
| BlockInfo.LiveOut |= LocalLiveOut; | ||
| } | ||
| } | ||
| } // while changed. | ||
| } | ||
|
|
||
| void StackColoring::calculateLiveIntervals() { | ||
| for (auto IT : BlockLiveness) { | ||
| BasicBlock *BB = IT.getFirst(); | ||
| BlockLifetimeInfo &BlockInfo = IT.getSecond(); | ||
| unsigned BBStart, BBEnd; | ||
| std::tie(BBStart, BBEnd) = BlockInstRange[BB]; | ||
|
|
||
| BitVector Started, Ended; | ||
| Started.resize(NumAllocas); | ||
| Ended.resize(NumAllocas); | ||
| SmallVector<unsigned, 8> Start; | ||
| Start.resize(NumAllocas); | ||
|
|
||
| // LiveIn ranges start at the first instruction. | ||
| for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) { | ||
| if (BlockInfo.LiveIn.test(AllocaNo)) { | ||
| Started.set(AllocaNo); | ||
| Start[AllocaNo] = BBStart; | ||
| } | ||
| } | ||
|
|
||
| for (auto &It : BBMarkers[BB]) { | ||
| unsigned InstNo = It.first; | ||
| bool IsStart = It.second.IsStart; | ||
| unsigned AllocaNo = It.second.AllocaNo; | ||
|
|
||
| if (IsStart) { | ||
| assert(!Started.test(AllocaNo)); | ||
| Started.set(AllocaNo); | ||
| Ended.reset(AllocaNo); | ||
| Start[AllocaNo] = InstNo; | ||
| } else { | ||
| assert(!Ended.test(AllocaNo)); | ||
| if (Started.test(AllocaNo)) { | ||
| LiveRanges[AllocaNo].AddRange(Start[AllocaNo], InstNo); | ||
| Started.reset(AllocaNo); | ||
| } | ||
| Ended.set(AllocaNo); | ||
| } | ||
| } | ||
|
|
||
| for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) | ||
| if (Started.test(AllocaNo)) | ||
| LiveRanges[AllocaNo].AddRange(Start[AllocaNo], BBEnd); | ||
| } | ||
| } | ||
|
|
||
| LLVM_DUMP_METHOD void StackColoring::dumpAllocas() { | ||
| dbgs() << "Allocas:\n"; | ||
| for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) | ||
| dbgs() << " " << AllocaNo << ": " << *Allocas[AllocaNo] << "\n"; | ||
| } | ||
|
|
||
| LLVM_DUMP_METHOD void StackColoring::dumpBlockLiveness() { | ||
| dbgs() << "Block liveness:\n"; | ||
| for (auto IT : BlockLiveness) { | ||
| BasicBlock *BB = IT.getFirst(); | ||
| BlockLifetimeInfo &BlockInfo = BlockLiveness[BB]; | ||
| auto BlockRange = BlockInstRange[BB]; | ||
| dbgs() << " BB [" << BlockRange.first << ", " << BlockRange.second | ||
| << "): begin " << BlockInfo.Begin << ", end " << BlockInfo.End | ||
| << ", livein " << BlockInfo.LiveIn << ", liveout " | ||
| << BlockInfo.LiveOut << "\n"; | ||
| } | ||
| } | ||
|
|
||
| LLVM_DUMP_METHOD void StackColoring::dumpLiveRanges() { | ||
| dbgs() << "Alloca liveness:\n"; | ||
| for (unsigned AllocaNo = 0; AllocaNo < NumAllocas; ++AllocaNo) { | ||
| LiveRange &Range = LiveRanges[AllocaNo]; | ||
| dbgs() << " " << AllocaNo << ": " << Range << "\n"; | ||
| } | ||
| } | ||
|
|
||
| void StackColoring::run() { | ||
| DEBUG(dumpAllocas()); | ||
|
|
||
| for (unsigned I = 0; I < NumAllocas; ++I) | ||
| AllocaNumbering[Allocas[I]] = I; | ||
| LiveRanges.resize(NumAllocas); | ||
|
|
||
| collectMarkers(); | ||
|
|
||
| if (!ClColoring) { | ||
| for (auto &R : LiveRanges) { | ||
| R.SetMaximum(1); | ||
| R.AddRange(0, 1); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| for (auto &R : LiveRanges) | ||
| R.SetMaximum(NumInst); | ||
| for (unsigned I = 0; I < NumAllocas; ++I) | ||
| if (!InterestingAllocas.test(I)) | ||
| LiveRanges[I] = getFullLiveRange(); | ||
|
|
||
| calculateLocalLiveness(); | ||
| DEBUG(dumpBlockLiveness()); | ||
| calculateLiveIntervals(); | ||
| DEBUG(dumpLiveRanges()); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| //===-- SafeStackColoring.h - SafeStack frame coloring ---------*- C++ -*--===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIB_CODEGEN_SAFESTACKCOLORING_H | ||
| #define LLVM_LIB_CODEGEN_SAFESTACKCOLORING_H | ||
|
|
||
| #include "llvm/ADT/BitVector.h" | ||
| #include "llvm/ADT/DenseMap.h" | ||
| #include "llvm/ADT/SmallVector.h" | ||
| #include "llvm/IR/Function.h" | ||
| #include "llvm/Support/raw_os_ostream.h" | ||
|
|
||
| namespace llvm { | ||
| class AllocaInst; | ||
|
|
||
| namespace safestack { | ||
| /// Compute live ranges of allocas. | ||
| /// Live ranges are represented as sets of "interesting" instructions, which are | ||
| /// defined as instructions that may start or end an alloca's lifetime. These | ||
| /// are: | ||
| /// * lifetime.start and lifetime.end intrinsics | ||
| /// * first instruction of any basic block | ||
| /// Interesting instructions are numbered in the depth-first walk of the CFG, | ||
| /// and in the program order inside each basic block. | ||
| class StackColoring { | ||
| /// A class representing liveness information for a single basic block. | ||
| /// Each bit in the BitVector represents the liveness property | ||
| /// for a different stack slot. | ||
| struct BlockLifetimeInfo { | ||
| /// Which slots BEGINs in each basic block. | ||
| BitVector Begin; | ||
| /// Which slots ENDs in each basic block. | ||
| BitVector End; | ||
| /// Which slots are marked as LIVE_IN, coming into each basic block. | ||
| BitVector LiveIn; | ||
| /// Which slots are marked as LIVE_OUT, coming out of each basic block. | ||
| BitVector LiveOut; | ||
| }; | ||
|
|
||
| public: | ||
| /// This class represents a set of interesting instructions where an alloca is | ||
| /// live. | ||
| struct LiveRange { | ||
| BitVector bv; | ||
| void SetMaximum(int size) { bv.resize(size); } | ||
| void AddRange(unsigned start, unsigned end) { bv.set(start, end); } | ||
| bool Overlaps(const LiveRange &Other) const { | ||
| return bv.anyCommon(Other.bv); | ||
| } | ||
| void Join(const LiveRange &Other) { bv |= Other.bv; } | ||
| }; | ||
|
|
||
| private: | ||
| Function &F; | ||
|
|
||
| /// Maps active slots (per bit) for each basic block. | ||
| typedef DenseMap<BasicBlock *, BlockLifetimeInfo> LivenessMap; | ||
| LivenessMap BlockLiveness; | ||
|
|
||
| /// Number of interesting instructions. | ||
| int NumInst; | ||
| /// Numeric ids for interesting instructions. | ||
| DenseMap<Instruction *, unsigned> InstructionNumbering; | ||
| /// A range [Start, End) of instruction ids for each basic block. | ||
| /// Instructions inside each BB have monotonic and consecutive ids. | ||
| DenseMap<const BasicBlock *, std::pair<unsigned, unsigned>> BlockInstRange; | ||
|
|
||
| ArrayRef<AllocaInst *> Allocas; | ||
| unsigned NumAllocas; | ||
| DenseMap<AllocaInst *, unsigned> AllocaNumbering; | ||
| /// LiveRange for allocas. | ||
| SmallVector<LiveRange, 8> LiveRanges; | ||
|
|
||
| /// The set of allocas that have at least one lifetime.start. All other | ||
| /// allocas get LiveRange that corresponds to the entire function. | ||
| BitVector InterestingAllocas; | ||
| SmallVector<Instruction *, 8> Markers; | ||
|
|
||
| struct Marker { | ||
| unsigned AllocaNo; | ||
| bool IsStart; | ||
| }; | ||
|
|
||
| /// List of {InstNo, {AllocaNo, IsStart}} for each BB, ordered by InstNo. | ||
| DenseMap<BasicBlock *, SmallVector<std::pair<unsigned, Marker>, 4>> BBMarkers; | ||
|
|
||
| void dumpAllocas(); | ||
| void dumpBlockLiveness(); | ||
| void dumpLiveRanges(); | ||
|
|
||
| bool readMarker(Instruction *I, bool *IsStart); | ||
| void collectMarkers(); | ||
| void calculateLocalLiveness(); | ||
| void calculateLiveIntervals(); | ||
|
|
||
| public: | ||
| StackColoring(Function &F, ArrayRef<AllocaInst *> Allocas) | ||
| : F(F), NumInst(-1), Allocas(Allocas), NumAllocas(Allocas.size()) {} | ||
|
|
||
| void run(); | ||
| void removeAllMarkers(); | ||
|
|
||
| /// Returns a set of "interesting" instructions where the given alloca is | ||
| /// live. Not all instructions in a function are interesting: we pick a set | ||
| /// that is large enough for LiveRange::Overlaps to be correct. | ||
| const LiveRange &getLiveRange(AllocaInst *AI); | ||
|
|
||
| /// Returns a live range that represents an alloca that is live throughout the | ||
| /// entire function. | ||
| LiveRange getFullLiveRange() { | ||
| assert(NumInst >= 0); | ||
| LiveRange R; | ||
| R.SetMaximum(NumInst); | ||
| R.AddRange(0, NumInst); | ||
| return R; | ||
| } | ||
| }; | ||
|
|
||
| static inline raw_ostream &operator<<(raw_ostream &OS, const BitVector &V) { | ||
| OS << "{"; | ||
| int idx = V.find_first(); | ||
| bool first = true; | ||
| while (idx >= 0) { | ||
| if (!first) { | ||
| OS << ", "; | ||
| } | ||
| first = false; | ||
| OS << idx; | ||
| idx = V.find_next(idx); | ||
| } | ||
| OS << "}"; | ||
| return OS; | ||
| } | ||
|
|
||
| static inline raw_ostream &operator<<(raw_ostream &OS, | ||
| const StackColoring::LiveRange &R) { | ||
| return OS << R.bv; | ||
| } | ||
|
|
||
| } // namespace safestack | ||
| } // namespace llvm | ||
|
|
||
| #endif // LLVM_LIB_CODEGEN_SAFESTACKCOLORING_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| //===-- SafeStackLayout.cpp - SafeStack frame layout -----------*- C++ -*--===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "SafeStackLayout.h" | ||
|
|
||
| #include "llvm/IR/Instructions.h" | ||
| #include "llvm/Support/Debug.h" | ||
|
|
||
| using namespace llvm; | ||
| using namespace llvm::safestack; | ||
|
|
||
| #define DEBUG_TYPE "safestacklayout" | ||
|
|
||
| static cl::opt<bool> ClLayout("safe-stack-layout", | ||
| cl::desc("enable safe stack layout"), cl::Hidden, | ||
| cl::init(true)); | ||
|
|
||
| LLVM_DUMP_METHOD void StackLayout::print(raw_ostream &OS) { | ||
| OS << "Stack regions:\n"; | ||
| for (unsigned i = 0; i < Regions.size(); ++i) { | ||
| OS << " " << i << ": [" << Regions[i].Start << ", " << Regions[i].End | ||
| << "), range " << Regions[i].Range << "\n"; | ||
| } | ||
| OS << "Stack objects:\n"; | ||
| for (auto &IT : ObjectOffsets) { | ||
| OS << " at " << IT.getSecond() << ": " << *IT.getFirst() << "\n"; | ||
| } | ||
| } | ||
|
|
||
| void StackLayout::addObject(const Value *V, unsigned Size, unsigned Alignment, | ||
| const StackColoring::LiveRange &Range) { | ||
| StackObjects.push_back({V, Size, Alignment, Range}); | ||
| MaxAlignment = std::max(MaxAlignment, Alignment); | ||
| } | ||
|
|
||
| static unsigned AdjustStackOffset(unsigned Offset, unsigned Size, | ||
| unsigned Alignment) { | ||
| return alignTo(Offset + Size, Alignment) - Size; | ||
| } | ||
|
|
||
| void StackLayout::layoutObject(StackObject &Obj) { | ||
| if (!ClLayout) { | ||
| // If layout is disabled, just grab the next aligned address. | ||
| // This effectively disables stack coloring as well. | ||
| unsigned LastRegionEnd = Regions.empty() ? 0 : Regions.back().End; | ||
| unsigned Start = AdjustStackOffset(LastRegionEnd, Obj.Size, Obj.Alignment); | ||
| unsigned End = Start + Obj.Size; | ||
| Regions.emplace_back(Start, End, Obj.Range); | ||
| ObjectOffsets[Obj.Handle] = End; | ||
| return; | ||
| } | ||
|
|
||
| DEBUG(dbgs() << "Layout: size " << Obj.Size << ", align " << Obj.Alignment | ||
| << ", range " << Obj.Range << "\n"); | ||
| assert(Obj.Alignment <= MaxAlignment); | ||
| unsigned Start = AdjustStackOffset(0, Obj.Size, Obj.Alignment); | ||
| unsigned End = Start + Obj.Size; | ||
| DEBUG(dbgs() << " First candidate: " << Start << " .. " << End << "\n"); | ||
| for (const StackRegion &R : Regions) { | ||
| DEBUG(dbgs() << " Examining region: " << R.Start << " .. " << R.End | ||
| << ", range " << R.Range << "\n"); | ||
| assert(End >= R.Start); | ||
| if (Start >= R.End) { | ||
| DEBUG(dbgs() << " Does not intersect, skip.\n"); | ||
| continue; | ||
| } | ||
| if (Obj.Range.Overlaps(R.Range)) { | ||
| // Find the next appropriate location. | ||
| Start = AdjustStackOffset(R.End, Obj.Size, Obj.Alignment); | ||
| End = Start + Obj.Size; | ||
| DEBUG(dbgs() << " Overlaps. Next candidate: " << Start << " .. " << End | ||
| << "\n"); | ||
| continue; | ||
| } | ||
| if (End <= R.End) { | ||
| DEBUG(dbgs() << " Reusing region(s).\n"); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| unsigned LastRegionEnd = Regions.empty() ? 0 : Regions.back().End; | ||
| if (End > LastRegionEnd) { | ||
| // Insert a new region at the end. Maybe two. | ||
| if (Start > LastRegionEnd) { | ||
| DEBUG(dbgs() << " Creating gap region: " << LastRegionEnd << " .. " | ||
| << Start << "\n"); | ||
| Regions.emplace_back(LastRegionEnd, Start, StackColoring::LiveRange()); | ||
| LastRegionEnd = Start; | ||
| } | ||
| DEBUG(dbgs() << " Creating new region: " << LastRegionEnd << " .. " << End | ||
| << ", range " << Obj.Range << "\n"); | ||
| Regions.emplace_back(LastRegionEnd, End, Obj.Range); | ||
| LastRegionEnd = End; | ||
| } | ||
|
|
||
| // Split starting and ending regions if necessary. | ||
| for (StackRegion &R : Regions) { | ||
| if (Start > R.Start && Start < R.End) { | ||
| StackRegion R0 = R; | ||
| R.Start = R0.End = Start; | ||
| Regions.insert(&R, R0); | ||
| continue; | ||
| } | ||
| if (End > R.Start && End < R.End) { | ||
| StackRegion R0 = R; | ||
| R0.End = R.Start = End; | ||
| Regions.insert(&R, R0); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| // Update live ranges for all affected regions. | ||
| for (StackRegion &R : Regions) { | ||
| if (Start < R.End && End > R.Start) | ||
| R.Range.Join(Obj.Range); | ||
| if (End <= R.End) | ||
| break; | ||
| } | ||
|
|
||
| ObjectOffsets[Obj.Handle] = End; | ||
| } | ||
|
|
||
| void StackLayout::computeLayout() { | ||
| // Simple greedy algorithm. | ||
| // If this is replaced with something smarter, it must preserve the property | ||
| // that the first object is always at the offset 0 in the stack frame (for | ||
| // StackProtectorSlot), or handle stack protector in some other way. | ||
| for (auto &Obj : StackObjects) | ||
| layoutObject(Obj); | ||
|
|
||
| DEBUG(print(dbgs())); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| //===-- SafeStackLayout.h - SafeStack frame layout -------------*- C++ -*--===// | ||
| // | ||
| // The LLVM Compiler Infrastructure | ||
| // | ||
| // This file is distributed under the University of Illinois Open Source | ||
| // License. See LICENSE.TXT for details. | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIB_CODEGEN_SAFESTACKLAYOUT_H | ||
| #define LLVM_LIB_CODEGEN_SAFESTACKLAYOUT_H | ||
|
|
||
| #include "SafeStackColoring.h" | ||
|
|
||
| namespace llvm { | ||
| namespace safestack { | ||
|
|
||
| /// Compute the layout of an unsafe stack frame. | ||
| class StackLayout { | ||
| unsigned MaxAlignment; | ||
|
|
||
| struct StackRegion { | ||
| unsigned Start; | ||
| unsigned End; | ||
| StackColoring::LiveRange Range; | ||
| StackRegion(unsigned Start, unsigned End, | ||
| const StackColoring::LiveRange &Range) | ||
| : Start(Start), End(End), Range(Range) {} | ||
| }; | ||
| /// The list of current stack regions, sorted by StackRegion::Start. | ||
| SmallVector<StackRegion, 16> Regions; | ||
|
|
||
| struct StackObject { | ||
| const Value *Handle; | ||
| unsigned Size, Alignment; | ||
| StackColoring::LiveRange Range; | ||
| }; | ||
| SmallVector<StackObject, 8> StackObjects; | ||
|
|
||
| DenseMap<const Value *, unsigned> ObjectOffsets; | ||
|
|
||
| void layoutObject(StackObject &Obj); | ||
|
|
||
| public: | ||
| StackLayout(unsigned StackAlignment) : MaxAlignment(StackAlignment) {} | ||
| /// Add an object to the stack frame. Value pointer is opaque and used as a | ||
| /// handle to retrieve the object's offset in the frame later. | ||
| void addObject(const Value *V, unsigned Size, unsigned Alignment, | ||
| const StackColoring::LiveRange &Range); | ||
|
|
||
| /// Run the layout computation for all previously added objects. | ||
| void computeLayout(); | ||
|
|
||
| /// Returns the offset to the object start in the stack frame. | ||
| unsigned getObjectOffset(const Value *V) { return ObjectOffsets[V]; } | ||
|
|
||
| /// Returns the size of the entire frame. | ||
| unsigned getFrameSize() { return Regions.empty() ? 0 : Regions.back().End; } | ||
|
|
||
| /// Returns the alignment of the frame. | ||
| unsigned getFrameAlignment() { return MaxAlignment; } | ||
| void print(raw_ostream &OS); | ||
| }; | ||
|
|
||
| } // namespace safestack | ||
| } // namespace llvm | ||
|
|
||
| #endif // LLVM_LIB_CODEGEN_SAFESTACKLAYOUT_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| ; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s | ||
| ; RUN: opt -safe-stack -S -mtriple=x86_64-pc-linux-gnu < %s -o - | FileCheck %s | ||
|
|
||
| define void @f() safestack { | ||
| entry: | ||
| ; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr | ||
| ; CHECK: %[[USST:.*]] = getelementptr i8, i8* %[[USP]], i32 -16 | ||
|
|
||
| %x = alloca i32, align 4 | ||
| %x1 = alloca i32, align 4 | ||
| %x2 = alloca i32, align 4 | ||
| %0 = bitcast i32* %x to i8* | ||
| call void @llvm.lifetime.start(i64 4, i8* %0) | ||
|
|
||
| ; CHECK: %[[A1:.*]] = getelementptr i8, i8* %[[USP]], i32 -4 | ||
| ; CHECK: %[[A2:.*]] = bitcast i8* %[[A1]] to i32* | ||
| ; CHECK: call void @capture(i32* nonnull %[[A2]]) | ||
|
|
||
| call void @capture(i32* nonnull %x) | ||
| call void @llvm.lifetime.end(i64 4, i8* %0) | ||
| %1 = bitcast i32* %x1 to i8* | ||
| call void @llvm.lifetime.start(i64 4, i8* %1) | ||
|
|
||
| ; CHECK: %[[B1:.*]] = getelementptr i8, i8* %[[USP]], i32 -4 | ||
| ; CHECK: %[[B2:.*]] = bitcast i8* %[[B1]] to i32* | ||
| ; CHECK: call void @capture(i32* nonnull %[[B2]]) | ||
|
|
||
| call void @capture(i32* nonnull %x1) | ||
| call void @llvm.lifetime.end(i64 4, i8* %1) | ||
| %2 = bitcast i32* %x2 to i8* | ||
| call void @llvm.lifetime.start(i64 4, i8* %2) | ||
|
|
||
| ; CHECK: %[[C1:.*]] = getelementptr i8, i8* %[[USP]], i32 -4 | ||
| ; CHECK: %[[C2:.*]] = bitcast i8* %[[C1]] to i32* | ||
| ; CHECK: call void @capture(i32* nonnull %[[C2]]) | ||
|
|
||
| call void @capture(i32* nonnull %x2) | ||
| call void @llvm.lifetime.end(i64 4, i8* %2) | ||
| ret void | ||
| } | ||
|
|
||
| declare void @llvm.lifetime.start(i64, i8* nocapture) | ||
| declare void @llvm.lifetime.end(i64, i8* nocapture) | ||
| declare void @capture(i32*) |