[clang] [llvm] [AIX] Implement the ifunc attribute. (PR #153049)
Wael Yehia via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 13 13:44:45 PDT 2025
https://github.com/w2yehia updated https://github.com/llvm/llvm-project/pull/153049
>From 87cd7b2124ed6e1450f10a53726fcb8db6d7b8e7 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 11 Aug 2025 15:56:25 +0000
Subject: [PATCH 1/9] Create and implement lowering of two intrinsics:
int.ppc.get.function.descriptor int.ppc.get.function.entry
---
llvm/include/llvm/IR/IntrinsicsPowerPC.td | 10 ++++++++
llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp | 10 ++++++--
llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp | 4 +++
llvm/lib/Target/PowerPC/PPCISelLowering.cpp | 27 +++++++++++++++++++++
4 files changed, 49 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/IR/IntrinsicsPowerPC.td b/llvm/include/llvm/IR/IntrinsicsPowerPC.td
index 7dd9ff7f08b8b..75b64fba6e3ca 100644
--- a/llvm/include/llvm/IR/IntrinsicsPowerPC.td
+++ b/llvm/include/llvm/IR/IntrinsicsPowerPC.td
@@ -2090,3 +2090,13 @@ let TargetPrefix = "ppc" in {
Intrinsic<[], [llvm_i64_ty, llvm_i64_ty, llvm_ptr_ty],
[IntrArgMemOnly, IntrWriteMem, NoCapture<ArgIndex<2>>]>;
}
+
+
+//===----------------------------------------------------------------------===//
+// XCOFF Intrinsics
+let TargetPrefix = "ppc" in {
+ def int_ppc_get_function_entry :
+ DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_ptr_ty], [IntrNoMem]>;
+ def int_ppc_get_function_descriptor :
+ DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_ptr_ty], [IntrNoMem]>;
+}
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 2ab2c147be0ec..8b9b4975643c4 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -763,6 +763,8 @@ static MCSymbol *getMCSymbolForTOCPseudoMO(const MachineOperand &MO,
return AP.GetJTISymbol(MO.getIndex());
case MachineOperand::MO_BlockAddress:
return AP.GetBlockAddressSymbol(MO.getBlockAddress());
+ case MachineOperand::MO_MCSymbol:
+ return MO.getMCSymbol();
default:
llvm_unreachable("Unexpected operand type to get symbol.");
}
@@ -792,6 +794,8 @@ getTOCEntryTypeForMO(const MachineOperand &MO) {
return PPCAsmPrinter::TOCType_JumpTable;
case MachineOperand::MO_BlockAddress:
return PPCAsmPrinter::TOCType_BlockAddress;
+ case MachineOperand::MO_MCSymbol:
+ return PPCAsmPrinter::TOCType_GlobalExternal; // TODO
default:
llvm_unreachable("Unexpected operand type to get TOC type.");
}
@@ -1043,7 +1047,8 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
TmpInst.setOpcode(PPC::LWZ);
const MachineOperand &MO = MI->getOperand(1);
- assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
+ assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress() ||
+ MO.isMCSymbol()) &&
"Invalid operand for LWZtoc.");
// Map the operand to its corresponding MCSymbol.
@@ -1127,7 +1132,8 @@ void PPCAsmPrinter::emitInstruction(const MachineInstr *MI) {
TmpInst.setOpcode(PPC::LD);
const MachineOperand &MO = MI->getOperand(1);
- assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress()) &&
+ assert((MO.isGlobal() || MO.isCPI() || MO.isJTI() || MO.isBlockAddress() ||
+ MO.isMCSymbol()) &&
"Invalid operand!");
// Map the operand to its corresponding MCSymbol.
diff --git a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
index 415164fc9e2cb..921cb72a83030 100644
--- a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp
@@ -6158,6 +6158,10 @@ void PPCDAGToDAGISel::Select(SDNode *N) {
replaceWith(PPC::ADDItoc8, N, MVT::i64);
return;
}
+ if (N->getOperand(0).getOpcode() == ISD::MCSymbol) {
+ replaceWith(PPC::LDtoc, N, MVT::i64);
+ return;
+ }
// Break if it doesn't have toc data attribute. Proceed with common
// SelectCode.
break;
diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index 74ae8502dccea..83525797ce5ca 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -11167,6 +11167,33 @@ SDValue PPCTargetLowering::LowerINTRINSIC_WO_CHAIN(SDValue Op,
SDLoc dl(Op);
switch (IntrinsicID) {
+ case Intrinsic::ppc_get_function_descriptor:
+ return Op.getOperand(1);
+ case Intrinsic::ppc_get_function_entry: {
+ SDValue Op1 = Op.getOperand(1);
+ if (auto *G = dyn_cast<GlobalAddressSDNode>(Op1)) {
+ const GlobalValue *GV = G->getGlobal();
+ assert(isFunctionGlobalAddress(GV));
+ assert(Subtarget.isAIXABI());
+ assert(!isa<GlobalIFunc>(GV) && "IFunc is not supported on AIX.");
+ const TargetMachine &TM = Subtarget.getTargetMachine();
+ const TargetLoweringObjectFile *TLOF = TM.getObjFileLowering();
+ MCSymbolXCOFF *S = static_cast<MCSymbolXCOFF *>(
+ TLOF->getFunctionEntryPointSymbol(GV, TM));
+
+ MVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout());
+ SDValue EntryPointSym = DAG.getMCSymbol(S, PtrVT);
+ return getTOCEntry(DAG, dl, EntryPointSym);
+ }
+ assert(Op1.getOpcode() == ISD::CopyFromReg);
+ SDLoc dl(Op);
+ EVT PtrVT = getPointerTy(DAG.getDataLayout());
+ SDValue Result =
+ DAG.getLoad(PtrVT, dl, DAG.getEntryNode(), Op1, MachinePointerInfo(),
+ DAG.getDataLayout().getPointerABIAlignment(0),
+ MachineMemOperand::MODereferenceable);
+ return Result;
+ }
case Intrinsic::thread_pointer:
// Reads the thread pointer register, used for __builtin_thread_pointer.
if (Subtarget.isPPC64())
>From 0bc22a6d89c3b86ad3e8b1584298b28734e95031 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Tue, 5 Aug 2025 18:01:19 +0000
Subject: [PATCH 2/9] accept ifunc attribute on AIX
---
clang/include/clang/Basic/TargetInfo.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index ce4677e540226..ffeab27b67911 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -1545,6 +1545,8 @@ class TargetInfo : public TransferrableTargetInfo,
return true;
if (getTriple().getArch() == llvm::Triple::ArchType::avr)
return true;
+ if (getTriple().isOSAIX())
+ return true;
return getTriple().isOSBinFormatELF() &&
((getTriple().isOSLinux() && !getTriple().isMusl()) ||
getTriple().isOSFreeBSD());
>From c294b4398dad26e5b7c05fb6e88dd82b2878d71c Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Mon, 11 Aug 2025 16:01:25 +0000
Subject: [PATCH 3/9] initial LowerIFuncsOnAIX impl
---
.../llvm/Transforms/Utils/ModuleUtils.h | 7 ++
llvm/lib/Target/PowerPC/PPCTargetMachine.cpp | 12 +++
llvm/lib/Target/PowerPC/PPCTargetMachine.h | 2 +
llvm/lib/Transforms/Utils/LowerIFunc.cpp | 7 +-
llvm/lib/Transforms/Utils/ModuleUtils.cpp | 98 ++++++++++++++++++-
5 files changed, 124 insertions(+), 2 deletions(-)
diff --git a/llvm/include/llvm/Transforms/Utils/ModuleUtils.h b/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
index 4036c4e947c75..2734422ba0329 100644
--- a/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
+++ b/llvm/include/llvm/Transforms/Utils/ModuleUtils.h
@@ -159,6 +159,13 @@ LLVM_ABI bool
lowerGlobalIFuncUsersAsGlobalCtor(Module &M,
ArrayRef<GlobalIFunc *> IFuncsToLower = {});
+/// AIX specific lowering of ifuncs where we convert an ifunc to a regular
+/// function with the following implementation:
+/// Check if the function's descriptor still points to itself (true on first
+/// entry), if so then call the resolver function and atomically store the
+/// resulting function pointer into the descriptor. Make an indirect call
+/// through the function pointer in the descriptor.
+LLVM_ABI void lowerIFuncsOnAIX(Module &M);
} // End llvm namespace
#endif // LLVM_TRANSFORMS_UTILS_MODULEUTILS_H
diff --git a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
index b5c6ac111dff0..a2d8bca663274 100644
--- a/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
+++ b/llvm/lib/Target/PowerPC/PPCTargetMachine.cpp
@@ -36,6 +36,7 @@
#include "llvm/InitializePasses.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Pass.h"
+#include "llvm/Passes/PassBuilder.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
@@ -43,6 +44,7 @@
#include "llvm/Target/TargetOptions.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Scalar.h"
+#include "llvm/Transforms/Utils/LowerIFunc.h"
#include <cassert>
#include <memory>
#include <optional>
@@ -452,6 +454,16 @@ class PPCPassConfig : public TargetPassConfig {
} // end anonymous namespace
+void PPCTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) {
+ if (getTargetTriple().isOSAIX())
+ PB.registerOptimizerLastEPCallback([](ModulePassManager &PM,
+ OptimizationLevel Level,
+ ThinOrFullLTOPhase Phase) {
+ if (Phase == ThinOrFullLTOPhase::None)
+ PM.addPass(LowerIFuncPass());
+ });
+}
+
TargetPassConfig *PPCTargetMachine::createPassConfig(PassManagerBase &PM) {
return new PPCPassConfig(*this, PM);
}
diff --git a/llvm/lib/Target/PowerPC/PPCTargetMachine.h b/llvm/lib/Target/PowerPC/PPCTargetMachine.h
index cb02b446fadb3..608750f7a3f2b 100644
--- a/llvm/lib/Target/PowerPC/PPCTargetMachine.h
+++ b/llvm/lib/Target/PowerPC/PPCTargetMachine.h
@@ -54,6 +54,8 @@ class PPCTargetMachine final : public CodeGenTargetMachineImpl {
// Pass Pipeline Configuration
TargetPassConfig *createPassConfig(PassManagerBase &PM) override;
+ void registerPassBuilderCallbacks(PassBuilder &PB) override;
+
TargetTransformInfo getTargetTransformInfo(const Function &F) const override;
TargetLoweringObjectFile *getObjFileLowering() const override {
diff --git a/llvm/lib/Transforms/Utils/LowerIFunc.cpp b/llvm/lib/Transforms/Utils/LowerIFunc.cpp
index 18ae0bbe2e731..991890aa56b83 100644
--- a/llvm/lib/Transforms/Utils/LowerIFunc.cpp
+++ b/llvm/lib/Transforms/Utils/LowerIFunc.cpp
@@ -13,6 +13,7 @@
#include "llvm/Transforms/Utils/LowerIFunc.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
+#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
@@ -22,6 +23,10 @@ PreservedAnalyses LowerIFuncPass::run(Module &M, ModuleAnalysisManager &AM) {
if (M.ifunc_empty())
return PreservedAnalyses::all();
- lowerGlobalIFuncUsersAsGlobalCtor(M, {});
+ Triple TargetTriple(M.getTargetTriple());
+ if (TargetTriple.isOSAIX())
+ lowerIFuncsOnAIX(M);
+ else
+ lowerGlobalIFuncUsersAsGlobalCtor(M, {});
return PreservedAnalyses::none();
}
diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
index 596849ecab742..10e898bf266f0 100644
--- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -11,11 +11,13 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/ModuleUtils.h"
-#include "llvm/Analysis/VectorUtils.h"
#include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/VectorUtils.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/IntrinsicsPowerPC.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Casting.h"
@@ -397,6 +399,100 @@ void llvm::embedBufferInModule(Module &M, MemoryBufferRef Buf,
appendToCompilerUsed(M, GV);
}
+void llvm::lowerIFuncsOnAIX(Module &M) {
+ for (GlobalIFunc &IFunc : make_early_inc_range(M.ifuncs())) {
+
+ // Let IFunc be:
+ // @foo = ifunc rt-type (type1, type2), ptr @resolver
+ assert(isa<llvm::FunctionType>(IFunc.getValueType()));
+ FunctionType *FTy = cast<llvm::FunctionType>(IFunc.getValueType());
+
+ // Create the regular (non-ifunc) function:
+ // define rt-type @foo(type1 %0, type2 %1)
+ Function *F = Function::Create(
+ FTy, IFunc.getLinkage(), IFunc.getAddressSpace(), IFunc.getName(), &M);
+ LLVMContext &Ctx = F->getContext();
+
+ // entry:
+ BasicBlock *CurBlock = BasicBlock::Create(Ctx, "entry", F);
+ IRBuilder<> Builder(CurBlock);
+
+ PointerType *PtrTy = Builder.getPtrTy();
+ // %DescPtr = call ptr @ppc_get_function_descriptor(ptr noundef @foo)
+ auto *DescPtr = Builder.CreateIntrinsic(
+ /*RetTy*/ PtrTy, Intrinsic::ppc_get_function_descriptor, {F}, {},
+ "desc.ptr");
+
+ // %DesctAddr = getelementptr inbounds %struct.Desc_t, ptr %DescPtr, i32
+ // 0, i32 0
+ StructType *DescriptorType = StructType::get(PtrTy, PtrTy, PtrTy);
+ auto *DesctAddr =
+ Builder.CreateStructGEP(DescriptorType, DescPtr, 0, "desc_t.addr");
+
+ // %AddrInDesc = load ptr, ptr %DesctAddr, align 4
+ auto *AddrInDesc = Builder.CreateAlignedLoad(
+ PtrTy, DesctAddr, DesctAddr->getPointerAlignment(M.getDataLayout()),
+ "addr.in.desc");
+
+ // %OriginalAddr = call ptr @ppc_get_function_entry(ptr noundef @foo) #3
+ auto *OriginalAddr = Builder.CreateIntrinsic(
+ /*RetTy*/ PtrTy, Intrinsic::ppc_get_function_entry, {F}, {},
+ "original.addr");
+
+ // %cmp = icmp eq ptr %AddrInDesc, %OriginalAddr
+ auto *CMP = Builder.CreateICmpEQ(AddrInDesc, OriginalAddr);
+
+ // br i1 %cmp, label %resolver, label %end
+ CurBlock = BasicBlock::Create(Ctx, "resolver", F);
+ BasicBlock *FinalBlock = BasicBlock::Create(Ctx, "end", F);
+ Builder.CreateCondBr(CMP, CurBlock, FinalBlock);
+
+ // Emit the 'then' (resolver) code:
+ Builder.SetInsertPoint(CurBlock);
+
+ // resolver:
+ // %ResolvedFunc = call ptr @resolver()
+ // %ResolvedAddr = call ptr @ppc_get_function_entry(ptr %ResolvedFunc)
+ // %4 = ptrtoint ptr %ResolvedAddr to i32
+ // store atomic i32 %4, ptr %desc_t.addr release, align 4
+ Function *ResolverFunc = IFunc.getResolverFunction();
+ auto *ResolvedFunc =
+ Builder.CreateCall(ResolverFunc, ArrayRef<Value *>(), "resolved.func");
+ auto *ResolvedAddr = Builder.CreateIntrinsic(
+ /*RetTy*/ PtrTy, Intrinsic::ppc_get_function_entry, {ResolvedFunc}, {},
+ "resolved.addr");
+ auto *PtrToInt = Builder.CreatePtrToInt(
+ ResolvedAddr, Builder.getIntPtrTy(M.getDataLayout()));
+ // TODO fix alignment
+ Builder.CreateAlignedStore(PtrToInt, DesctAddr, MaybeAlign())
+ ->setAtomic(AtomicOrdering::Release);
+
+ // br label %if.end
+ Builder.CreateBr(FinalBlock);
+
+ // Emit the continuation block for code after the if.
+ Builder.SetInsertPoint(FinalBlock);
+
+ // %res = musttail call i32 %DescPtr(i32 noundef %a) #3
+ SmallVector<Value *, 10> Args(make_pointer_range(F->args()));
+ CallInst *Result =
+ Builder.CreateCall(F->getFunctionType(), DescPtr, Args, "res");
+ // Result->setTailCallKind(CallInst::TCK_MustTail);
+
+ // ret i32 %res
+ if (F->getReturnType()->isVoidTy())
+ Builder.CreateRetVoid();
+ else
+ Builder.CreateRet(Result);
+
+ // replace all uses of the ifunc with the newly created function
+ IFunc.replaceAllUsesWith(F);
+
+ std::string name = IFunc.getName().str();
+ IFunc.eraseFromParent();
+ F->setName(name);
+ }
+}
bool llvm::lowerGlobalIFuncUsersAsGlobalCtor(
Module &M, ArrayRef<GlobalIFunc *> FilteredIFuncsToLower) {
SmallVector<GlobalIFunc *, 32> AllIFuncs;
>From a60e6b104cba91e39f7c04559277a14a96e9eb7f Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Tue, 12 Aug 2025 03:10:06 +0000
Subject: [PATCH 4/9] add clang codegen tests
---
clang/test/CodeGen/attr-ifunc.c | 2 ++
clang/test/CodeGen/attr-ifunc.cpp | 2 ++
clang/test/CodeGen/ifunc.c | 3 ++-
clang/test/SemaCXX/ifunc-has-attribute.cpp | 1 +
llvm/lib/Transforms/Utils/LowerIFunc.cpp | 5 ++++-
llvm/lib/Transforms/Utils/ModuleUtils.cpp | 2 +-
6 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/clang/test/CodeGen/attr-ifunc.c b/clang/test/CodeGen/attr-ifunc.c
index c9e70b17a8302..55d1866c17a69 100644
--- a/clang/test/CodeGen/attr-ifunc.c
+++ b/clang/test/CodeGen/attr-ifunc.c
@@ -4,6 +4,8 @@
// RUN: %clang_cc1 -triple x86_64-apple-macosx -verify -emit-llvm-only %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -verify -emit-llvm-only %s
// RUN: %clang_cc1 -triple aarch64-pc-windows-msvcu -verify -emit-llvm-only %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -verify -emit-llvm-only %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -verify -emit-llvm-only -DCHECK_ALIASES %s
#if defined(_WIN32) && !defined(__aarch64__)
void foo(void) {}
diff --git a/clang/test/CodeGen/attr-ifunc.cpp b/clang/test/CodeGen/attr-ifunc.cpp
index 9e6cd7312122d..601fad94530bd 100644
--- a/clang/test/CodeGen/attr-ifunc.cpp
+++ b/clang/test/CodeGen/attr-ifunc.cpp
@@ -1,9 +1,11 @@
// RUN: %clang_cc1 -triple x86_64-linux -verify -emit-llvm-only %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx -verify -emit-llvm-only %s
// RUN: %clang_cc1 -triple arm64-apple-macosx -verify -emit-llvm-only %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -verify -emit-llvm-only %s
// RUN: not %clang_cc1 -triple x86_64-linux -emit-llvm-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: not %clang_cc1 -triple x86_64-apple-macosx -emit-llvm-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
// RUN: not %clang_cc1 -triple arm64-apple-macosx -emit-llvm-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: not %clang_cc1 -triple powerpc64-ibm-aix-xcoff -emit-llvm-only -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
void *f1_ifunc(void) { return nullptr; }
void f1(void) __attribute__((ifunc("f1_ifunc")));
diff --git a/clang/test/CodeGen/ifunc.c b/clang/test/CodeGen/ifunc.c
index 7d21f742e8676..6aec451ff97aa 100644
--- a/clang/test/CodeGen/ifunc.c
+++ b/clang/test/CodeGen/ifunc.c
@@ -16,6 +16,7 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -O2 -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=thread -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsanitize=address -O2 -emit-llvm -o - %s | FileCheck %s --check-prefix=SAN
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix-xcoff -mllvm -lower-ifuncs=0 -emit-llvm -o - %s | FileCheck %s
/// The ifunc is emitted before its resolver.
@@ -65,7 +66,7 @@ extern void hoo(int) __attribute__ ((ifunc("hoo_ifunc")));
// AVR: @goo = ifunc void (), ptr addrspace(1) @goo_ifunc
// AVR: @hoo = ifunc void (i16), ptr addrspace(1) @hoo_ifunc
-// CHECK: call i32 @foo(i32
+// CHECK: call {{(signext )?}}i32 @foo(i32
// CHECK: call void @goo()
// SAN: define {{(dso_local )?}}noalias {{(noundef )?}}ptr @goo_ifunc() #[[#GOO_IFUNC:]] {
diff --git a/clang/test/SemaCXX/ifunc-has-attribute.cpp b/clang/test/SemaCXX/ifunc-has-attribute.cpp
index 242f3b621745f..913bc40ffee44 100644
--- a/clang/test/SemaCXX/ifunc-has-attribute.cpp
+++ b/clang/test/SemaCXX/ifunc-has-attribute.cpp
@@ -2,6 +2,7 @@
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-apple-macosx -verify %s -DSUPPORTED=1
// RUN: %clang_cc1 -emit-llvm-only -triple arm64-apple-macosx -verify %s -DSUPPORTED=1
// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-pc-win32 -verify %s -DNOT_SUPPORTED=1
+// RUN: %clang_cc1 -emit-llvm-only -triple powerpc64-ibm-aix-xcoff -verify %s -DSUPPORTED=1
// expected-no-diagnostics
diff --git a/llvm/lib/Transforms/Utils/LowerIFunc.cpp b/llvm/lib/Transforms/Utils/LowerIFunc.cpp
index 991890aa56b83..8c2bbce9215ef 100644
--- a/llvm/lib/Transforms/Utils/LowerIFunc.cpp
+++ b/llvm/lib/Transforms/Utils/LowerIFunc.cpp
@@ -13,14 +13,17 @@
#include "llvm/Transforms/Utils/LowerIFunc.h"
#include "llvm/IR/Module.h"
#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
#include "llvm/TargetParser/Triple.h"
#include "llvm/Transforms/Utils/ModuleUtils.h"
using namespace llvm;
+static cl::opt<bool> Enable("lower-ifuncs", cl::init(true), cl::Hidden);
+
/// Replace all call users of ifuncs in the module.
PreservedAnalyses LowerIFuncPass::run(Module &M, ModuleAnalysisManager &AM) {
- if (M.ifunc_empty())
+ if (M.ifunc_empty() || !Enable)
return PreservedAnalyses::all();
Triple TargetTriple(M.getTargetTriple());
diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
index 10e898bf266f0..b8ad15f7de6c8 100644
--- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -476,7 +476,7 @@ void llvm::lowerIFuncsOnAIX(Module &M) {
// %res = musttail call i32 %DescPtr(i32 noundef %a) #3
SmallVector<Value *, 10> Args(make_pointer_range(F->args()));
CallInst *Result =
- Builder.CreateCall(F->getFunctionType(), DescPtr, Args, "res");
+ Builder.CreateCall(F->getFunctionType(), DescPtr, Args);
// Result->setTailCallKind(CallInst::TCK_MustTail);
// ret i32 %res
>From 118f6008f309c140f86f60df67bfd6df74e144b3 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 13 Aug 2025 12:46:30 +0000
Subject: [PATCH 5/9] add simple codegen test
---
.../LowerIFunc/lower-ifunc-aix-simple.ll | 48 +++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll
diff --git a/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll b/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll
new file mode 100644
index 0000000000000..92e5231bf2cda
--- /dev/null
+++ b/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll
@@ -0,0 +1,48 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals --include-generated-funcs
+; RUN: opt < %s -passes=lower-ifunc -S --mtriple=powerpc64-ibm-aix-xcoff | FileCheck --check-prefixes=CHECK,CHECK-64 %s
+; RUN: opt < %s -passes=lower-ifunc -S --mtriple=powerpc-ibm-aix-xcoff | FileCheck --check-prefixes=CHECK,CHECK-32 %s
+
+
+ at ifunc_1 = ifunc void (), ptr @resolver1
+
+define ptr @resolver1() {
+ ret ptr null
+}
+
+; Test call to ifunc
+define void @call_ifunc(ptr %ptr) {
+ call void @ifunc_1()
+ ret void
+}
+; CHECK-LABEL: define ptr @resolver1(
+; CHECK-NEXT: ret ptr null
+;
+;
+; CHECK-LABEL: define void @call_ifunc(
+; CHECK-NEXT: call void @ifunc_1()
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define void @ifunc_1(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[DESC_PTR:%.*]] = call ptr @llvm.ppc.get.function.descriptor(ptr @ifunc_1)
+; CHECK-NEXT: [[DESC_T_ADDR:%.*]] = getelementptr inbounds nuw { ptr, ptr, ptr }, ptr [[DESC_PTR]], i32 0, i32 0
+; CHECK-NEXT: [[ADDR_IN_DESC:%.*]] = load ptr, ptr [[DESC_T_ADDR]], align 1
+; CHECK-NEXT: [[ORIGINAL_ADDR:%.*]] = call ptr @llvm.ppc.get.function.entry(ptr @ifunc_1)
+; CHECK-NEXT: [[TMP0:%.*]] = icmp eq ptr [[ADDR_IN_DESC]], [[ORIGINAL_ADDR]]
+; CHECK-NEXT: br i1 [[TMP0]], label [[RESOLVER:%.*]], label [[END:%.*]]
+; CHECK: resolver:
+; CHECK-NEXT: [[RESOLVED_FUNC:%.*]] = call ptr @resolver1()
+; CHECK-NEXT: [[RESOLVED_ADDR:%.*]] = call ptr @llvm.ppc.get.function.entry(ptr [[RESOLVED_FUNC]])
+; CHECK-64-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[RESOLVED_ADDR]] to i64
+; CHECK-64-NEXT: store atomic i64 [[TMP1]], ptr [[DESC_T_ADDR]] release, align 8
+; CHECK-32-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[RESOLVED_ADDR]] to i32
+; CHECK-32-NEXT: store atomic i32 [[TMP1]], ptr [[DESC_T_ADDR]] release, align 4
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: call void [[DESC_PTR]]()
+; CHECK-NEXT: ret void
+;
+;.
+; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(none) }
+;.
>From 2a68132256b03a0ae4400ef081a4e4a9e2b6e6a7 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 13 Aug 2025 17:12:24 +0000
Subject: [PATCH 6/9] enable more tests
---
.../Transforms/LowerIFunc/lower-ifunc-aix.ll | 114 ++++++++++++++++++
1 file changed, 114 insertions(+)
create mode 100644 llvm/test/Transforms/LowerIFunc/lower-ifunc-aix.ll
diff --git a/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix.ll b/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix.ll
new file mode 100644
index 0000000000000..f90a53e2a2850
--- /dev/null
+++ b/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix.ll
@@ -0,0 +1,114 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals --include-generated-funcs
+; RUN: opt < %S/lower-ifunc.ll -passes=lower-ifunc -S --mtriple=powerpc64-ibm-aix-xcoff | FileCheck %s
+
+;.
+; CHECK: @initialized_with_ifunc = global ptr @ifunc_constant_initializer_user
+;.
+; CHECK-LABEL: @resolver1(
+; CHECK-NEXT: ret ptr inttoptr (i64 123 to ptr)
+;
+;
+; CHECK-LABEL: @resolver2(
+; CHECK-NEXT: ret ptr inttoptr (i64 456 to ptr)
+;
+;
+; CHECK-LABEL: @resolver3(
+; CHECK-NEXT: ret ptr inttoptr (i64 789 to ptr)
+;
+;
+; CHECK-LABEL: @resolver4(
+; CHECK-NEXT: ret ptr inttoptr (i64 999 to ptr)
+;
+;
+; CHECK-LABEL: @resolver5(
+; CHECK-NEXT: ret ptr inttoptr (i64 420 to ptr)
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@call_ifunc(
+; CHECK-NEXT: call void @ifunc_1()
+; CHECK-NEXT: call void @ifunc_2()
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@store_ifunc_2(
+; CHECK-NEXT: store ptr @ifunc_2, ptr [[PTR:%.*]], align 8
+; CHECK-NEXT: store ptr [[PTR]], ptr @ifunc_2, align 8
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@call_ifunc_is_argument(
+; CHECK-NEXT: call void @other_func(ptr @ifunc_2)
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@call_ifunc_both_call_argument(
+; CHECK-NEXT: call void @ifunc_ptr_arg(ptr @ifunc_ptr_arg)
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@call_ifunc_nonvoid(
+; CHECK-NEXT: [[RET:%.*]] = call i32 @ifunc_nonvoid_0(double [[ARG:%.*]])
+; CHECK-NEXT: ret i32 [[RET]]
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@call_different_type_ifunc_nonvoid(
+; CHECK-NEXT: [[CAST_ARG:%.*]] = bitcast double [[ARG:%.*]] to i64
+; CHECK-NEXT: [[RET:%.*]] = call float @ifunc_nonvoid_0(i64 [[CAST_ARG]])
+; CHECK-NEXT: ret float [[RET]]
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@call_addrspacecast_callee_type_ifunc_nonvoid(
+; CHECK-NEXT: [[RET:%.*]] = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_1 to ptr addrspace(1))(double [[ARG:%.*]])
+; CHECK-NEXT: ret i32 [[RET]]
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@call_used_in_initializer(
+; CHECK-NEXT: [[RET:%.*]] = call i32 @ifunc_constant_initializer_user(double [[ARG:%.*]])
+; CHECK-NEXT: ret i32 [[RET]]
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@ifunc_1(
+; CHECK: [[DESC_PTR:%.*]] = call ptr @llvm.ppc.get.function.descriptor(ptr @ifunc_1)
+; CHECK: call void [[DESC_PTR]]()
+; CHECK-NEXT: ret void
+;
+; skip checking ifunc_2 because it's the same as ifunc_1
+;
+; CHECK-LABEL: define {{[^@]+}}@ifunc_no_users(
+; CHECK: [[DESC_PTR:%.*]] = call ptr @llvm.ppc.get.function.descriptor(ptr @ifunc_no_users)
+; CHECK: [[TMP3:%.*]] = call float [[DESC_PTR]](i64 [[TMP0:%.*]])
+; CHECK-NEXT: ret float [[TMP3]]
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@ifunc7(
+; CHECK: [[DESC_PTR:%.*]] = call ptr @llvm.ppc.get.function.descriptor(ptr @ifunc7)
+; CHECK: [[TMP3:%.*]] = call float [[DESC_PTR]](i64 [[TMP0:%.*]])
+; CHECK-NEXT: ret float [[TMP3]]
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@ifunc_ptr_arg(
+; CHECK: [[DESC_T_ADDR:%.*]] = getelementptr inbounds nuw { ptr, ptr, ptr }, ptr [[DESC_PTR]], i32 0, i32 0
+; CHECK: call void [[DESC_PTR]](ptr [[TMP0:%.*]])
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@ifunc_nonvoid_0(
+; CHECK: [[DESC_PTR:%.*]] = call ptr @llvm.ppc.get.function.descriptor(ptr @ifunc_nonvoid_0)
+; CHECK: [[TMP3:%.*]] = call i32 [[DESC_PTR]](double [[TMP0:%.*]])
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@ifunc_nonvoid_1(
+; CHECK: [[DESC_PTR:%.*]] = call ptr @llvm.ppc.get.function.descriptor(ptr @ifunc_nonvoid_1)
+; CHECK: [[TMP3:%.*]] = call i32 [[DESC_PTR]](double [[TMP0:%.*]])
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+;
+; CHECK-LABEL: define {{[^@]+}}@ifunc_constant_initializer_user(
+; CHECK: [[DESC_PTR:%.*]] = call ptr @llvm.ppc.get.function.descriptor(ptr @ifunc_constant_initializer_user)
+; CHECK: [[TMP3:%.*]] = call i32 [[DESC_PTR]](double [[TMP0:%.*]])
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+;.
+; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(none) }
+;.
>From 0462f585cdfdd8b65ea57af497c5c13d9269c55a Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 13 Aug 2025 19:23:12 +0000
Subject: [PATCH 7/9] fix alignment
---
llvm/lib/Transforms/Utils/ModuleUtils.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
index b8ad15f7de6c8..976b80c37c00f 100644
--- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -431,7 +431,7 @@ void llvm::lowerIFuncsOnAIX(Module &M) {
// %AddrInDesc = load ptr, ptr %DesctAddr, align 4
auto *AddrInDesc = Builder.CreateAlignedLoad(
- PtrTy, DesctAddr, DesctAddr->getPointerAlignment(M.getDataLayout()),
+ PtrTy, DesctAddr, MaybeAlign(),
"addr.in.desc");
// %OriginalAddr = call ptr @ppc_get_function_entry(ptr noundef @foo) #3
>From 342a004eb24639d782183d505d1870d5404c6787 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 13 Aug 2025 19:24:33 +0000
Subject: [PATCH 8/9] clang-format
---
llvm/lib/Transforms/Utils/ModuleUtils.cpp | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/ModuleUtils.cpp b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
index 976b80c37c00f..0b73eb5a952b3 100644
--- a/llvm/lib/Transforms/Utils/ModuleUtils.cpp
+++ b/llvm/lib/Transforms/Utils/ModuleUtils.cpp
@@ -430,9 +430,8 @@ void llvm::lowerIFuncsOnAIX(Module &M) {
Builder.CreateStructGEP(DescriptorType, DescPtr, 0, "desc_t.addr");
// %AddrInDesc = load ptr, ptr %DesctAddr, align 4
- auto *AddrInDesc = Builder.CreateAlignedLoad(
- PtrTy, DesctAddr, MaybeAlign(),
- "addr.in.desc");
+ auto *AddrInDesc = Builder.CreateAlignedLoad(PtrTy, DesctAddr, MaybeAlign(),
+ "addr.in.desc");
// %OriginalAddr = call ptr @ppc_get_function_entry(ptr noundef @foo) #3
auto *OriginalAddr = Builder.CreateIntrinsic(
@@ -475,8 +474,7 @@ void llvm::lowerIFuncsOnAIX(Module &M) {
// %res = musttail call i32 %DescPtr(i32 noundef %a) #3
SmallVector<Value *, 10> Args(make_pointer_range(F->args()));
- CallInst *Result =
- Builder.CreateCall(F->getFunctionType(), DescPtr, Args);
+ CallInst *Result = Builder.CreateCall(F->getFunctionType(), DescPtr, Args);
// Result->setTailCallKind(CallInst::TCK_MustTail);
// ret i32 %res
>From d77843c87a030e0d52b2bf153e16fe35d678b557 Mon Sep 17 00:00:00 2001
From: Wael Yehia <wyehia at ca.ibm.com>
Date: Wed, 13 Aug 2025 20:49:15 +0000
Subject: [PATCH 9/9] update testcase
---
llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll b/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll
index 92e5231bf2cda..9ab8a7c880289 100644
--- a/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll
+++ b/llvm/test/Transforms/LowerIFunc/lower-ifunc-aix-simple.ll
@@ -27,7 +27,8 @@ define void @call_ifunc(ptr %ptr) {
; CHECK-NEXT: entry:
; CHECK-NEXT: [[DESC_PTR:%.*]] = call ptr @llvm.ppc.get.function.descriptor(ptr @ifunc_1)
; CHECK-NEXT: [[DESC_T_ADDR:%.*]] = getelementptr inbounds nuw { ptr, ptr, ptr }, ptr [[DESC_PTR]], i32 0, i32 0
-; CHECK-NEXT: [[ADDR_IN_DESC:%.*]] = load ptr, ptr [[DESC_T_ADDR]], align 1
+; CHECK-64-NEXT: [[ADDR_IN_DESC:%.*]] = load ptr, ptr [[DESC_T_ADDR]], align 8
+; CHECK-32-NEXT: [[ADDR_IN_DESC:%.*]] = load ptr, ptr [[DESC_T_ADDR]], align 4
; CHECK-NEXT: [[ORIGINAL_ADDR:%.*]] = call ptr @llvm.ppc.get.function.entry(ptr @ifunc_1)
; CHECK-NEXT: [[TMP0:%.*]] = icmp eq ptr [[ADDR_IN_DESC]], [[ORIGINAL_ADDR]]
; CHECK-NEXT: br i1 [[TMP0]], label [[RESOLVER:%.*]], label [[END:%.*]]
More information about the cfe-commits
mailing list