diff options
author | Matt Arsenault <Matthew.Arsenault@amd.com> | 2022-12-01 19:02:18 -0500 |
---|---|---|
committer | Matt Arsenault <arsenm2@gmail.com> | 2023-01-17 22:33:56 -0500 |
commit | e7cd42f8e4da1beed52f401dcf87d22d36a2c81c (patch) | |
tree | 69baf2fd37af141c90e5ea08cd91d46334bfc57f /llvm/lib/Transforms/Utils/ModuleUtils.cpp | |
parent | 76d3e1a4980ff64a69494087f0a094b90ad54dff (diff) | |
download | llvm-e7cd42f8e4da1beed52f401dcf87d22d36a2c81c.zip llvm-e7cd42f8e4da1beed52f401dcf87d22d36a2c81c.tar.gz llvm-e7cd42f8e4da1beed52f401dcf87d22d36a2c81c.tar.bz2 |
Utils: Add utility pass to lower ifuncs
Create a global constructor which will initialize a global table of
function pointers. For now, this is only used as a reduction technique
for llvm-reduce.
In the future this may be useful to support ifunc on systems where the
program loader doesn't natively support it.
Diffstat (limited to 'llvm/lib/Transforms/Utils/ModuleUtils.cpp')
-rw-r--r-- | llvm/lib/Transforms/Utils/ModuleUtils.cpp | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp index 2c2e8db..5148df5 100644 --- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp +++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp @@ -347,3 +347,102 @@ void llvm::embedBufferInModule(Module &M, MemoryBufferRef Buf, appendToCompilerUsed(M, GV); } + +bool llvm::lowerGlobalIFuncUsersAsGlobalCtor( + Module &M, ArrayRef<GlobalIFunc *> FilteredIFuncsToLower) { + SmallVector<GlobalIFunc *, 32> AllIFuncs; + ArrayRef<GlobalIFunc *> IFuncsToLower = FilteredIFuncsToLower; + if (FilteredIFuncsToLower.empty()) { // Default to lowering all ifuncs + for (GlobalIFunc &GI : M.ifuncs()) + AllIFuncs.push_back(&GI); + IFuncsToLower = AllIFuncs; + } + + bool UnhandledUsers = false; + LLVMContext &Ctx = M.getContext(); + const DataLayout &DL = M.getDataLayout(); + + PointerType *TableEntryTy = + Ctx.supportsTypedPointers() + ? PointerType::get(Type::getInt8Ty(Ctx), DL.getProgramAddressSpace()) + : PointerType::get(Ctx, DL.getProgramAddressSpace()); + + ArrayType *FuncPtrTableTy = + ArrayType::get(TableEntryTy, IFuncsToLower.size()); + + Align PtrAlign = DL.getABITypeAlign(TableEntryTy); + + // Create a global table of function pointers we'll initialize in a global + // constructor. + auto *FuncPtrTable = new GlobalVariable( + M, FuncPtrTableTy, false, GlobalValue::InternalLinkage, + PoisonValue::get(FuncPtrTableTy), "", nullptr, + GlobalVariable::NotThreadLocal, DL.getDefaultGlobalsAddressSpace()); + FuncPtrTable->setAlignment(PtrAlign); + + // Create a function to initialize the function pointer table. + Function *NewCtor = Function::Create( + FunctionType::get(Type::getVoidTy(Ctx), false), Function::InternalLinkage, + DL.getProgramAddressSpace(), "", &M); + + BasicBlock *BB = BasicBlock::Create(Ctx, "", NewCtor); + IRBuilder<> InitBuilder(BB); + + size_t TableIndex = 0; + for (GlobalIFunc *GI : IFuncsToLower) { + Function *ResolvedFunction = GI->getResolverFunction(); + + // We don't know what to pass to a resolver function taking arguments + // + // FIXME: Is this even valid? clang and gcc don't complain but this + // probably should be invalid IR. We could just pass through undef. + if (!std::empty(ResolvedFunction->getFunctionType()->params())) { + LLVM_DEBUG(dbgs() << "Not lowering ifunc resolver function " + << ResolvedFunction->getName() << " with parameters\n"); + UnhandledUsers = true; + continue; + } + + // Initialize the function pointer table. + CallInst *ResolvedFunc = InitBuilder.CreateCall(ResolvedFunction); + Value *Casted = InitBuilder.CreatePointerCast(ResolvedFunc, TableEntryTy); + Constant *GEP = cast<Constant>(InitBuilder.CreateConstInBoundsGEP2_32( + FuncPtrTableTy, FuncPtrTable, 0, TableIndex++)); + InitBuilder.CreateAlignedStore(Casted, GEP, PtrAlign); + + // Update all users to load a pointer from the global table. + for (User *User : make_early_inc_range(GI->users())) { + Instruction *UserInst = dyn_cast<Instruction>(User); + if (!UserInst) { + // TODO: Should handle constantexpr casts in user instructions. Probably + // can't do much about constant initializers. + UnhandledUsers = true; + continue; + } + + IRBuilder<> UseBuilder(UserInst); + LoadInst *ResolvedTarget = + UseBuilder.CreateAlignedLoad(TableEntryTy, GEP, PtrAlign); + Value *ResolvedCast = + UseBuilder.CreatePointerCast(ResolvedTarget, GI->getType()); + UserInst->replaceUsesOfWith(GI, ResolvedCast); + } + + // If we handled all users, erase the ifunc. + if (GI->use_empty()) + GI->eraseFromParent(); + } + + InitBuilder.CreateRetVoid(); + + PointerType *ConstantDataTy = Ctx.supportsTypedPointers() + ? PointerType::get(Type::getInt8Ty(Ctx), 0) + : PointerType::get(Ctx, 0); + + // TODO: Is this the right priority? Probably should be before any other + // constructors? + const int Priority = 10; + appendToGlobalCtors(M, NewCtor, Priority, + ConstantPointerNull::get(ConstantDataTy)); + return UnhandledUsers; +} |