346 lines · cpp
1//===-- GCRootLowering.cpp - Garbage collection infrastructure ------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the lowering for the gc.root mechanism.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/CodeGen/GCMetadata.h"14#include "llvm/CodeGen/MachineFrameInfo.h"15#include "llvm/CodeGen/MachineFunctionPass.h"16#include "llvm/CodeGen/MachineInstrBuilder.h"17#include "llvm/CodeGen/Passes.h"18#include "llvm/CodeGen/TargetFrameLowering.h"19#include "llvm/CodeGen/TargetInstrInfo.h"20#include "llvm/CodeGen/TargetRegisterInfo.h"21#include "llvm/CodeGen/TargetSubtargetInfo.h"22#include "llvm/IR/Dominators.h"23#include "llvm/IR/IntrinsicInst.h"24#include "llvm/IR/Module.h"25#include "llvm/InitializePasses.h"26#include "llvm/MC/MCContext.h"27 28using namespace llvm;29 30/// Lower barriers out of existence (if the associated GCStrategy hasn't31/// already done so...), and insert initializing stores to roots as a defensive32/// measure. Given we're going to report all roots live at all safepoints, we33/// need to be able to ensure each root has been initialized by the point the34/// first safepoint is reached. This really should have been done by the35/// frontend, but the old API made this non-obvious, so we do a potentially36/// redundant store just in case.37static bool DoLowering(Function &F, GCStrategy &S);38 39namespace {40 41/// LowerIntrinsics - This pass rewrites calls to the llvm.gcread or42/// llvm.gcwrite intrinsics, replacing them with simple loads and stores as43/// directed by the GCStrategy. It also performs automatic root initialization44/// and custom intrinsic lowering.45class LowerIntrinsics : public FunctionPass {46public:47 static char ID;48 49 LowerIntrinsics();50 StringRef getPassName() const override;51 void getAnalysisUsage(AnalysisUsage &AU) const override;52 53 bool doInitialization(Module &M) override;54 bool runOnFunction(Function &F) override;55};56 57/// GCMachineCodeAnalysis - This is a target-independent pass over the machine58/// function representation to identify safe points for the garbage collector59/// in the machine code. It inserts labels at safe points and populates a60/// GCMetadata record for each function.61class GCMachineCodeAnalysis : public MachineFunctionPass {62 GCFunctionInfo *FI = nullptr;63 const TargetInstrInfo *TII = nullptr;64 65 void FindSafePoints(MachineFunction &MF);66 void VisitCallPoint(MachineBasicBlock::iterator CI);67 MCSymbol *InsertLabel(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,68 const DebugLoc &DL) const;69 70 void FindStackOffsets(MachineFunction &MF);71 72public:73 static char ID;74 75 GCMachineCodeAnalysis();76 void getAnalysisUsage(AnalysisUsage &AU) const override;77 78 bool runOnMachineFunction(MachineFunction &MF) override;79};80}81 82PreservedAnalyses GCLoweringPass::run(Function &F,83 FunctionAnalysisManager &FAM) {84 if (!F.hasGC())85 return PreservedAnalyses::all();86 87 auto &Info = FAM.getResult<GCFunctionAnalysis>(F);88 89 bool Changed = DoLowering(F, Info.getStrategy());90 91 if (!Changed)92 return PreservedAnalyses::all();93 PreservedAnalyses PA;94 PA.preserve<DominatorTreeAnalysis>();95 return PA;96}97 98// -----------------------------------------------------------------------------99 100INITIALIZE_PASS_BEGIN(LowerIntrinsics, "gc-lowering", "GC Lowering", false,101 false)102INITIALIZE_PASS_DEPENDENCY(GCModuleInfo)103INITIALIZE_PASS_END(LowerIntrinsics, "gc-lowering", "GC Lowering", false, false)104 105FunctionPass *llvm::createGCLoweringPass() { return new LowerIntrinsics(); }106 107char LowerIntrinsics::ID = 0;108char &llvm::GCLoweringID = LowerIntrinsics::ID;109 110LowerIntrinsics::LowerIntrinsics() : FunctionPass(ID) {111 initializeLowerIntrinsicsPass(*PassRegistry::getPassRegistry());112}113 114StringRef LowerIntrinsics::getPassName() const {115 return "Lower Garbage Collection Instructions";116}117 118void LowerIntrinsics::getAnalysisUsage(AnalysisUsage &AU) const {119 FunctionPass::getAnalysisUsage(AU);120 AU.addRequired<GCModuleInfo>();121 AU.addPreserved<DominatorTreeWrapperPass>();122}123 124/// doInitialization - If this module uses the GC intrinsics, find them now.125bool LowerIntrinsics::doInitialization(Module &M) {126 GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>();127 assert(MI && "LowerIntrinsics didn't require GCModuleInfo!?");128 for (Function &F : M)129 if (!F.isDeclaration() && F.hasGC())130 MI->getFunctionInfo(F); // Instantiate the GC strategy.131 132 return false;133}134 135/// CouldBecomeSafePoint - Predicate to conservatively determine whether the136/// instruction could introduce a safe point.137static bool CouldBecomeSafePoint(Instruction *I) {138 // The natural definition of instructions which could introduce safe points139 // are:140 //141 // - call, invoke (AfterCall, BeforeCall)142 // - phis (Loops)143 // - invoke, ret, unwind (Exit)144 //145 // However, instructions as seemingly inoccuous as arithmetic can become146 // libcalls upon lowering (e.g., div i64 on a 32-bit platform), so instead147 // it is necessary to take a conservative approach.148 149 if (isa<AllocaInst>(I) || isa<GetElementPtrInst>(I) || isa<StoreInst>(I) ||150 isa<LoadInst>(I))151 return false;152 153 // llvm.gcroot is safe because it doesn't do anything at runtime.154 if (CallInst *CI = dyn_cast<CallInst>(I))155 if (Function *F = CI->getCalledFunction())156 if (Intrinsic::ID IID = F->getIntrinsicID())157 if (IID == Intrinsic::gcroot)158 return false;159 160 return true;161}162 163static bool InsertRootInitializers(Function &F, ArrayRef<AllocaInst *> Roots) {164 // Scroll past alloca instructions.165 BasicBlock::iterator IP = F.getEntryBlock().begin();166 while (isa<AllocaInst>(IP))167 ++IP;168 169 // Search for initializers in the initial BB.170 SmallPtrSet<AllocaInst *, 16> InitedRoots;171 for (; !CouldBecomeSafePoint(&*IP); ++IP)172 if (StoreInst *SI = dyn_cast<StoreInst>(IP))173 if (AllocaInst *AI =174 dyn_cast<AllocaInst>(SI->getOperand(1)->stripPointerCasts()))175 InitedRoots.insert(AI);176 177 // Add root initializers.178 bool MadeChange = false;179 180 for (AllocaInst *Root : Roots)181 if (!InitedRoots.count(Root)) {182 new StoreInst(183 ConstantPointerNull::get(cast<PointerType>(Root->getAllocatedType())),184 Root, std::next(Root->getIterator()));185 MadeChange = true;186 }187 188 return MadeChange;189}190 191/// runOnFunction - Replace gcread/gcwrite intrinsics with loads and stores.192/// Leave gcroot intrinsics; the code generator needs to see those.193bool LowerIntrinsics::runOnFunction(Function &F) {194 // Quick exit for functions that do not use GC.195 if (!F.hasGC())196 return false;197 198 GCFunctionInfo &FI = getAnalysis<GCModuleInfo>().getFunctionInfo(F);199 GCStrategy &S = FI.getStrategy();200 201 return DoLowering(F, S);202}203 204bool DoLowering(Function &F, GCStrategy &S) {205 SmallVector<AllocaInst *, 32> Roots;206 207 bool MadeChange = false;208 for (BasicBlock &BB : F)209 for (Instruction &I : llvm::make_early_inc_range(BB)) {210 IntrinsicInst *CI = dyn_cast<IntrinsicInst>(&I);211 if (!CI)212 continue;213 214 Function *F = CI->getCalledFunction();215 switch (F->getIntrinsicID()) {216 default: break;217 case Intrinsic::gcwrite: {218 // Replace a write barrier with a simple store.219 Value *St = new StoreInst(CI->getArgOperand(0), CI->getArgOperand(2),220 CI->getIterator());221 CI->replaceAllUsesWith(St);222 CI->eraseFromParent();223 MadeChange = true;224 break;225 }226 case Intrinsic::gcread: {227 // Replace a read barrier with a simple load.228 Value *Ld = new LoadInst(CI->getType(), CI->getArgOperand(1), "",229 CI->getIterator());230 Ld->takeName(CI);231 CI->replaceAllUsesWith(Ld);232 CI->eraseFromParent();233 MadeChange = true;234 break;235 }236 case Intrinsic::gcroot: {237 // Initialize the GC root, but do not delete the intrinsic. The238 // backend needs the intrinsic to flag the stack slot.239 Roots.push_back(240 cast<AllocaInst>(CI->getArgOperand(0)->stripPointerCasts()));241 break;242 }243 }244 }245 246 if (Roots.size())247 MadeChange |= InsertRootInitializers(F, Roots);248 249 return MadeChange;250}251 252// -----------------------------------------------------------------------------253 254char GCMachineCodeAnalysis::ID = 0;255char &llvm::GCMachineCodeAnalysisID = GCMachineCodeAnalysis::ID;256 257INITIALIZE_PASS(GCMachineCodeAnalysis, "gc-analysis",258 "Analyze Machine Code For Garbage Collection", false, false)259 260GCMachineCodeAnalysis::GCMachineCodeAnalysis() : MachineFunctionPass(ID) {}261 262void GCMachineCodeAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {263 MachineFunctionPass::getAnalysisUsage(AU);264 AU.setPreservesAll();265 AU.addRequired<GCModuleInfo>();266}267 268MCSymbol *GCMachineCodeAnalysis::InsertLabel(MachineBasicBlock &MBB,269 MachineBasicBlock::iterator MI,270 const DebugLoc &DL) const {271 MCSymbol *Label = MBB.getParent()->getContext().createTempSymbol();272 BuildMI(MBB, MI, DL, TII->get(TargetOpcode::GC_LABEL)).addSym(Label);273 return Label;274}275 276void GCMachineCodeAnalysis::VisitCallPoint(MachineBasicBlock::iterator CI) {277 // Find the return address (next instruction), since that's what will be on278 // the stack when the call is suspended and we need to inspect the stack.279 MachineBasicBlock::iterator RAI = CI;280 ++RAI;281 282 MCSymbol *Label = InsertLabel(*CI->getParent(), RAI, CI->getDebugLoc());283 FI->addSafePoint(Label, CI->getDebugLoc());284}285 286void GCMachineCodeAnalysis::FindSafePoints(MachineFunction &MF) {287 for (MachineBasicBlock &MBB : MF)288 for (MachineInstr &MI : MBB)289 if (MI.isCall()) {290 // Do not treat tail or sibling call sites as safe points. This is291 // legal since any arguments passed to the callee which live in the292 // remnants of the callers frame will be owned and updated by the293 // callee if required.294 if (MI.isTerminator())295 continue;296 VisitCallPoint(&MI);297 }298}299 300void GCMachineCodeAnalysis::FindStackOffsets(MachineFunction &MF) {301 const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering();302 assert(TFI && "TargetRegisterInfo not available!");303 304 for (GCFunctionInfo::roots_iterator RI = FI->roots_begin();305 RI != FI->roots_end();) {306 // If the root references a dead object, no need to keep it.307 if (MF.getFrameInfo().isDeadObjectIndex(RI->Num)) {308 RI = FI->removeStackRoot(RI);309 } else {310 Register FrameReg; // FIXME: surely GCRoot ought to store the311 // register that the offset is from?312 auto FrameOffset = TFI->getFrameIndexReference(MF, RI->Num, FrameReg);313 assert(!FrameOffset.getScalable() &&314 "Frame offsets with a scalable component are not supported");315 RI->StackOffset = FrameOffset.getFixed();316 ++RI;317 }318 }319}320 321bool GCMachineCodeAnalysis::runOnMachineFunction(MachineFunction &MF) {322 // Quick exit for functions that do not use GC.323 if (!MF.getFunction().hasGC())324 return false;325 326 FI = &getAnalysis<GCModuleInfo>().getFunctionInfo(MF.getFunction());327 TII = MF.getSubtarget().getInstrInfo();328 329 // Find the size of the stack frame. There may be no correct static frame330 // size, we use UINT64_MAX to represent this.331 const MachineFrameInfo &MFI = MF.getFrameInfo();332 const TargetRegisterInfo *RegInfo = MF.getSubtarget().getRegisterInfo();333 const bool DynamicFrameSize =334 MFI.hasVarSizedObjects() || RegInfo->hasStackRealignment(MF);335 FI->setFrameSize(DynamicFrameSize ? UINT64_MAX : MFI.getStackSize());336 337 // Find all safe points.338 if (FI->getStrategy().needsSafePoints())339 FindSafePoints(MF);340 341 // Find the concrete stack offsets for all roots (stack slots)342 FindStackOffsets(MF);343 344 return false;345}346