summaryrefslogtreecommitdiffstats
path: root/llvm
diff options
context:
space:
mode:
Diffstat (limited to 'llvm')
-rw-r--r--llvm/lib/Target/SystemZ/SystemZCallingConv.cpp4
-rw-r--r--llvm/lib/Target/SystemZ/SystemZCallingConv.h50
-rw-r--r--llvm/lib/Target/SystemZ/SystemZCallingConv.td3
-rw-r--r--llvm/lib/Target/SystemZ/SystemZISelLowering.cpp48
-rw-r--r--llvm/test/CodeGen/SystemZ/args-09.ll53
-rw-r--r--llvm/test/CodeGen/SystemZ/args-10.ll50
6 files changed, 198 insertions, 10 deletions
diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.cpp b/llvm/lib/Target/SystemZ/SystemZCallingConv.cpp
index cc9c84b6a05..72da51f74b1 100644
--- a/llvm/lib/Target/SystemZ/SystemZCallingConv.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.cpp
@@ -12,10 +12,10 @@
using namespace llvm;
-const unsigned SystemZ::ArgGPRs[SystemZ::NumArgGPRs] = {
+const MCPhysReg SystemZ::ArgGPRs[SystemZ::NumArgGPRs] = {
SystemZ::R2D, SystemZ::R3D, SystemZ::R4D, SystemZ::R5D, SystemZ::R6D
};
-const unsigned SystemZ::ArgFPRs[SystemZ::NumArgFPRs] = {
+const MCPhysReg SystemZ::ArgFPRs[SystemZ::NumArgFPRs] = {
SystemZ::F0D, SystemZ::F2D, SystemZ::F4D, SystemZ::F6D
};
diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.h b/llvm/lib/Target/SystemZ/SystemZCallingConv.h
index bff0706618a..b5523e586f4 100644
--- a/llvm/lib/Target/SystemZ/SystemZCallingConv.h
+++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.h
@@ -12,14 +12,15 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/CallingConvLower.h"
+#include "llvm/MC/MCRegisterInfo.h"
namespace llvm {
namespace SystemZ {
const unsigned NumArgGPRs = 5;
- extern const unsigned ArgGPRs[NumArgGPRs];
+ extern const MCPhysReg ArgGPRs[NumArgGPRs];
const unsigned NumArgFPRs = 4;
- extern const unsigned ArgFPRs[NumArgFPRs];
+ extern const MCPhysReg ArgFPRs[NumArgFPRs];
} // end namespace SystemZ
class SystemZCCState : public CCState {
@@ -79,6 +80,51 @@ public:
bool IsShortVector(unsigned ValNo) { return ArgIsShortVector[ValNo]; }
};
+// Handle i128 argument types. These need to be passed by implicit
+// reference. This could be as simple as the following .td line:
+// CCIfType<[i128], CCPassIndirect<i64>>,
+// except that i128 is not a legal type, and therefore gets split by
+// common code into a pair of i64 arguments.
+inline bool CC_SystemZ_I128Indirect(unsigned &ValNo, MVT &ValVT,
+ MVT &LocVT,
+ CCValAssign::LocInfo &LocInfo,
+ ISD::ArgFlagsTy &ArgFlags,
+ CCState &State) {
+ SmallVectorImpl<CCValAssign> &PendingMembers = State.getPendingLocs();
+
+ // ArgFlags.isSplit() is true on the first part of a i128 argument;
+ // PendingMembers.empty() is false on all subsequent parts.
+ if (!ArgFlags.isSplit() && PendingMembers.empty())
+ return false;
+
+ // Push a pending Indirect value location for each part.
+ LocVT = MVT::i64;
+ LocInfo = CCValAssign::Indirect;
+ PendingMembers.push_back(CCValAssign::getPending(ValNo, ValVT,
+ LocVT, LocInfo));
+ if (!ArgFlags.isSplitEnd())
+ return true;
+
+ // OK, we've collected all parts in the pending list. Allocate
+ // the location (register or stack slot) for the indirect pointer.
+ // (This duplicates the usual i64 calling convention rules.)
+ unsigned Reg = State.AllocateReg(SystemZ::ArgGPRs);
+ unsigned Offset = Reg ? 0 : State.AllocateStack(8, 8);
+
+ // Use that same location for all the pending parts.
+ for (auto &It : PendingMembers) {
+ if (Reg)
+ It.convertToReg(Reg);
+ else
+ It.convertToMem(Offset);
+ State.addLoc(It);
+ }
+
+ PendingMembers.clear();
+
+ return true;
+}
+
} // end namespace llvm
#endif
diff --git a/llvm/lib/Target/SystemZ/SystemZCallingConv.td b/llvm/lib/Target/SystemZ/SystemZCallingConv.td
index bdd1b1598ad..2404b96642c 100644
--- a/llvm/lib/Target/SystemZ/SystemZCallingConv.td
+++ b/llvm/lib/Target/SystemZ/SystemZCallingConv.td
@@ -67,6 +67,9 @@ def CC_SystemZ : CallingConv<[
// Force long double values to the stack and pass i64 pointers to them.
CCIfType<[f128], CCPassIndirect<i64>>,
+ // Same for i128 values. These are already split into two i64 here,
+ // so we have to use a custom handler.
+ CCIfType<[i64], CCCustom<"CC_SystemZ_I128Indirect">>,
// The first 5 integer arguments are passed in R2-R6. Note that R6
// is call-saved.
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 4140be7bad0..0cafa21678a 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -813,9 +813,6 @@ static SDValue convertLocVTToValVT(SelectionDAG &DAG, SDLoc DL,
if (VA.isExtInLoc())
Value = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Value);
- else if (VA.getLocInfo() == CCValAssign::Indirect)
- Value = DAG.getLoad(VA.getValVT(), DL, Chain, Value,
- MachinePointerInfo(), false, false, false, 0);
else if (VA.getLocInfo() == CCValAssign::BCvt) {
// If this is a short vector argument loaded from the stack,
// extend from i64 to full vector size and then bitcast.
@@ -868,6 +865,7 @@ LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
MF.getInfo<SystemZMachineFunctionInfo>();
auto *TFL =
static_cast<const SystemZFrameLowering *>(Subtarget.getFrameLowering());
+ EVT PtrVT = getPointerTy(DAG.getDataLayout());
// Detect unsupported vector argument types.
if (Subtarget.hasVector())
@@ -930,7 +928,6 @@ LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
// Create the SelectionDAG nodes corresponding to a load
// from this parameter. Unpromoted ints and floats are
// passed as right-justified 8-byte values.
- EVT PtrVT = getPointerTy(DAG.getDataLayout());
SDValue FIN = DAG.getFrameIndex(FI, PtrVT);
if (VA.getLocVT() == MVT::i32 || VA.getLocVT() == MVT::f32)
FIN = DAG.getNode(ISD::ADD, DL, PtrVT, FIN,
@@ -942,7 +939,26 @@ LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
// Convert the value of the argument register into the value that's
// being passed.
- InVals.push_back(convertLocVTToValVT(DAG, DL, VA, Chain, ArgValue));
+ if (VA.getLocInfo() == CCValAssign::Indirect) {
+ InVals.push_back(DAG.getLoad(VA.getValVT(), DL, Chain,
+ ArgValue, MachinePointerInfo(),
+ false, false, false, 0));
+ // If the original argument was split (e.g. i128), we need
+ // to load all parts of it here (using the same address).
+ unsigned ArgIndex = Ins[I].OrigArgIndex;
+ assert (Ins[I].PartOffset == 0);
+ while (I + 1 != E && Ins[I + 1].OrigArgIndex == ArgIndex) {
+ CCValAssign &PartVA = ArgLocs[I + 1];
+ unsigned PartOffset = Ins[I + 1].PartOffset;
+ SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, ArgValue,
+ DAG.getIntPtrConstant(PartOffset, DL));
+ InVals.push_back(DAG.getLoad(PartVA.getValVT(), DL, Chain,
+ Address, MachinePointerInfo(),
+ false, false, false, 0));
+ ++I;
+ }
+ } else
+ InVals.push_back(convertLocVTToValVT(DAG, DL, VA, Chain, ArgValue));
}
if (IsVarArg) {
@@ -1054,11 +1070,25 @@ SystemZTargetLowering::LowerCall(CallLoweringInfo &CLI,
if (VA.getLocInfo() == CCValAssign::Indirect) {
// Store the argument in a stack slot and pass its address.
- SDValue SpillSlot = DAG.CreateStackTemporary(VA.getValVT());
+ SDValue SpillSlot = DAG.CreateStackTemporary(Outs[I].ArgVT);
int FI = cast<FrameIndexSDNode>(SpillSlot)->getIndex();
MemOpChains.push_back(DAG.getStore(
Chain, DL, ArgValue, SpillSlot,
MachinePointerInfo::getFixedStack(MF, FI), false, false, 0));
+ // If the original argument was split (e.g. i128), we need
+ // to store all parts of it here (and pass just one address).
+ unsigned ArgIndex = Outs[I].OrigArgIndex;
+ assert (Outs[I].PartOffset == 0);
+ while (I + 1 != E && Outs[I + 1].OrigArgIndex == ArgIndex) {
+ SDValue PartValue = OutVals[I + 1];
+ unsigned PartOffset = Outs[I + 1].PartOffset;
+ SDValue Address = DAG.getNode(ISD::ADD, DL, PtrVT, SpillSlot,
+ DAG.getIntPtrConstant(PartOffset, DL));
+ MemOpChains.push_back(DAG.getStore(
+ Chain, DL, PartValue, Address,
+ MachinePointerInfo::getFixedStack(MF, FI), false, false, 0));
+ ++I;
+ }
ArgValue = SpillSlot;
} else
ArgValue = convertValVTToLocVT(DAG, DL, VA, ArgValue);
@@ -1180,6 +1210,12 @@ CanLowerReturn(CallingConv::ID CallConv,
if (Subtarget.hasVector())
VerifyVectorTypes(Outs);
+ // Special case that we cannot easily detect in RetCC_SystemZ since
+ // i128 is not a legal type.
+ for (auto &Out : Outs)
+ if (Out.ArgVT == MVT::i128)
+ return false;
+
SmallVector<CCValAssign, 16> RetLocs;
CCState RetCCInfo(CallConv, isVarArg, MF, RetLocs, Context);
return RetCCInfo.CheckReturn(Outs, RetCC_SystemZ);
diff --git a/llvm/test/CodeGen/SystemZ/args-09.ll b/llvm/test/CodeGen/SystemZ/args-09.ll
new file mode 100644
index 00000000000..333b1daec2a
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-09.ll
@@ -0,0 +1,53 @@
+; Test the handling of i128 argument values
+;
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s -check-prefix=CHECK-INT
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s -check-prefix=CHECK-I128-1
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s -check-prefix=CHECK-I128-2
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s -check-prefix=CHECK-STACK
+
+declare void @bar(i64, i64, i64, i64, i128,
+ i64, i64, i64, i64, i128)
+
+; There are two indirect i128 slots, one at offset 200 (the first available
+; byte after the outgoing arguments) and one immediately after it at 216.
+; These slots should be set up outside the glued call sequence, so would
+; normally use %f0/%f2 as the first available 128-bit pair. This choice
+; is hard-coded in the I128 tests.
+;
+; The order of the CHECK-STACK stores doesn't matter. It would be OK to reorder
+; them in response to future code changes.
+define void @foo() {
+; CHECK-INT-LABEL: foo:
+; CHECK-INT-DAG: lghi %r2, 1
+; CHECK-INT-DAG: lghi %r3, 2
+; CHECK-INT-DAG: lghi %r4, 3
+; CHECK-INT-DAG: lghi %r5, 4
+; CHECK-INT-DAG: la %r6, {{200|216}}(%r15)
+; CHECK-INT: brasl %r14, bar@PLT
+;
+; CHECK-I128-1-LABEL: foo:
+; CHECK-I128-1: aghi %r15, -232
+; CHECK-I128-1-DAG: mvghi 200(%r15), 0
+; CHECK-I128-1-DAG: mvghi 208(%r15), 0
+; CHECK-I128-1: brasl %r14, bar@PLT
+;
+; CHECK-I128-2-LABEL: foo:
+; CHECK-I128-2: aghi %r15, -232
+; CHECK-I128-2-DAG: mvghi 216(%r15), 0
+; CHECK-I128-2-DAG: mvghi 224(%r15), 0
+; CHECK-I128-2: brasl %r14, bar@PLT
+;
+; CHECK-STACK-LABEL: foo:
+; CHECK-STACK: aghi %r15, -232
+; CHECK-STACK: la [[REGISTER:%r[0-5]+]], {{200|216}}(%r15)
+; CHECK-STACK: stg [[REGISTER]], 192(%r15)
+; CHECK-STACK: mvghi 184(%r15), 8
+; CHECK-STACK: mvghi 176(%r15), 7
+; CHECK-STACK: mvghi 168(%r15), 6
+; CHECK-STACK: mvghi 160(%r15), 5
+; CHECK-STACK: brasl %r14, bar@PLT
+
+ call void @bar (i64 1, i64 2, i64 3, i64 4, i128 0,
+ i64 5, i64 6, i64 7, i64 8, i128 0)
+ ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/args-10.ll b/llvm/test/CodeGen/SystemZ/args-10.ll
new file mode 100644
index 00000000000..6083c4415b3
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/args-10.ll
@@ -0,0 +1,50 @@
+; Test incoming i128 arguments.
+;
+; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
+
+; Do some arithmetic so that we can see the register being used.
+define void @f1(i128 *%r2, i16 %r3, i32 %r4, i64 %r5, i128 %r6) {
+; CHECK-LABEL: f1:
+; CHECK-DAG: lg [[REGL:%r[0-5]+]], 8(%r6)
+; CHECK-DAG: lg [[REGH:%r[0-5]+]], 0(%r6)
+; CHECK: algr [[REGL]], [[REGL]]
+; CHECK-NEXT: alcgr [[REGH]], [[REGH]]
+; CHECK-DAG: stg [[REGL]], 8(%r2)
+; CHECK-DAG: stg [[REGH]], 0(%r2)
+; CHECK: br %r14
+ %y = add i128 %r6, %r6
+ store i128 %y, i128 *%r2
+ ret void
+}
+
+; Test a case where the i128 address is passed on the stack.
+define void @f2(i128 *%r2, i16 %r3, i32 %r4, i64 %r5,
+ i128 %r6, i64 %s1, i64 %s2, i128 %s4) {
+; CHECK-LABEL: f2:
+; CHECK: lg [[ADDR:%r[1-5]+]], 176(%r15)
+; CHECK-DAG: lg [[REGL:%r[0-5]+]], 8([[ADDR]])
+; CHECK-DAG: lg [[REGH:%r[0-5]+]], 0([[ADDR]])
+; CHECK: algr [[REGL]], [[REGL]]
+; CHECK-NEXT: alcgr [[REGH]], [[REGH]]
+; CHECK-DAG: stg [[REGL]], 8(%r2)
+; CHECK-DAG: stg [[REGH]], 0(%r2)
+; CHECK: br %r14
+ %y = add i128 %s4, %s4
+ store i128 %y, i128 *%r2
+ ret void
+}
+
+; Explicit i128 return values are likewise passed indirectly.
+define i128 @f14(i128 %r3) {
+; CHECK-LABEL: f14:
+; CHECK-DAG: lg [[REGL:%r[0-5]+]], 8(%r3)
+; CHECK-DAG: lg [[REGH:%r[0-5]+]], 0(%r3)
+; CHECK: algr [[REGL]], [[REGL]]
+; CHECK-NEXT: alcgr [[REGH]], [[REGH]]
+; CHECK-DAG: stg [[REGL]], 8(%r2)
+; CHECK-DAG: stg [[REGH]], 0(%r2)
+; CHECK: br %r14
+ %y = add i128 %r3, %r3
+ ret i128 %y
+}
+
OpenPOWER on IntegriCloud