diff options
author | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2016-02-19 14:10:21 +0000 |
---|---|---|
committer | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2016-02-19 14:10:21 +0000 |
commit | cfa1d2b49d7b3f42833dc16cc5bfbaf3d9ba74a8 (patch) | |
tree | 4233bbf11c39e819da58139267aa24675acf4b65 /llvm/lib | |
parent | 611d2e4ee6a5150b45659abafd76e5917d4382a7 (diff) | |
download | bcm5719-llvm-cfa1d2b49d7b3f42833dc16cc5bfbaf3d9ba74a8.tar.gz bcm5719-llvm-cfa1d2b49d7b3f42833dc16cc5bfbaf3d9ba74a8.zip |
[SystemZ] Fix ABI for i128 argument and return types
According to the SystemZ ABI, 128-bit integer types should be
passed and returned via implicit reference. However, this is
not currently implemented at the LLVM IR level for the i128
type. This does not matter when compiling C/C++ code, since
clang will implement the implicit reference itself.
However, it turns out that when calling libgcc helper routines
operating on 128-bit integers, LLVM will use i128 argument and
return value types; the resulting code is not compatible with
the ABI used in libgcc, leading to crashes (see PR26559).
This should be simple to fix, except that i128 currently is not
even a legal type for the SystemZ back end. Therefore, common
code will already split arguments and return values into multiple
parts. The bulk of this patch therefore consists of detecting
such parts, and correctly handling passing via implicit reference
of a value split into multiple parts. If at some time in the
future, i128 becomes a legal type, this code can be removed again.
This fixes PR26559.
llvm-svn: 261325
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/Target/SystemZ/SystemZCallingConv.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/Target/SystemZ/SystemZCallingConv.h | 50 | ||||
-rw-r--r-- | llvm/lib/Target/SystemZ/SystemZCallingConv.td | 3 | ||||
-rw-r--r-- | llvm/lib/Target/SystemZ/SystemZISelLowering.cpp | 48 |
4 files changed, 95 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); |