aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms
diff options
context:
space:
mode:
authorMatt Arsenault <Matthew.Arsenault@amd.com>2022-12-01 19:02:18 -0500
committerMatt Arsenault <arsenm2@gmail.com>2023-01-17 22:33:56 -0500
commite7cd42f8e4da1beed52f401dcf87d22d36a2c81c (patch)
tree69baf2fd37af141c90e5ea08cd91d46334bfc57f /llvm/lib/Transforms
parent76d3e1a4980ff64a69494087f0a094b90ad54dff (diff)
downloadllvm-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')
-rw-r--r--llvm/lib/Transforms/Utils/CMakeLists.txt1
-rw-r--r--llvm/lib/Transforms/Utils/LowerIFunc.cpp27
-rw-r--r--llvm/lib/Transforms/Utils/ModuleUtils.cpp99
3 files changed, 127 insertions, 0 deletions
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index c2750cd..0edd42b 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -46,6 +46,7 @@ add_llvm_component_library(LLVMTransformUtils
LoopVersioning.cpp
LowerAtomic.cpp
LowerGlobalDtors.cpp
+ LowerIFunc.cpp
LowerInvoke.cpp
LowerMemIntrinsics.cpp
LowerSwitch.cpp
diff --git a/llvm/lib/Transforms/Utils/LowerIFunc.cpp b/llvm/lib/Transforms/Utils/LowerIFunc.cpp
new file mode 100644
index 0000000..18ae0bbe
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/LowerIFunc.cpp
@@ -0,0 +1,27 @@
+//===- LowerIFunc.cpp -----------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements replacing calls to ifuncs by introducing indirect calls.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/LowerIFunc.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/Utils/ModuleUtils.h"
+
+using namespace llvm;
+
+/// Replace all call users of ifuncs in the module.
+PreservedAnalyses LowerIFuncPass::run(Module &M, ModuleAnalysisManager &AM) {
+ if (M.ifunc_empty())
+ return PreservedAnalyses::all();
+
+ lowerGlobalIFuncUsersAsGlobalCtor(M, {});
+ return PreservedAnalyses::none();
+}
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;
+}