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 @@ -5099,6 +5099,16 @@ return PPCISD::CALL; } + +static bool isValidAIXExternalSymSDNode(StringRef SymName) { + return StringSwitch(SymName) + .Cases("__divdi3", "__fixdfdi", "__fixunsdfdi", "__floatdidf", + "__floatdisf", "__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()) @@ -5123,42 +5133,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. + // If there exists user-defined function whose name is the same as the + // ExternalSymbol's, we pick up user-defined version. + const Module *Mod = DAG.getMachineFunction().getFunction().getParent(); + if (const GlobalValue *GV = Mod->getNamedValue(SymName)) { + const GlobalObject *GO = dyn_cast(GV); 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); - } - - EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout()); - return DAG.getMCSymbol(S, PtrVT); + return getAIXFuncEntryPointSymbol(GO->getName(), GO->isDeclaration(), SC); + } else { + // TODO: Remove this when the support for ExternalSymbolSDNode is complete. + if (isValidAIXExternalSymSDNode(SymName)) { + return getAIXFuncEntryPointSymbol(SymName, true, XCOFF::C_EXT); + } else { + 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,152 @@ +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff \ +; RUN: -stop-after=machine-cp < %s | FileCheck \ +; RUN: --check-prefix=32BIT %s + +; RUN: llc -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_fixdfdi(double %p) { +entry: + %conv = fptosi double %p to i64 + ret i64 %conv +} + +; 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_floatdidf(i64 %p) { +entry: + %conv = sitofp i64 %p to double + ret double %conv +} + +; 32BIT: BL_NOP + +define float @call_floatdisf(i64 %p) { +entry: + %conv = sitofp i64 %p to float + ret float %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,22 @@ +; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr4 \ +; RUN: -mattr=-altivec < %s | FileCheck --check-prefix=CHECK %s + +; RUN: llc -verify-machineinstrs -mtriple powerpc64-ibm-aix-xcoff -mcpu=pwr4 \ +; RUN: -mattr=-altivec < %s | FileCheck --check-prefix=CHECK %s + +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) + +; CHECK: bl .memcpy +; CHECK-NOT: error +; CHECK-NOT: warning