For the GPU, we emit external kernels that call the initializers and
constructors, however if we had a persistent kernel like in the `_start`
kernel for the `libc` project, we could initialize the standard way of
calling constructors. This patch adds new global variables containing
pointers to the constructors to be called. If these are placed in the
`.init_array` and `.fini_array` sections, then the backend will handle
them specially. The linker will then provide the `__init_array_` and
`__fini_array_` sections to traverse them. An implementation would look
like this.
```
extern uintptr_t __init_array_start[];
extern uintptr_t __init_array_end[];
extern uintptr_t __fini_array_start[];
extern uintptr_t __fini_array_end[];
using InitCallback = void(int, char **, char **);
using FiniCallback = void(void);
extern "C" [[gnu::visibility("protected"), clang::amdgpu_kernel]] void
_start(int argc, char **argv, char **envp) {
uint64_t init_array_size = __init_array_end - __init_array_start;
for (uint64_t i = 0; i < init_array_size; ++i)
reinterpret_cast<InitCallback *>(__init_array_start[i])(argc, argv, env);
uint64_t fini_array_size = __fini_array_end - __fini_array_start;
for (uint64_t i = 0; i < fini_array_size; ++i)
reinterpret_cast<FiniCallback *>(__fini_array_start[i])();
}
```
Reviewed By: yaxunl
Differential Revision: https://reviews.llvm.org/D149340
114 lines
3.9 KiB
C++
114 lines
3.9 KiB
C++
//===-- AMDGPUCtorDtorLowering.cpp - Handle global ctors and dtors --------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// This pass creates a unified init and fini kernel with the required metadata
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AMDGPUCtorDtorLowering.h"
|
|
#include "AMDGPU.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/IR/GlobalVariable.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/Value.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Transforms/Utils/ModuleUtils.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "amdgpu-lower-ctor-dtor"
|
|
|
|
namespace {
|
|
|
|
static Function *createInitOrFiniKernelFunction(Module &M, bool IsCtor) {
|
|
StringRef InitOrFiniKernelName = "amdgcn.device.init";
|
|
if (!IsCtor)
|
|
InitOrFiniKernelName = "amdgcn.device.fini";
|
|
if (Function *F = M.getFunction(InitOrFiniKernelName))
|
|
return F;
|
|
|
|
Function *InitOrFiniKernel = Function::createWithDefaultAttr(
|
|
FunctionType::get(Type::getVoidTy(M.getContext()), false),
|
|
GlobalValue::ExternalLinkage, 0, InitOrFiniKernelName, &M);
|
|
BasicBlock *InitOrFiniKernelBB =
|
|
BasicBlock::Create(M.getContext(), "", InitOrFiniKernel);
|
|
ReturnInst::Create(M.getContext(), InitOrFiniKernelBB);
|
|
|
|
InitOrFiniKernel->setCallingConv(CallingConv::AMDGPU_KERNEL);
|
|
if (IsCtor)
|
|
InitOrFiniKernel->addFnAttr("device-init");
|
|
else
|
|
InitOrFiniKernel->addFnAttr("device-fini");
|
|
return InitOrFiniKernel;
|
|
}
|
|
|
|
static bool createInitOrFiniKernel(Module &M, StringRef GlobalName,
|
|
bool IsCtor) {
|
|
GlobalVariable *GV = M.getGlobalVariable(GlobalName);
|
|
if (!GV || !GV->hasInitializer())
|
|
return false;
|
|
ConstantArray *GA = dyn_cast<ConstantArray>(GV->getInitializer());
|
|
if (!GA || GA->getNumOperands() == 0)
|
|
return false;
|
|
|
|
Function *InitOrFiniKernel = createInitOrFiniKernelFunction(M, IsCtor);
|
|
IRBuilder<> IRB(InitOrFiniKernel->getEntryBlock().getTerminator());
|
|
|
|
FunctionType *ConstructorTy = InitOrFiniKernel->getFunctionType();
|
|
|
|
for (Value *V : GA->operands()) {
|
|
auto *CS = cast<ConstantStruct>(V);
|
|
bool AlreadyRegistered =
|
|
llvm::any_of(CS->getOperand(1)->uses(), [=](Use &U) {
|
|
if (auto *CB = dyn_cast<CallBase>(U.getUser()))
|
|
if (CB->getCaller() == InitOrFiniKernel)
|
|
return true;
|
|
return false;
|
|
});
|
|
if (!AlreadyRegistered)
|
|
IRB.CreateCall(ConstructorTy, CS->getOperand(1));
|
|
}
|
|
|
|
appendToUsed(M, {InitOrFiniKernel});
|
|
return true;
|
|
}
|
|
|
|
static bool lowerCtorsAndDtors(Module &M) {
|
|
bool Modified = false;
|
|
Modified |= createInitOrFiniKernel(M, "llvm.global_ctors", /*IsCtor =*/true);
|
|
Modified |= createInitOrFiniKernel(M, "llvm.global_dtors", /*IsCtor =*/false);
|
|
return Modified;
|
|
}
|
|
|
|
class AMDGPUCtorDtorLoweringLegacy final : public ModulePass {
|
|
public:
|
|
static char ID;
|
|
AMDGPUCtorDtorLoweringLegacy() : ModulePass(ID) {}
|
|
bool runOnModule(Module &M) override { return lowerCtorsAndDtors(M); }
|
|
};
|
|
|
|
} // End anonymous namespace
|
|
|
|
PreservedAnalyses AMDGPUCtorDtorLoweringPass::run(Module &M,
|
|
ModuleAnalysisManager &AM) {
|
|
return lowerCtorsAndDtors(M) ? PreservedAnalyses::none()
|
|
: PreservedAnalyses::all();
|
|
}
|
|
|
|
char AMDGPUCtorDtorLoweringLegacy::ID = 0;
|
|
char &llvm::AMDGPUCtorDtorLoweringLegacyPassID =
|
|
AMDGPUCtorDtorLoweringLegacy::ID;
|
|
INITIALIZE_PASS(AMDGPUCtorDtorLoweringLegacy, DEBUG_TYPE,
|
|
"Lower ctors and dtors for AMDGPU", false, false)
|
|
|
|
ModulePass *llvm::createAMDGPUCtorDtorLoweringLegacyPass() {
|
|
return new AMDGPUCtorDtorLoweringLegacy();
|
|
}
|