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 <wye...@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 <wye...@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 <wye...@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 <wye...@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 <wye...@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 + + +@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 <wye...@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 <wye...@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 <wye...@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 <wye...@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:%.*]] _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits