Commit 25a8aec7 authored by Xiangling Liao's avatar Xiangling Liao
Browse files

[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
parent 4b5bc388
Loading
Loading
Loading
Loading
+64 −24
Original line number Diff line number Diff line
@@ -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;
+128 −0
Original line number Diff line number Diff line
; 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>
+58 −0
Original line number Diff line number Diff line
; 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.