diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -5115,6 +5115,15 @@ return PPCISD::CALL; } + +static bool isValidAIXExternalSymSDNode(StringRef SymName) { + return StringSwitch(SymName) + .Cases("__divdi3", "__fixunsdfdi", "__floatdidf", "__floatundidf", + "__floatundisf", "__moddi3", "__udivdi3", "__umoddi3", true) + .Cases("ceil", "floor", "memcpy", "memmove", "memset", "round", true) + .Default(false); +} + static SDValue transformCallee(const SDValue &Callee, SelectionDAG &DAG, const SDLoc &dl, const PPCSubtarget &Subtarget) { if (!Subtarget.usesFunctionDescriptors() && !Subtarget.isELFv2ABI()) @@ -5139,42 +5148,70 @@ Subtarget.is32BitELFABI() && !isLocalCallee() && Subtarget.getTargetMachine().getRelocationModel() == Reloc::PIC_; + // On AIX, direct function calls reference the symbol for the function's + // entry point, which is named by prepending a "." before the function's + // C-linkage name. + const auto getAIXFuncEntryPointSymbol = + [&](StringRef FuncName, bool IsDeclaration, + const XCOFF::StorageClass &SC) { + auto &Context = DAG.getMachineFunction().getMMI().getContext(); + + MCSymbolXCOFF *S = cast( + Context.getOrCreateSymbol(Twine(".") + Twine(FuncName))); + + if (IsDeclaration && !S->hasContainingCsect()) { + // On AIX, an undefined symbol needs to be associated with a + // MCSectionXCOFF to get the correct storage mapping class. + // In this case, XCOFF::XMC_PR. + MCSectionXCOFF *Sec = Context.getXCOFFSection( + S->getName(), XCOFF::XMC_PR, XCOFF::XTY_ER, SC, + SectionKind::getMetadata()); + S->setContainingCsect(Sec); + } + + // Replace the callee SDNode with the MCSymbolSDNode. + MVT PtrVT = + DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); + return DAG.getMCSymbol(S, PtrVT); + }; + if (isFunctionGlobalAddress(Callee)) { const GlobalAddressSDNode *G = cast(Callee); + const GlobalObject *GO = cast(G->getGlobal()); + if (!Subtarget.isAIXABI()) - return DAG.getTargetGlobalAddress(G->getGlobal(), dl, - Callee.getValueType(), 0, + return DAG.getTargetGlobalAddress(GO, dl, Callee.getValueType(), 0, UsePlt ? PPCII::MO_PLT : 0); - // On AIX, direct function calls reference the symbol for the function's - // entry point, which is named by prepending a "." before the function's - // C-linkage name. - auto &Context = DAG.getMachineFunction().getMMI().getContext(); + const XCOFF::StorageClass SC = + TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GO); + return getAIXFuncEntryPointSymbol(GO->getName(), GO->isDeclaration(), SC); + } - const GlobalObject *GO = cast(G->getGlobal()); - MCSymbolXCOFF *S = cast( - Context.getOrCreateSymbol(Twine(".") + Twine(GO->getName()))); + if (ExternalSymbolSDNode *S = dyn_cast(Callee)) { + const char *SymName = S->getSymbol(); + if (!Subtarget.isAIXABI()) + return DAG.getTargetExternalSymbol(SymName, Callee.getValueType(), + UsePlt ? PPCII::MO_PLT : 0); - if (GO && GO->isDeclaration() && !S->hasContainingCsect()) { - // On AIX, an undefined symbol needs to be associated with a - // MCSectionXCOFF to get the correct storage mapping class. - // In this case, XCOFF::XMC_PR. - const XCOFF::StorageClass SC = - TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GO); - MCSectionXCOFF *Sec = - Context.getXCOFFSection(S->getName(), XCOFF::XMC_PR, XCOFF::XTY_ER, - SC, SectionKind::getMetadata()); - S->setContainingCsect(Sec); + // If there exists a user-declared function whose name is the same as the + // ExternalSymbol's, then we pick up the user-declared version. + const Module *Mod = DAG.getMachineFunction().getFunction().getParent(); + if (const GlobalValue *GV = Mod->getNamedValue(SymName)) + if (const Function *F = dyn_cast(GV)) { + const XCOFF::StorageClass SC = + TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(F); + return getAIXFuncEntryPointSymbol(F->getName(), F->isDeclaration(), SC); + } + + // TODO: Remove this when the support for ExternalSymbolSDNode is complete. + if (isValidAIXExternalSymSDNode(SymName)) { + return getAIXFuncEntryPointSymbol(SymName, true, XCOFF::C_EXT); } - EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); - return DAG.getMCSymbol(S, PtrVT); + report_fatal_error("Unexpected ExternalSymbolSDNode: " + Twine(SymName)); } - if (ExternalSymbolSDNode *S = dyn_cast(Callee)) - return DAG.getTargetExternalSymbol(S->getSymbol(), Callee.getValueType(), - UsePlt ? PPCII::MO_PLT : 0); - // No transformation needed. assert(Callee.getNode() && "What no callee?"); return Callee; diff --git a/llvm/test/CodeGen/PowerPC/aix-external-sym-sdnode-lowering.ll b/llvm/test/CodeGen/PowerPC/aix-external-sym-sdnode-lowering.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-external-sym-sdnode-lowering.ll @@ -0,0 +1,128 @@ +; RUN: llc -mcpu=pwr4 -mattr=-altivec -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff \ +; RUN: -stop-after=machine-cp < %s | FileCheck \ +; RUN: --check-prefix=32BIT %s + +; RUN: llc -mcpu=pwr4 -mattr=-altivec -verify-machineinstrs -mtriple powerpc64-ibm-aix-xcoff \ +; RUN: -stop-after=machine-cp < %s | FileCheck \ +; RUN: --check-prefix=64BIT %s + +define i64 @call_divdi3(i64 %p, i64 %num) { +entry: + %div = sdiv i64 %p, %num + ret i64 %div +} + +; 32BIT: BL_NOP + +define i64 @call_fixunsdfdi(double %p) { +entry: + %conv = fptoui double %p to i64 + ret i64 %conv +} + +; 32BIT: BL_NOP + +define double @call_floatundidf(i64 %p) { +entry: + %conv = uitofp i64 %p to double + ret double %conv +} + +; 32BIT: BL_NOP + +define float @call_floatundisf(i64 %p) { +entry: + %conv = uitofp i64 %p to float + ret float %conv +} + +; 32BIT: BL_NOP + +define i64 @call_moddi3(i64 %p, i64 %num) { +entry: + %rem = srem i64 %p, %num + ret i64 %rem +} + +; 32BIT: BL_NOP + +define i64 @call_udivdi3(i64 %p, i64 %q) { + %1 = udiv i64 %p, %q + ret i64 %1 +} + +; 32BIT: BL_NOP + +define i64 @call_umoddi3(i64 %p, i64 %num) { +entry: + %rem = urem i64 %p, %num + ret i64 %rem +} + +; 32BIT: BL_NOP + +define double @call_ceil(double %n) { +entry: + %0 = call double @llvm.ceil.f64(double %n) + ret double %0 +} + +declare double @llvm.ceil.f64(double) + +; 32BIT: BL_NOP +; 64BIT: BL8_NOP + +define double @call_floor(double %n) { +entry: + %0 = call double @llvm.floor.f64(double %n) + ret double %0 +} + +declare double @llvm.floor.f64(double) + +; 32BIT: BL_NOP +; 64BIT: BL8_NOP + +define void @call_memcpy(i8* %p, i8* %q, i32 %n) { +entry: + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %p, i8* %q, i32 %n, i1 false) + ret void +} + +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture writeonly, i8* nocapture readonly, i32, i1) + +; 32BIT: BL_NOP +; 64BIT: BL8_NOP + +define void @call_memmove(i8* %p, i8* %q, i32 %n) { +entry: + call void @llvm.memmove.p0i8.p0i8.i32(i8* %p, i8* %q, i32 %n, i1 false) + ret void +} + +declare void @llvm.memmove.p0i8.p0i8.i32(i8* nocapture, i8* nocapture readonly, i32, i1) + +; 32BIT: BL_NOP +; 64BIT: BL8_NOP + +define void @call_memset(i8* %p, i8 %q, i32 %n) #0 { +entry: + call void @llvm.memset.p0i8.i32(i8* %p, i8 %q, i32 %n, i1 false) + ret void +} + +declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i1) + +; 32BIT: BL_NOP +; 64BIT: BL8_NOP + +define double @call_round(double %n) { +entry: + %0 = call double @llvm.round.f64(double %n) + ret double %0 +} + +declare double @llvm.round.f64(double) + +; 32BIT: BL_NOP +; 64BIT: BL8_NOP diff --git a/llvm/test/CodeGen/PowerPC/aix-user-defined-memcpy.ll b/llvm/test/CodeGen/PowerPC/aix-user-defined-memcpy.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/aix-user-defined-memcpy.ll @@ -0,0 +1,57 @@ +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr4 \ +; RUN: -mattr=-altivec -filetype=obj -o %t.o < %s | llvm-readobj --syms %t.o | \ +; RUN: FileCheck --check-prefix=32-SYM %s + +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr4 \ +; RUN: -mattr=-altivec -filetype=obj -o %t.o < %s | not llvm-readobj --r %t.o \ +; RUN: 2>&1 | FileCheck --check-prefix=32-REL %s + +; RUN: not llc -verify-machineinstrs -mtriple powerpc64-ibm-aix-xcoff \ +; RUN: -mcpu=pwr4 -mattr=-altivec -filetype=obj < %s 2>&1 | FileCheck \ +; RUN: --check-prefix=64-CHECK %s + +; Test verifies: +; If there exists a user-defined function whose name is the same as the +; "memcpy" ExternalSymbol's, we pick up the user-defined version, even if this +; may lead to some undefined behavior. + +define dso_local signext i32 @memcpy(i8* %destination, i32 signext %num) { +entry: + ret i32 3 +} + +define void @call_memcpy(i8* %p, i8* %q, i32 %n) { +entry: + call void @llvm.memcpy.p0i8.p0i8.i32(i8* %p, i8* %q, i32 %n, i1 false) + ret void +} + +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* nocapture writeonly, i8* nocapture readonly, i32, i1) + +; TODO: This test should preferably check the symbol table for a.o file and +; the relocation associated with the call. + +; 32-SYM: Symbol { +; 32-SYM: Index: 2 +; 32-SYM-NEXT: Name: .memcpy +; 32-SYM-NEXT: Value (RelocatableAddress): 0x0 +; 32-SYM-NEXT: Section: .text +; 32-SYM-NEXT: Type: 0x0 +; 32-SYM-NEXT: StorageClass: C_EXT (0x2) +; 32-SYM-NEXT: NumberOfAuxEntries: 1 +; 32-SYM-NEXT: CSECT Auxiliary Entry { +; 32-SYM-NEXT: Index: 3 +; 32-SYM-NEXT: ContainingCsectSymbolIndex: 0 +; 32-SYM-NEXT: ParameterHashIndex: 0x0 +; 32-SYM-NEXT: TypeChkSectNum: 0x0 +; 32-SYM-NEXT: SymbolAlignmentLog2: 0 +; 32-SYM-NEXT: SymbolType: XTY_LD (0x2) +; 32-SYM-NEXT: StorageMappingClass: XMC_PR (0x0) +; 32-SYM-NEXT: StabInfoIndex: 0x0 +; 32-SYM-NEXT: StabSectNum: 0x0 +; 32-SYM-NEXT: } +; 32-SYM-NEXT: } + +; 32-REL: LLVM ERROR: Unexpanded relocation output not implemented. + +; 64-CHECK: LLVM ERROR: 64-bit XCOFF object files are not supported yet.