[AIX] ExternalSymbolSDNode lowering

For memcpy/memset/memmove etc., replace ExternalSymbolSDNode with a
MCSymbolSDNode, which have a prefix dot before function name as entry
point symbol.

Differential Revision: https://reviews.llvm.org/D70718
This commit is contained in:
Xiangling Liao 2020-01-07 10:53:24 -05:00
parent 4b5bc38802
commit 25a8aec7f3
3 changed files with 254 additions and 28 deletions

View File

@ -5117,6 +5117,15 @@ static unsigned getCallOpcode(bool isIndirectCall, bool isPatchPoint,
return PPCISD::CALL;
}
static bool isValidAIXExternalSymSDNode(StringRef SymName) {
return StringSwitch<bool>(SymName)
.Cases("__divdi3", "__fixunsdfdi", "__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())
@ -5141,42 +5150,73 @@ static SDValue transformCallee(const SDValue &Callee, SelectionDAG &DAG,
Subtarget.is32BitELFABI() && !isLocalCallee() &&
Subtarget.getTargetMachine().getRelocationModel() == Reloc::PIC_;
if (isFunctionGlobalAddress(Callee)) {
const GlobalAddressSDNode *G = cast<GlobalAddressSDNode>(Callee);
if (!Subtarget.isAIXABI())
return DAG.getTargetGlobalAddress(G->getGlobal(), 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.
const auto getAIXFuncEntryPointSymbolSDNode =
[&](StringRef FuncName, bool IsDeclaration,
const XCOFF::StorageClass &SC) {
auto &Context = DAG.getMachineFunction().getMMI().getContext();
const GlobalObject *GO = cast<GlobalObject>(G->getGlobal());
MCSymbolXCOFF *S = cast<MCSymbolXCOFF>(
Context.getOrCreateSymbol(Twine(".") + Twine(GO->getName())));
Context.getOrCreateSymbol(Twine(".") + Twine(FuncName)));
if (GO && GO->isDeclaration() && !S->hasContainingCsect()) {
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.
const XCOFF::StorageClass SC =
TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GO);
MCSectionXCOFF *Sec =
Context.getXCOFFSection(S->getName(), XCOFF::XMC_PR, XCOFF::XTY_ER,
SC, SectionKind::getMetadata());
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());
MVT PtrVT =
DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout());
return DAG.getMCSymbol(S, PtrVT);
};
if (isFunctionGlobalAddress(Callee)) {
const GlobalAddressSDNode *G = cast<GlobalAddressSDNode>(Callee);
const GlobalValue *GV = G->getGlobal();
if (!Subtarget.isAIXABI())
return DAG.getTargetGlobalAddress(GV, dl, Callee.getValueType(), 0,
UsePlt ? PPCII::MO_PLT : 0);
assert(!isa<GlobalIFunc>(GV) && "IFunc is not supported on AIX.");
const GlobalObject *GO = cast<GlobalObject>(GV);
const XCOFF::StorageClass SC =
TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(GO);
return getAIXFuncEntryPointSymbolSDNode(GO->getName(), GO->isDeclaration(),
SC);
}
if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee))
return DAG.getTargetExternalSymbol(S->getSymbol(), Callee.getValueType(),
if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) {
const char *SymName = S->getSymbol();
if (!Subtarget.isAIXABI())
return DAG.getTargetExternalSymbol(SymName, Callee.getValueType(),
UsePlt ? PPCII::MO_PLT : 0);
// 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 Function *F =
dyn_cast_or_null<Function>(Mod->getNamedValue(SymName))) {
const XCOFF::StorageClass SC =
TargetLoweringObjectFileXCOFF::getStorageClassForGlobal(F);
return getAIXFuncEntryPointSymbolSDNode(F->getName(), F->isDeclaration(),
SC);
}
// TODO: Remove this when the support for ExternalSymbolSDNode is complete.
if (isValidAIXExternalSymSDNode(SymName)) {
return getAIXFuncEntryPointSymbolSDNode(SymName, true, XCOFF::C_EXT);
}
report_fatal_error("Unexpected ExternalSymbolSDNode: " + Twine(SymName));
}
// No transformation needed.
assert(Callee.getNode() && "What no callee?");
return Callee;

View File

@ -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 <mcsymbol .__divdi3>
define i64 @call_fixunsdfdi(double %p) {
entry:
%conv = fptoui double %p to i64
ret i64 %conv
}
; 32BIT: BL_NOP <mcsymbol .__fixunsdfdi>
define double @call_floatundidf(i64 %p) {
entry:
%conv = uitofp i64 %p to double
ret double %conv
}
; 32BIT: BL_NOP <mcsymbol .__floatundidf>
define float @call_floatundisf(i64 %p) {
entry:
%conv = uitofp i64 %p to float
ret float %conv
}
; 32BIT: BL_NOP <mcsymbol .__floatundisf>
define i64 @call_moddi3(i64 %p, i64 %num) {
entry:
%rem = srem i64 %p, %num
ret i64 %rem
}
; 32BIT: BL_NOP <mcsymbol .__moddi3>
define i64 @call_udivdi3(i64 %p, i64 %q) {
%1 = udiv i64 %p, %q
ret i64 %1
}
; 32BIT: BL_NOP <mcsymbol .__udivdi3>
define i64 @call_umoddi3(i64 %p, i64 %num) {
entry:
%rem = urem i64 %p, %num
ret i64 %rem
}
; 32BIT: BL_NOP <mcsymbol .__umoddi3>
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 <mcsymbol .ceil>
; 64BIT: BL8_NOP <mcsymbol .ceil>
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 <mcsymbol .floor>
; 64BIT: BL8_NOP <mcsymbol .floor>
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 <mcsymbol .memcpy>
; 64BIT: BL8_NOP <mcsymbol .memcpy>
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 <mcsymbol .memmove>
; 64BIT: BL8_NOP <mcsymbol .memmove>
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 <mcsymbol .memset>
; 64BIT: BL8_NOP <mcsymbol .memset>
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 <mcsymbol .round>
; 64BIT: BL8_NOP <mcsymbol .round>

View File

@ -0,0 +1,58 @@
; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr4 \
; RUN: -mattr=-altivec -filetype=obj -o %t.o < %s
; RUN: llvm-readobj --syms %t.o | FileCheck --check-prefix=32-SYM %s
; RUN: llvm-readobj --relocs --expand-relocs %t.o | FileCheck \
; RUN: --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 .o file and
; the relocation associated with the call.
; 32-SYM: Symbol {{[{][[:space:]] *}}Index: [[#Index:]]{{[[:space:]] *}}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-SYM-NOT: .memcpy
; We are expecting to have the test fail when the support for relocations land.
; 32-REL-NOT: Relocation{{[[:space:]]}}
; 64-CHECK: LLVM ERROR: 64-bit XCOFF object files are not supported yet.