| //===- SafeStackColoring.cpp - SafeStack frame coloring -------------------===// |
| // |
| // 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 "SafeStackColoring.h" |
| #include "llvm/ADT/BitVector.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/DepthFirstIterator.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/Config/llvm-config.h" |
| #include "llvm/IR/BasicBlock.h" |
| #include "llvm/IR/CFG.h" |
| #include "llvm/IR/Instruction.h" |
| #include "llvm/IR/Instructions.h" |
| #include "llvm/IR/IntrinsicInst.h" |
| #include "llvm/IR/Intrinsics.h" |
| #include "llvm/IR/User.h" |
| #include "llvm/Support/Casting.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <cassert> |
| #include <tuple> |
| #include <utility> |
| |
| using namespace llvm; |
| using namespace llvm::safestack; |
| |
| #define DEBUG_TYPE "safestackcoloring" |
| |
| // Disabled by default due to PR32143. |
| static cl::opt<bool> ClColoring("safe-stack-coloring", |
| cl::desc("enable safe stack coloring"), |
| cl::Hidden, cl::init(false)); |
| |
| const StackColoring::LiveRange &StackColoring::getLiveRange(AllocaInst *AI) { |
| const auto IT = AllocaNumbering.find(AI); |
| assert(IT != AllocaNumbering.end()); |
| return LiveRanges[IT->second]; |
| } |
| |
| bool StackColoring::readMarker(Instruction *I, bool *IsStart) { |
| if (!I->isLifetimeStartOrEnd()) |
| return false; |
| |
| auto *II = cast<IntrinsicInst>(I); |
| *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 |
| LLVM_DEBUG(dbgs() << "Instructions:\n"); |
| unsigned InstNo = 0; |
| for (BasicBlock *BB : depth_first(&F)) { |
| LLVM_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) { |
| LLVM_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); |
| // If a predecessor is unreachable, ignore it. |
| if (I == BlockLiveness.end()) |
| continue; |
| 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) || Start[AllocaNo] == BBStart); |
| if (!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); |
| } |
| } |
| |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| 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"; |
| } |
| } |
| #endif |
| |
| void StackColoring::run() { |
| LLVM_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(); |
| LLVM_DEBUG(dumpBlockLiveness()); |
| calculateLiveIntervals(); |
| LLVM_DEBUG(dumpLiveRanges()); |
| } |