Files
llvm-project/llvm/lib/Target/PowerPC/PPCPrepareIFuncsOnAIX.cpp
Wael Yehia e1f69ee8e8 [AIX] Implement the ifunc attribute. (#153049)
Currently, the AIX linker and loader do not provide a mechanism to
implement ifuncs similar to GNU_ifunc on ELF Linux.
On AIX, we will lower `__attribute__((ifunc("resolver"))` to the llvm
`ifunc` as other platforms do. The llvm `ifunc` in turn will get lowered
at late stages of the optimization pipeline to an AIX-specific
implementation. No special linkage or relocations are needed when
generating assembly/object output.

On AIX, a function `foo` has two symbols associated with it: a function
descriptor (`foo`) residing in the `.data` section, and an entry point
(`.foo`) residing in the `.text` section. The first field of the
descriptor is the address of the entry point. Typically, the address
field in the descriptor is initialized once: statically, at load time
(?), or at runtime if runtime linking is enabled.

Here we would like to use the address field in the descriptor to
implement the `ifunc` semantics. Specifically, the ifunc function will
become a stub that jumps to the entry point in the address field. A
constructor function is linked into every linkage module. The
constructor walks an array of `{descriptor, resolver}` pairs, calling
the resolver and saving the result in the address field in the
descriptor (thus setting `foo`'s descriptor to point to the resolved
version early during program runtime).

Known limitations:
- Due to bug #161576, which affects object generation path, you will
need either `-ffunction-sections` or `-fno-integrated-as` to generate a
correct/linkable object file.
- aliases to ifuncs are not supported, a testcase has been added and
marked XFAIL. I'm planning to address in a follow-up PR because it's not
important enough, IMHO, for this PR
- dead ifuncs in a CU that contains at least one live ifunc, will result
in all ifuncs being kept by the linker. The fix for this is common with
a similar problem we have with PGO. PR #159435 is trying to provide a
mechanism that will allow the ifunc and PGO implementations to avoid the
dead code retention at the link step.
- the resolver must return a function that is in the same DSO as the
ifunc; the compiler will try to detect if this condition is violated and
report it, but it cannot detect it in general. To be safe, all candidate
functions (returned by a particular resolver) must either be static or
have hidden/protected visibility. This is so that the ifunc stub doesn't
have to save and restore the TOC register r2. In future work, this case
will be supported and the requirement will be lifted.

---------

Co-authored-by: Wael Yehia <wyehia@ca.ibm.com>
2026-02-03 14:15:16 -05:00

115 lines
4.1 KiB
C++

//===-- PPCPrepareIFuncsOnAIX.cpp - Prepare for ifunc lowering in codegen ===//
//
// 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 pass generates...
//
//===----------------------------------------------------------------------===//
#include "PPC.h"
#include "PPCSubtarget.h"
#include "PPCTargetMachine.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include <cassert>
using namespace llvm;
#define DEBUG_TYPE "ppc-prep-ifunc-aix"
STATISTIC(NumIFuncs, "Number of IFuncs prepared");
namespace {
class PPCPrepareIFuncsOnAIX : public ModulePass {
public:
static char ID;
PPCPrepareIFuncsOnAIX() : ModulePass(ID) {}
bool runOnModule(Module &M) override;
StringRef getPassName() const override {
return "PPC Prepare for AIX IFunc lowering";
}
};
} // namespace
char PPCPrepareIFuncsOnAIX::ID = 0;
INITIALIZE_PASS(PPCPrepareIFuncsOnAIX, DEBUG_TYPE,
"PPC Prepare for AIX IFunc lowering", false, false)
ModulePass *llvm::createPPCPrepareIFuncsOnAIXPass() {
return new PPCPrepareIFuncsOnAIX();
}
// For each ifunc `foo` with a resolver `foo_resolver`, create a global variable
// `__update_foo` in the `ifunc_sec` section, representing the pair:
// { ptr @foo, ptr @foo_resolver }
// The compiler arranges for the constructor function `__init_ifuncs` to be
// included on the link step. The constructor walks the `ifunc_sec` section,
// calling the resolver function and storing the result in foo's descriptor.
// On AIX, the address of a function is the address of its descriptor, so the
// constructor accesses foo's descriptor from the first field of the pair.
//
// Since the global `__update_foo` is unreferenced, it's liveness needs to be
// associated to the liveness of ifunc `foo`
//
bool PPCPrepareIFuncsOnAIX::runOnModule(Module &M) {
if (M.ifuncs().empty())
return false;
const DataLayout &DL = M.getDataLayout();
LLVMContext &Ctx = M.getContext();
auto *PtrTy = PointerType::getUnqual(Ctx);
StringRef IFuncUpdatePrefix = "__update_";
StringRef IFuncUpdateSectionName = "__ifunc_sec";
StructType *IFuncPairType = StructType::get(PtrTy, PtrTy);
StringRef IFuncConstructorName = "__init_ifuncs";
auto *IFuncConstructorFnType =
FunctionType::get(Type::getVoidTy(Ctx), {}, /*isVarArg=*/false);
auto *IFuncConstructorDecl = cast<Function>(
M.getOrInsertFunction(IFuncConstructorName, IFuncConstructorFnType)
.getCallee());
for (GlobalIFunc &IFunc : M.ifuncs()) {
NumIFuncs++;
LLVM_DEBUG(dbgs() << "expanding ifunc " << IFunc.getName() << "\n");
// @__update_foo = private global { ptr @foo, ptr @foo_resolver },
// section "ifunc_sec"
std::string Name = (Twine(IFuncUpdatePrefix) + IFunc.getName()).str();
auto *GV = new GlobalVariable(M, IFuncPairType, /*isConstant*/ false,
GlobalValue::PrivateLinkage, nullptr, Name);
GV->setAlignment(DL.getPointerPrefAlignment());
GV->setSection(IFuncUpdateSectionName);
// Note that on AIX, the address of a function is the address of it's
// function descriptor, which is what these two values end up being
// in assembly.
Constant *InitVals[] = {&IFunc, IFunc.getResolver()};
GV->setInitializer(ConstantStruct::get(IFuncPairType, InitVals));
// Liveness of __update_foo is dependent on liveness of ifunc foo.
IFunc.setMetadata(LLVMContext::MD_implicit_ref,
MDNode::get(Ctx, ValueAsMetadata::get(GV)));
// An implicit.ref creates linkage dependency, so make function foo require
// the constructor that calls each ifunc's resolver and saves the result in
// the ifunc's function descriptor.
IFunc.addMetadata(
LLVMContext::MD_implicit_ref,
*MDNode::get(Ctx, ValueAsMetadata::get(IFuncConstructorDecl)));
}
return true;
}