forked from OSchip/llvm-project
[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:
parent
4b5bc38802
commit
25a8aec7f3
|
@ -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,41 +5150,72 @@ static SDValue transformCallee(const SDValue &Callee, SelectionDAG &DAG,
|
|||
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 getAIXFuncEntryPointSymbolSDNode =
|
||||
[&](StringRef FuncName, bool IsDeclaration,
|
||||
const XCOFF::StorageClass &SC) {
|
||||
auto &Context = DAG.getMachineFunction().getMMI().getContext();
|
||||
|
||||
MCSymbolXCOFF *S = cast<MCSymbolXCOFF>(
|
||||
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);
|
||||
}
|
||||
|
||||
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(G->getGlobal(), dl,
|
||||
Callee.getValueType(), 0,
|
||||
return DAG.getTargetGlobalAddress(GV, 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 GlobalObject *GO = cast<GlobalObject>(G->getGlobal());
|
||||
MCSymbolXCOFF *S = cast<MCSymbolXCOFF>(
|
||||
Context.getOrCreateSymbol(Twine(".") + Twine(GO->getName())));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
EVT PtrVT = DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout());
|
||||
return DAG.getMCSymbol(S, PtrVT);
|
||||
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(),
|
||||
UsePlt ? PPCII::MO_PLT : 0);
|
||||
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?");
|
||||
|
|
|
@ -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>
|
|
@ -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.
|
Loading…
Reference in New Issue