1 //===-- AMDGPUCtorDtorLowering.cpp - Handle global ctors and dtors --------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 /// 9 /// \file 10 /// This pass creates a unified init and fini kernel with the required metadata 11 //===----------------------------------------------------------------------===// 12 13 #include "AMDGPUCtorDtorLowering.h" 14 #include "AMDGPU.h" 15 #include "llvm/IR/Constants.h" 16 #include "llvm/IR/Function.h" 17 #include "llvm/IR/GlobalVariable.h" 18 #include "llvm/IR/IRBuilder.h" 19 #include "llvm/IR/Module.h" 20 #include "llvm/IR/Value.h" 21 #include "llvm/Pass.h" 22 #include "llvm/Transforms/Utils/ModuleUtils.h" 23 24 using namespace llvm; 25 26 #define DEBUG_TYPE "amdgpu-lower-ctor-dtor" 27 28 namespace { 29 30 static Function *createInitOrFiniKernelFunction(Module &M, bool IsCtor) { 31 StringRef InitOrFiniKernelName = "amdgcn.device.init"; 32 if (!IsCtor) 33 InitOrFiniKernelName = "amdgcn.device.fini"; 34 if (M.getFunction(InitOrFiniKernelName)) 35 return nullptr; 36 37 Function *InitOrFiniKernel = Function::createWithDefaultAttr( 38 FunctionType::get(Type::getVoidTy(M.getContext()), false), 39 GlobalValue::WeakODRLinkage, 0, InitOrFiniKernelName, &M); 40 InitOrFiniKernel->setCallingConv(CallingConv::AMDGPU_KERNEL); 41 InitOrFiniKernel->addFnAttr("amdgpu-flat-work-group-size", "1,1"); 42 if (IsCtor) 43 InitOrFiniKernel->addFnAttr("device-init"); 44 else 45 InitOrFiniKernel->addFnAttr("device-fini"); 46 return InitOrFiniKernel; 47 } 48 49 // The linker will provide the associated symbols to allow us to traverse the 50 // global constructors / destructors in priority order. We create the IR 51 // required to call each callback in this section. This is equivalent to the 52 // following code. 53 // 54 // extern "C" void * __init_array_start[]; 55 // extern "C" void * __init_array_end[]; 56 // 57 // using InitCallback = void(); 58 // 59 // void call_init_array_callbacks() { 60 // for (auto start = __init_array_start; start != __init_array_end; ++start) 61 // reinterpret_cast<InitCallback *>(*start)(); 62 // } 63 static void createInitOrFiniCalls(Function &F, bool IsCtor) { 64 Module &M = *F.getParent(); 65 LLVMContext &C = M.getContext(); 66 67 IRBuilder<> IRB(BasicBlock::Create(C, "entry", &F)); 68 auto *LoopBB = BasicBlock::Create(C, "while.entry", &F); 69 auto *ExitBB = BasicBlock::Create(C, "while.end", &F); 70 Type *PtrTy = IRB.getPtrTy(AMDGPUAS::GLOBAL_ADDRESS); 71 72 auto *Begin = M.getOrInsertGlobal( 73 IsCtor ? "__init_array_start" : "__fini_array_start", 74 ArrayType::get(PtrTy, 0), [&]() { 75 return new GlobalVariable( 76 M, ArrayType::get(PtrTy, 0), 77 /*isConstant=*/true, GlobalValue::ExternalLinkage, 78 /*Initializer=*/nullptr, 79 IsCtor ? "__init_array_start" : "__fini_array_start", 80 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal, 81 /*AddressSpace=*/1); 82 }); 83 auto *End = M.getOrInsertGlobal( 84 IsCtor ? "__init_array_end" : "__fini_array_end", 85 ArrayType::get(PtrTy, 0), [&]() { 86 return new GlobalVariable( 87 M, ArrayType::get(PtrTy, 0), 88 /*isConstant=*/true, GlobalValue::ExternalLinkage, 89 /*Initializer=*/nullptr, 90 IsCtor ? "__init_array_end" : "__fini_array_end", 91 /*InsertBefore=*/nullptr, GlobalVariable::NotThreadLocal, 92 /*AddressSpace=*/1); 93 }); 94 95 // The constructor type is suppoed to allow using the argument vectors, but 96 // for now we just call them with no arguments. 97 auto *CallBackTy = FunctionType::get(IRB.getVoidTy(), {}); 98 99 IRB.CreateCondBr(IRB.CreateICmpNE(Begin, End), LoopBB, ExitBB); 100 IRB.SetInsertPoint(LoopBB); 101 auto *CallBackPHI = IRB.CreatePHI(PtrTy, 2, "ptr"); 102 auto *CallBack = IRB.CreateLoad(CallBackTy->getPointerTo(F.getAddressSpace()), 103 CallBackPHI, "callback"); 104 IRB.CreateCall(CallBackTy, CallBack); 105 auto *NewCallBack = IRB.CreateConstGEP1_64(PtrTy, CallBackPHI, 1, "next"); 106 auto *EndCmp = IRB.CreateICmpEQ(NewCallBack, End, "end"); 107 CallBackPHI->addIncoming(Begin, &F.getEntryBlock()); 108 CallBackPHI->addIncoming(NewCallBack, LoopBB); 109 IRB.CreateCondBr(EndCmp, ExitBB, LoopBB); 110 IRB.SetInsertPoint(ExitBB); 111 IRB.CreateRetVoid(); 112 } 113 114 static bool createInitOrFiniKernel(Module &M, StringRef GlobalName, 115 bool IsCtor) { 116 GlobalVariable *GV = M.getGlobalVariable(GlobalName); 117 if (!GV || !GV->hasInitializer()) 118 return false; 119 ConstantArray *GA = dyn_cast<ConstantArray>(GV->getInitializer()); 120 if (!GA || GA->getNumOperands() == 0) 121 return false; 122 123 Function *InitOrFiniKernel = createInitOrFiniKernelFunction(M, IsCtor); 124 if (!InitOrFiniKernel) 125 return false; 126 127 createInitOrFiniCalls(*InitOrFiniKernel, IsCtor); 128 129 appendToUsed(M, {InitOrFiniKernel}); 130 return true; 131 } 132 133 static bool lowerCtorsAndDtors(Module &M) { 134 bool Modified = false; 135 Modified |= createInitOrFiniKernel(M, "llvm.global_ctors", /*IsCtor =*/true); 136 Modified |= createInitOrFiniKernel(M, "llvm.global_dtors", /*IsCtor =*/false); 137 return Modified; 138 } 139 140 class AMDGPUCtorDtorLoweringLegacy final : public ModulePass { 141 public: 142 static char ID; 143 AMDGPUCtorDtorLoweringLegacy() : ModulePass(ID) {} 144 bool runOnModule(Module &M) override { return lowerCtorsAndDtors(M); } 145 }; 146 147 } // End anonymous namespace 148 149 PreservedAnalyses AMDGPUCtorDtorLoweringPass::run(Module &M, 150 ModuleAnalysisManager &AM) { 151 return lowerCtorsAndDtors(M) ? PreservedAnalyses::none() 152 : PreservedAnalyses::all(); 153 } 154 155 char AMDGPUCtorDtorLoweringLegacy::ID = 0; 156 char &llvm::AMDGPUCtorDtorLoweringLegacyPassID = 157 AMDGPUCtorDtorLoweringLegacy::ID; 158 INITIALIZE_PASS(AMDGPUCtorDtorLoweringLegacy, DEBUG_TYPE, 159 "Lower ctors and dtors for AMDGPU", false, false) 160 161 ModulePass *llvm::createAMDGPUCtorDtorLoweringLegacyPass() { 162 return new AMDGPUCtorDtorLoweringLegacy(); 163 } 164