[llvm] 93faa23 - [PowerPC] Add Support for indirect calls on AIX.

Sean Fertile via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 13 17:07:54 PST 2019


Author: Sean Fertile
Date: 2019-12-13T20:07:00-05:00
New Revision: 93faa237da8ddeb9e2ad91980387bd4d633369a9

URL: https://github.com/llvm/llvm-project/commit/93faa237da8ddeb9e2ad91980387bd4d633369a9
DIFF: https://github.com/llvm/llvm-project/commit/93faa237da8ddeb9e2ad91980387bd4d633369a9.diff

LOG: [PowerPC] Add Support for indirect calls on AIX.

Extends the desciptor-based indirect call support for 32-bit codegen,
and enables indirect calls for AIX.

In-depth Description:
In a function descriptor based ABI, a function pointer points at a
descriptor structure as opposed to the function's entry point. The
descriptor takes the form of 3 pointers: 1 for the function's entry
point, 1 for the TOC anchor of the module containing the function
definition, and 1 for the environment pointer:

struct FunctionDescriptor {
  void *EntryPoint;
  void *TOCAnchor;
  void *EnvironmentPointer;
};

An indirect call has several steps of loading the the information from
the descriptor into the proper registers for setting up the call. Namely
it has to:

1) Save the caller's TOC pointer into the TOC save slot in the linkage
   area, and then load the callee's TOC pointer into the TOC register
   (GPR 2 on AIX).

2) Load the function descriptor's entry point into the count register.

3) Load the environment pointer into the environment pointer register
   (GPR 11 on AIX).

4) Perform the call by branching on count register.

5) Restore the caller's TOC pointer after returning from the indirect call.

A couple important caveats to the above:

- There is no way to directly load a value from memory into the count register.
  Instead we populate the count register by loading the entry point address into
  a gpr and then moving the gpr to the count register.

- The TOC restore has to come immediately after the branch on count register
  instruction (i.e., the 1st instruction executed after we return from the
  call). This is an implementation limitation. We could, in theory, schedule
  the restore elsewhere as long as no uses of the TOC pointer fall in between
  the call and the restore; however, to keep it simple, we insert a pseudo
  instruction that represents both the indirect branch instruction and the
  load instruction that restores the caller's TOC from the linkage area. As
  they flow through the compiler as a single pseudo instruction, nothing can be
  inserted between them and the caller's TOC is then valid at any use.

Differtential Revision: https://reviews.llvm.org/D70724

Added: 
    llvm/test/CodeGen/PowerPC/aix-indirect-call.ll
    llvm/test/CodeGen/PowerPC/aix-trampoline.ll

Modified: 
    llvm/lib/Target/PowerPC/P9InstrResources.td
    llvm/lib/Target/PowerPC/PPCFrameLowering.cpp
    llvm/lib/Target/PowerPC/PPCISelLowering.cpp
    llvm/lib/Target/PowerPC/PPCISelLowering.h
    llvm/lib/Target/PowerPC/PPCInstrFormats.td
    llvm/lib/Target/PowerPC/PPCInstrInfo.td
    llvm/lib/Target/PowerPC/PPCSubtarget.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/PowerPC/P9InstrResources.td b/llvm/lib/Target/PowerPC/P9InstrResources.td
index 7bb836494062..22e8217cc0eb 100644
--- a/llvm/lib/Target/PowerPC/P9InstrResources.td
+++ b/llvm/lib/Target/PowerPC/P9InstrResources.td
@@ -1318,6 +1318,7 @@ def : InstRW<[P9_BR_2C, DISP_BR_1C],
   BCLalways,
   BCLn,
   BCTRL8_LDinto_toc,
+  BCTRL_LWZinto_toc,
   BCn,
   CTRL_DEP
 )>;

diff  --git a/llvm/lib/Target/PowerPC/PPCFrameLowering.cpp b/llvm/lib/Target/PowerPC/PPCFrameLowering.cpp
index b62c2fea04d1..4c608520e265 100644
--- a/llvm/lib/Target/PowerPC/PPCFrameLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCFrameLowering.cpp
@@ -2441,10 +2441,6 @@ PPCFrameLowering::restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
 }
 
 unsigned PPCFrameLowering::getTOCSaveOffset() const {
-  if (Subtarget.isAIXABI())
-    // TOC save/restore is normally handled by the linker.
-    // Indirect calls should hit this limitation.
-    report_fatal_error("TOC save is not implemented on AIX yet.");
   return TOCSaveOffset;
 }
 

diff  --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
index fd3f726b3399..bbd647938c69 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp
@@ -3154,11 +3154,17 @@ SDValue PPCTargetLowering::LowerVACOPY(SDValue Op, SelectionDAG &DAG) const {
 
 SDValue PPCTargetLowering::LowerADJUST_TRAMPOLINE(SDValue Op,
                                                   SelectionDAG &DAG) const {
+  if (Subtarget.isAIXABI())
+    report_fatal_error("ADJUST_TRAMPOLINE operation is not supported on AIX.");
+
   return Op.getOperand(0);
 }
 
 SDValue PPCTargetLowering::LowerINIT_TRAMPOLINE(SDValue Op,
                                                 SelectionDAG &DAG) const {
+  if (Subtarget.isAIXABI())
+    report_fatal_error("INIT_TRAMPOLINE operation is not supported on AIX.");
+
   SDValue Chain = Op.getOperand(0);
   SDValue Trmp = Op.getOperand(1); // trampoline
   SDValue FPtr = Op.getOperand(2); // nested function
@@ -5209,34 +5215,48 @@ static void prepareDescriptorIndirectCall(SelectionDAG &DAG, SDValue &Callee,
 
   MachinePointerInfo MPI(CS ? CS.getCalledValue() : nullptr);
 
+  // Registers used in building the DAG.
+  const MCRegister EnvPtrReg = Subtarget.getEnvironmentPointerRegister();
+  const MCRegister TOCReg = Subtarget.getTOCPointerRegister();
+
+  // Offsets of descriptor members.
+  const unsigned TOCAnchorOffset = Subtarget.descriptorTOCAnchorOffset();
+  const unsigned EnvPtrOffset = Subtarget.descriptorEnvironmentPointerOffset();
+
+  const MVT RegVT = Subtarget.isPPC64() ? MVT::i64 : MVT::i32;
+  const unsigned Alignment = Subtarget.isPPC64() ? 8 : 4;
+
   // One load for the functions entry point address.
-  SDValue LoadFuncPtr = DAG.getLoad(MVT::i64, dl, LDChain, Callee, MPI,
-                                    /* Alignment = */ 8, MMOFlags);
+  SDValue LoadFuncPtr = DAG.getLoad(RegVT, dl, LDChain, Callee, MPI,
+                                    Alignment, MMOFlags);
 
   // One for loading the TOC anchor for the module that contains the called
   // function.
-  SDValue TOCOff = DAG.getIntPtrConstant(8, dl);
-  SDValue AddTOC = DAG.getNode(ISD::ADD, dl, MVT::i64, Callee, TOCOff);
+  SDValue TOCOff = DAG.getIntPtrConstant(TOCAnchorOffset, dl);
+  SDValue AddTOC = DAG.getNode(ISD::ADD, dl, RegVT, Callee, TOCOff);
   SDValue TOCPtr =
-      DAG.getLoad(MVT::i64, dl, LDChain, AddTOC, MPI.getWithOffset(8),
-                  /* Alignment = */ 8, MMOFlags);
+      DAG.getLoad(RegVT, dl, LDChain, AddTOC,
+                  MPI.getWithOffset(TOCAnchorOffset), Alignment, MMOFlags);
 
   // One for loading the environment pointer.
-  SDValue PtrOff = DAG.getIntPtrConstant(16, dl);
-  SDValue AddPtr = DAG.getNode(ISD::ADD, dl, MVT::i64, Callee, PtrOff);
+  SDValue PtrOff = DAG.getIntPtrConstant(EnvPtrOffset, dl);
+  SDValue AddPtr = DAG.getNode(ISD::ADD, dl, RegVT, Callee, PtrOff);
   SDValue LoadEnvPtr =
-      DAG.getLoad(MVT::i64, dl, LDChain, AddPtr, MPI.getWithOffset(16),
-                  /* Alignment = */ 8, MMOFlags);
+      DAG.getLoad(RegVT, dl, LDChain, AddPtr,
+                  MPI.getWithOffset(EnvPtrOffset), Alignment, MMOFlags);
+
 
   // Then copy the newly loaded TOC anchor to the TOC pointer.
-  SDValue TOCVal = DAG.getCopyToReg(Chain, dl, PPC::X2, TOCPtr, Glue);
+  SDValue TOCVal = DAG.getCopyToReg(Chain, dl, TOCReg, TOCPtr, Glue);
   Chain = TOCVal.getValue(0);
   Glue = TOCVal.getValue(1);
 
   // If the function call has an explicit 'nest' parameter, it takes the
   // place of the environment pointer.
+  assert((!hasNest || !Subtarget.isAIXABI()) &&
+         "Nest parameter is not supported on AIX.");
   if (!hasNest) {
-    SDValue EnvVal = DAG.getCopyToReg(Chain, dl, PPC::X11, LoadEnvPtr, Glue);
+    SDValue EnvVal = DAG.getCopyToReg(Chain, dl, EnvPtrReg, LoadEnvPtr, Glue);
     Chain = EnvVal.getValue(0);
     Glue = EnvVal.getValue(1);
   }
@@ -5265,27 +5285,29 @@ buildCallOperands(SmallVectorImpl<SDValue> &Ops, CallingConv::ID CallConv,
     Ops.push_back(Callee);
   else {
     assert(!isPatchPoint && "Patch point call are not indirect.");
-    if (Subtarget.isAIXABI())
-      report_fatal_error("Indirect call on AIX is not implemented.");
-
-    // For 64-bit ELF we have saved the TOC pointer to the linkage area on the
-    // stack (this would have  been done in `LowerCall_64SVR4`). The call
-    // instruction is a pseudo instruction that represents both the indirect
-    // branch and a load that restores the TOC pointer from the linkage area.
-    // The operand for the TOC restore is an add of the TOC save offset to the
-    // stack pointer. This must be the second operand: after the chain input but
-    // before any other variadic arguments.
-    if (Subtarget.is64BitELFABI()) {
-      SDValue StackPtr = DAG.getRegister(PPC::X1, MVT::i64);
+
+    // For the TOC based ABIs, we have saved the TOC pointer to the linkage area
+    // on the stack (this would have been done in `LowerCall_64SVR4` or
+    // `LowerCall_AIX`). The call instruction is a pseudo instruction that
+    // represents both the indirect branch and a load that restores the TOC
+    // pointer from the linkage area. The operand for the TOC restore is an add
+    // of the TOC save offset to the stack pointer. This must be the second
+    // operand: after the chain input but before any other variadic arguments.
+    if (Subtarget.is64BitELFABI() || Subtarget.isAIXABI()) {
+      const MCRegister StackPtrReg = Subtarget.getStackPointerRegister();
+
+      SDValue StackPtr = DAG.getRegister(StackPtrReg, RegVT);
       unsigned TOCSaveOffset = Subtarget.getFrameLowering()->getTOCSaveOffset();
       SDValue TOCOff = DAG.getIntPtrConstant(TOCSaveOffset, dl);
-      SDValue AddTOC = DAG.getNode(ISD::ADD, dl, MVT::i64, StackPtr, TOCOff);
+      SDValue AddTOC = DAG.getNode(ISD::ADD, dl, RegVT, StackPtr, TOCOff);
       Ops.push_back(AddTOC);
     }
 
     // Add the register used for the environment pointer.
     if (Subtarget.usesFunctionDescriptors() && !hasNest)
-      Ops.push_back(DAG.getRegister(PPC::X11, MVT::i64));
+      Ops.push_back(DAG.getRegister(Subtarget.getEnvironmentPointerRegister(),
+                                    RegVT));
+
 
     // Add CTR register as callee so a bctr can be emitted later.
     if (isTailCall)
@@ -5306,7 +5328,7 @@ buildCallOperands(SmallVectorImpl<SDValue> &Ops, CallingConv::ID CallConv,
   // no way to mark dependencies as implicit here.
   // We will add the R2/X2 dependency in EmitInstrWithCustomInserter.
   if ((Subtarget.is64BitELFABI() || Subtarget.isAIXABI()) && !isPatchPoint)
-    Ops.push_back(DAG.getRegister(IsPPC64 ? PPC::X2 : PPC::R2, RegVT));
+    Ops.push_back(DAG.getRegister(Subtarget.getTOCPointerRegister(), RegVT));
 
   // Add implicit use of CR bit 6 for 32-bit SVR4 vararg calls
   if (isVarArg && Subtarget.is32BitELFABI())
@@ -6962,9 +6984,6 @@ SDValue PPCTargetLowering::LowerCall_AIX(
   if (isVarArg || isPatchPoint)
     report_fatal_error("This call type is unimplemented on AIX.");
 
-  if (!isFunctionGlobalAddress(Callee) && !isa<ExternalSymbolSDNode>(Callee))
-    report_fatal_error("Handling of indirect call is unimplemented!");
-
   const PPCSubtarget& Subtarget =
       static_cast<const PPCSubtarget&>(DAG.getSubtarget());
   if (Subtarget.hasQPX())
@@ -7023,6 +7042,26 @@ SDValue PPCTargetLowering::LowerCall_AIX(
                          "unimplemented!");
   }
 
+  // For indirect calls, we need to save the TOC base to the stack for
+  // restoration after the call.
+  if (!isTailCall && !isPatchPoint &&
+      !isFunctionGlobalAddress(Callee) && !isa<ExternalSymbolSDNode>(Callee)) {
+    const MCRegister TOCBaseReg = Subtarget.getTOCPointerRegister();
+    const MCRegister StackPtrReg = Subtarget.getStackPointerRegister();
+    const MVT PtrVT = Subtarget.isPPC64() ? MVT::i64 : MVT::i32;
+    const unsigned TOCSaveOffset =
+        Subtarget.getFrameLowering()->getTOCSaveOffset();
+
+    setUsesTOCBasePtr(DAG);
+    SDValue Val = DAG.getCopyFromReg(Chain, dl, TOCBaseReg, PtrVT);
+    SDValue PtrOff = DAG.getIntPtrConstant(TOCSaveOffset, dl);
+    SDValue StackPtr = DAG.getRegister(StackPtrReg, PtrVT);
+    SDValue AddPtr = DAG.getNode(ISD::ADD, dl, PtrVT, StackPtr, PtrOff);
+    Chain = DAG.getStore(
+        Val.getValue(1), dl, Val, AddPtr,
+        MachinePointerInfo::getStack(DAG.getMachineFunction(), TOCSaveOffset));
+  }
+
   // Build a sequence of copy-to-reg nodes chained together with token chain
   // and flag operands which copy the outgoing args into the appropriate regs.
   SDValue InFlag;

diff  --git a/llvm/lib/Target/PowerPC/PPCISelLowering.h b/llvm/lib/Target/PowerPC/PPCISelLowering.h
index 612d1c6b3f26..34e371e613e8 100644
--- a/llvm/lib/Target/PowerPC/PPCISelLowering.h
+++ b/llvm/lib/Target/PowerPC/PPCISelLowering.h
@@ -174,7 +174,8 @@ namespace llvm {
       BCTRL,
 
       /// CHAIN,FLAG = BCTRL(CHAIN, ADDR, INFLAG) - The combination of a bctrl
-      /// instruction and the TOC reload required on SVR4 PPC64.
+      /// instruction and the TOC reload required on 64-bit ELF, 32-bit AIX
+      /// and 64-bit AIX.
       BCTRL_LOAD_TOC,
 
       /// Return with a flag operand, matched by 'blr'

diff  --git a/llvm/lib/Target/PowerPC/PPCInstrFormats.td b/llvm/lib/Target/PowerPC/PPCInstrFormats.td
index 96b9c9a119c0..ab61f73207d2 100644
--- a/llvm/lib/Target/PowerPC/PPCInstrFormats.td
+++ b/llvm/lib/Target/PowerPC/PPCInstrFormats.td
@@ -1529,6 +1529,29 @@ class XLForm_2_ext_and_DSForm_1<bits<6> opcode1, bits<10> xo1,
   let BH = 0;
 }
 
+class XLForm_2_ext_and_DForm_1<bits<6> opcode1, bits<10> xo1, bits<5> bo,
+                               bits<5> bi, bit lk, bits<6> opcode2, dag OOL,
+                               dag IOL, string asmstr, InstrItinClass itin,
+                               list<dag> pattern>
+  : I2<opcode1, opcode2, OOL, IOL, asmstr, itin> {
+
+  bits<5>  RST;
+  bits<21> D_RA;
+
+  let Pattern = pattern;
+
+  let Inst{6-10} = bo;
+  let Inst{11-15} = bi;
+  let Inst{16-18} = 0;
+  let Inst{19-20} = 0;  // Unused (BH)
+  let Inst{21-30} = xo1;
+  let Inst{31} = lk;
+
+  let Inst{38-42} = RST;
+  let Inst{43-47} = D_RA{20-16};  // Base Register
+  let Inst{48-63} = D_RA{15-0};   // Displacement
+}
+
 // 1.7.8 XFX-Form
 class XFXForm_1<bits<6> opcode, bits<10> xo, dag OOL, dag IOL, string asmstr,
                 InstrItinClass itin>

diff  --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.td b/llvm/lib/Target/PowerPC/PPCInstrInfo.td
index e3154f5cccde..c7f2c5505d12 100644
--- a/llvm/lib/Target/PowerPC/PPCInstrInfo.td
+++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.td
@@ -1645,6 +1645,15 @@ def TCRETURNri : PPCEmitTimePseudo<(outs), (ins CTRRC:$dst, i32imm:$offset),
                  "#TC_RETURNr $dst $offset",
                  []>;
 
+let isCall = 1, PPC970_Unit = 7, isCodeGenOnly = 1,
+    Defs = [LR, R2], Uses = [CTR, RM], RST = 2 in {
+  def BCTRL_LWZinto_toc:
+    XLForm_2_ext_and_DForm_1<19, 528, 20, 0, 1, 32, (outs),
+     (ins memri:$src), "bctrl\n\tlwz 2, $src", IIC_BrB,
+     [(PPCbctrl_load_toc iaddr:$src)]>, Requires<[In32BitMode]>;
+
+}
+
 
 let isCodeGenOnly = 1 in {
 

diff  --git a/llvm/lib/Target/PowerPC/PPCSubtarget.h b/llvm/lib/Target/PowerPC/PPCSubtarget.h
index fdda92f0b34d..6dff0c126ab5 100644
--- a/llvm/lib/Target/PowerPC/PPCSubtarget.h
+++ b/llvm/lib/Target/PowerPC/PPCSubtarget.h
@@ -358,6 +358,34 @@ class PPCSubtarget : public PPCGenSubtargetInfo {
     return isAIXABI() || (is64BitELFABI() && !isELFv2ABI());
   }
 
+  unsigned descriptorTOCAnchorOffset() const {
+    assert(usesFunctionDescriptors() &&
+           "Should only be called when the target uses descriptors.");
+    return IsPPC64 ? 8 : 4;
+  }
+
+  unsigned descriptorEnvironmentPointerOffset() const {
+    assert(usesFunctionDescriptors() &&
+           "Should only be called when the target uses descriptors.");
+    return IsPPC64 ? 16 : 8;
+  }
+
+  MCRegister getEnvironmentPointerRegister() const {
+    assert(usesFunctionDescriptors() &&
+           "Should only be called when the target uses descriptors.");
+     return IsPPC64 ? PPC::X11 : PPC::R11;
+  }
+
+  MCRegister getTOCPointerRegister() const {
+    assert((is64BitELFABI() || isAIXABI()) &&
+           "Should only be called when the target is a TOC based ABI.");
+    return IsPPC64 ? PPC::X2 : PPC::R2;
+  }
+
+  MCRegister getStackPointerRegister() const {
+    return IsPPC64 ? PPC::X1 : PPC::R1;
+  }
+
   bool isXRaySupported() const override { return IsPPC64 && IsLittleEndian; }
 };
 } // End llvm namespace

diff  --git a/llvm/test/CodeGen/PowerPC/aix-indirect-call.ll b/llvm/test/CodeGen/PowerPC/aix-indirect-call.ll
new file mode 100644
index 000000000000..a80a137ee419
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/aix-indirect-call.ll
@@ -0,0 +1,144 @@
+; RUN: llc -verify-machineinstrs -mcpu=pwr4 -mattr=-altivec \
+; RUN:  -mtriple powerpc-ibm-aix-xcoff -stop-after=machine-cp < %s | \
+; RUN: FileCheck --check-prefixes=CHECKMIR,MIR32 %s
+
+; RUN: llc -verify-machineinstrs -mcpu=pwr4 -mattr=-altivec \
+; RUN:  -mtriple powerpc-ibm-aix-xcoff < %s | \
+; RUN: FileCheck --check-prefixes=CHECKASM,ASMOBJ32,ASM32 %s
+
+; RUN: llc -verify-machineinstrs -mcpu=pwr4 -mattr=-altivec \
+; RUN:  -mtriple powerpc64-ibm-aix-xcoff -stop-after=machine-cp < %s | \
+; RUN: FileCheck --check-prefixes=CHECKMIR,MIR64 %s
+
+; RUN: llc -verify-machineinstrs -mcpu=pwr4 -mattr=-altivec \
+; RUN:  -mtriple powerpc64-ibm-aix-xcoff < %s | \
+; RUN: FileCheck --check-prefixes=CHECKASM,ASM64 %s
+
+; RUN: llc -verify-machineinstrs -mcpu=pwr4 -mattr=-altivec \
+; RUN: -mtriple powerpc-ibm-aix-xcoff  -filetype=obj < %s -o %t
+; RUN: llvm-objdump -d %t | FileCheck \
+; RUN: --check-prefixes=CHECKOBJ,ASMOBJ32,OBJ32 %s
+
+define signext i32 @callThroughPtr(i32 ()* nocapture) {
+  %2 = tail call signext i32 %0()
+  ret i32 %2
+}
+
+; CHECKMIR:   name:            callThroughPtr
+
+; MIR32:      liveins: $r3
+; MIR32:      ADJCALLSTACKDOWN 56, 0, implicit-def dead $r1, implicit $r1
+; MIR32-DAG:  STW $r2, 20, $r1
+; MIR32-DAG:  renamable $r11 = LWZ 8, renamable $r3 :: (dereferenceable invariant load 4 from %ir.0 + 8)
+; MIR32-DAG:  renamable $[[REG:r[0-9]+]] = LWZ 0, renamable $r3 :: (dereferenceable invariant load 4 from %ir.0)
+; MIR32-DAG:  $r2 = LWZ 4, killed renamable $r3 :: (dereferenceable invariant load 4 from %ir.0 + 4)
+; MIR32-DAG:  MTCTR killed renamable $[[REG]], implicit-def $ctr
+; MIR32-NEXT: BCTRL_LWZinto_toc 20, $r1, csr_aix32, implicit-def dead $lr, implicit-def dead $r2, implicit $ctr, implicit $rm, implicit $r11, implicit $r2, implicit-def $r1, implicit-def $r3
+; MIR32-NEXT: ADJCALLSTACKUP 56, 0, implicit-def dead $r1, implicit $r1
+
+; MIR64:      liveins: $x3
+; MIR64:      ADJCALLSTACKDOWN 112, 0, implicit-def dead $r1, implicit $r1
+; MIR64-DAG:  STD $x2, 40, $x1 :: (store 8 into stack + 40)
+; MIR64-DAG:  renamable $x11 = LD 16, renamable $x3 :: (dereferenceable invariant load 8 from %ir.0 + 16)
+; MIR64-DAG:  renamable $[[REG:x[0-9]+]] = LD 0, renamable $x3 :: (dereferenceable invariant load 8 from %ir.0)
+; MIR64-DAG:  $x2 = LD 8, killed renamable $x3 :: (dereferenceable invariant load 8 from %ir.0 + 8)
+; MIR64-DAG:  MTCTR8 killed renamable $[[REG]], implicit-def $ctr8
+; MIR64-NEXT: BCTRL8_LDinto_toc 40, $x1, csr_aix64, implicit-def dead $lr8, implicit-def dead $x2, implicit $ctr8, implicit $rm, implicit $x11, implicit $x2, implicit-def $r1, implicit-def $x3
+; MIR64-NEXT: ADJCALLSTACKUP 112, 0, implicit-def dead $r1, implicit $r1
+
+; CHECKASM-LABEL: .callThroughPtr:
+
+; ASM32:         stwu 1, -64(1)
+; ASM32-DAG:     lwz [[REG:[0-9]+]], 0(3)
+; ASM32-DAG:     stw 2, 20(1)
+; ASM32-DAG:     mtctr [[REG]]
+; ASM32-DAG:     lwz 11, 8(3)
+; ASM32-DAG:     lwz 2, 4(3)
+; ASM32-NEXT:    bctrl
+; ASM32-NEXT:    lwz 2, 20(1)
+; ASM32-NEXT:    addi 1, 1, 64
+
+; ASM64:            stdu 1, -112(1)
+; ASM64-DAG:        ld [[REG:[0-9]+]], 0(3)
+; ASM64-DAG:        std 2, 40(1)
+; ASM64-DAG:        mtctr [[REG]]
+; ASM64-DAG:        ld 11, 16(3)
+; ASM64-DAG:        ld 2, 8(3)
+; ASM64-NEXT:       bctrl
+; ASM64-NEXT:       ld 2, 40(1)
+; ASM64-NEXT:       addi 1, 1, 112
+
+; OBJ32-LABEL: .text:
+; OBJ32:                      stwu 1, -64(1)
+; OBJ32-DAG:                  lwz [[REG:[0-9]+]], 0(3)
+; OBJ32-DAG:                  stw 2, 20(1)
+; OBJ32-DAG:                  mtctr [[REG]]
+; OBJ32-DAG:                  lwz 11, 8(3)
+; OBJ32-DAG:                  lwz 2, 4(3)
+; OBJ32-NEXT:    4e 80 04 21  bctrl
+; OBJ32-NEXT:    80 41 00 14  lwz 2, 20(1)
+; OBJ32-NEXT:                 addi 1, 1, 64
+
+define void @callThroughPtrWithArgs(void (i32, i16, i64)* nocapture) {
+  tail call void %0(i32 signext 1, i16 zeroext 2, i64 3)
+  ret void
+}
+
+; CHECKMIR:   name:            callThroughPtrWithArgs
+
+; MIR32:      liveins: $r3
+; MIR32:      ADJCALLSTACKDOWN 56, 0, implicit-def dead $r1, implicit $r1
+; MIR32-DAG:  renamable $[[REG:r[0-9]+]] = LWZ 0, renamable $r3 :: (dereferenceable invariant load 4 from %ir.0)
+; MIR32-DAG:  MTCTR killed renamable $[[REG]], implicit-def $ctr
+; MIR32-DAG:  STW $r2, 20, $r1 :: (store 4 into stack + 20)
+; MIR32-DAG:  renamable $r11 = LWZ 8, renamable $r3 :: (dereferenceable invariant load 4 from %ir.0 + 8)
+; MIR32-DAG:  $r2 = LWZ 4, killed renamable $r3 :: (dereferenceable invariant load 4 from %ir.0 + 4)
+; MIR32-DAG:  $r3 = LI 1
+; MIR32-DAG:  $r4 = LI 2
+; MIR32-DAG:  $r5 = LI 0
+; MIR32-DAG:  $r6 = LI 3
+; MIR32-NEXT: BCTRL_LWZinto_toc 20, $r1, csr_aix32, implicit-def dead $lr, implicit-def dead $r2, implicit $ctr, implicit $rm, implicit $r11, implicit $r3, implicit $r4, implicit $r5, implicit $r6, implicit $r2, implicit-def $r1
+; MIR32-NEXT: ADJCALLSTACKUP 56, 0, implicit-def dead $r1, implicit $r1
+
+; MIR64:      liveins: $x3
+; MIR64:      ADJCALLSTACKDOWN 112, 0, implicit-def dead $r1, implicit $r1
+; MIR64-DAG:  renamable $[[REG:x[0-9]+]] = LD 0, renamable $x3 :: (dereferenceable invariant load 8 from %ir.0)
+; MIR64-DAG:  MTCTR8 killed renamable $[[REG]], implicit-def $ctr8
+; MIR64-DAG:  STD $x2, 40, $x1 :: (store 8 into stack + 40)
+; MIR64-DAG:  renamable $x11 = LD 16, renamable $x3 :: (dereferenceable invariant load 8 from %ir.0 + 16)
+; MIR64-DAG:  $x2 = LD 8, killed renamable $x3 :: (dereferenceable invariant load 8 from %ir.0 + 8)
+; MIR64-DAG:  $x3 = LI8 1
+; MIR64-DAG:  $x4 = LI8 2
+; MIR64-DAG:  $x5 = LI8 3
+; MIR64-NEXT: BCTRL8_LDinto_toc 40, $x1, csr_aix64, implicit-def dead $lr8, implicit-def dead $x2, implicit $ctr8, implicit $rm, implicit $x11, implicit $x3, implicit $x4, implicit $x5, implicit $x2, implicit-def $r1
+; MIR64-NEXT: ADJCALLSTACKUP 112, 0, implicit-def dead $r1, implicit $r1
+
+; CHECKASM-LABEL: .callThroughPtrWithArgs:
+; CHECKOBJ-LABEL: .callThroughPtrWithArgs:
+
+; ASMOBJ32:      stwu 1, -64(1)
+; ASMOBJ32-DAG:  lwz [[REG:[0-9]+]], 0(3)
+; ASMOBJ32-DAG:  li 5, 0
+; ASMOBJ32-DAG:  li 6, 3
+; ASMOBJ32-DAG:  stw 2, 20(1)
+; ASMOBJ32-DAG:  mtctr [[REG]]
+; ASMOBJ32-DAG:  li 4, 2
+; ASMOBJ32-DAG:  lwz 11, 8(3)
+; ASMOBJ32-DAG:  lwz 2, 4(3)
+; ASMOBJ32-DAG:  li 3, 1
+; ASMOBJ32-NEXT: bctrl
+; ASMOBJ32-NEXT: lwz 2, 20(1)
+; ASMOBJ32-NEXT: addi 1, 1, 64
+
+; ASM64:            stdu 1, -112(1)
+; ASM64-DAG:        ld [[REG:[0-9]+]], 0(3)
+; ASM64-DAG:        li 5, 3
+; ASM64-DAG:        std 2, 40(1)
+; ASM64-DAG:        mtctr [[REG]]
+; ASM64-DAG:        li 4, 2
+; ASM64-DAG:        ld 11, 16(3)
+; ASM64-DAG:        ld 2, 8(3)
+; ASM64-DAG:        li 3, 1
+; ASM64-NEXT:       bctrl
+; ASM64-NEXT:       ld 2, 40(1)
+; ASM64-NEXT:       addi 1, 1, 112

diff  --git a/llvm/test/CodeGen/PowerPC/aix-trampoline.ll b/llvm/test/CodeGen/PowerPC/aix-trampoline.ll
new file mode 100644
index 000000000000..5c45dc588969
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/aix-trampoline.ll
@@ -0,0 +1,14 @@
+; RUN: not llc -mtriple powerpc-ibm-aix-xcoff < %s 2>&1 | FileCheck %s
+; RUN: not llc -mtriple powerpc64-ibm-aix-xcoff < %s 2>&1 | FileCheck %s
+
+; CHECK: LLVM ERROR: INIT_TRAMPOLINE operation is not supported on AIX.
+
+define void @create_trampoline(i8* %buffer, i8* %nval) nounwind {
+entry:
+  call void @llvm.init.trampoline(i8* %buffer, i8* bitcast (i32 (i32)* @nested to i8*) , i8* %nval)
+  ret void
+}
+
+declare i32 @nested(i32);
+
+declare void @llvm.init.trampoline(i8*, i8*, i8*) nounwind


        


More information about the llvm-commits mailing list