summaryrefslogtreecommitdiffstats
path: root/llvm/lib
diff options
context:
space:
mode:
authorMandeep Singh Grang <mgrang@quicinc.com>2019-05-03 21:12:36 +0000
committerMandeep Singh Grang <mgrang@quicinc.com>2019-05-03 21:12:36 +0000
commit5dc8aeb26d2896c65cbea0d3b87f090241444bb6 (patch)
treee9fdeab58b37e92852c61425e7ae3d8430d1fd04 /llvm/lib
parent85a0f8fe6c5c8ab35790c40d078d4fa103a5a54a (diff)
downloadbcm5719-llvm-5dc8aeb26d2896c65cbea0d3b87f090241444bb6.tar.gz
bcm5719-llvm-5dc8aeb26d2896c65cbea0d3b87f090241444bb6.zip
[COFF, ARM64] Fix ABI implementation of struct returns
Summary: Refer the ABI doc at: https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=vs-2019#return-values Related clang patch: D60349 Reviewers: rnk, efriedma, TomTan, ssijaric Reviewed By: rnk, efriedma Subscribers: mstorsjo, javed.absar, kristof.beyls, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D60348 llvm-svn: 359934
Diffstat (limited to 'llvm/lib')
-rw-r--r--llvm/lib/IR/Function.cpp4
-rw-r--r--llvm/lib/Target/AArch64/AArch64CallingConvention.td18
-rw-r--r--llvm/lib/Target/AArch64/AArch64ISelLowering.cpp52
-rw-r--r--llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h8
4 files changed, 80 insertions, 2 deletions
diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp
index 70a0dbc28b6..b00deb677b3 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -145,6 +145,10 @@ bool Argument::hasStructRetAttr() const {
return hasAttribute(Attribute::StructRet);
}
+bool Argument::hasInRegAttr() const {
+ return hasAttribute(Attribute::InReg);
+}
+
bool Argument::hasReturnedAttr() const {
return hasAttribute(Attribute::Returned);
}
diff --git a/llvm/lib/Target/AArch64/AArch64CallingConvention.td b/llvm/lib/Target/AArch64/AArch64CallingConvention.td
index e164dcbf63b..d969a9e1ab3 100644
--- a/llvm/lib/Target/AArch64/AArch64CallingConvention.td
+++ b/llvm/lib/Target/AArch64/AArch64CallingConvention.td
@@ -34,7 +34,23 @@ def CC_AArch64_AAPCS : CallingConv<[
CCIfBigEndian<CCIfType<[v2i64, v2f64, v4i32, v4f32, v8i16, v8f16, v16i8],
CCBitConvertToType<f128>>>,
- // An SRet is passed in X8, not X0 like a normal pointer parameter.
+ // In AAPCS, an SRet is passed in X8, not X0 like a normal pointer parameter.
+ // However, on windows, in some circumstances, the SRet is passed in X0 or X1
+ // instead. The presence of the inreg attribute indicates that SRet is
+ // passed in the alternative register (X0 or X1), not X8:
+ // - X0 for non-instance methods.
+ // - X1 for instance methods.
+
+ // The "sret" attribute identifies indirect returns.
+ // The "inreg" attribute identifies non-aggregate types.
+ // The position of the "sret" attribute identifies instance/non-instance
+ // methods.
+ // "sret" on argument 0 means non-instance methods.
+ // "sret" on argument 1 means instance methods.
+
+ CCIfInReg<CCIfType<[i64],
+ CCIfSRet<CCIfType<[i64], CCAssignToRegWithShadow<[X0, X1], [W0, W1]>>>>>,
+
CCIfSRet<CCIfType<[i64], CCAssignToRegWithShadow<[X8], [W8]>>>,
// Put ByVal arguments directly on the stack. Minimum size and alignment of a
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 3c6c87663ec..3ff4a225794 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -3208,6 +3208,26 @@ SDValue AArch64TargetLowering::LowerFormalArguments(
}
}
+ // On Windows, InReg pointers must be returned, so record the pointer in a
+ // virtual register at the start of the function so it can be returned in the
+ // epilogue.
+ if (IsWin64) {
+ for (unsigned I = 0, E = Ins.size(); I != E; ++I) {
+ if (Ins[I].Flags.isInReg()) {
+ assert(!FuncInfo->getSRetReturnReg());
+
+ MVT PtrTy = getPointerTy(DAG.getDataLayout());
+ unsigned Reg =
+ MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrTy));
+ FuncInfo->setSRetReturnReg(Reg);
+
+ SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), DL, Reg, InVals[I]);
+ Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Copy, Chain);
+ break;
+ }
+ }
+ }
+
unsigned StackArgSize = CCInfo.getNextStackOffset();
bool TailCallOpt = MF.getTarget().Options.GuaranteedTailCallOpt;
if (DoesCalleeRestoreStack(CallConv, TailCallOpt)) {
@@ -3403,10 +3423,20 @@ bool AArch64TargetLowering::isEligibleForTailCallOptimization(
// X86) but less efficient and uglier in LowerCall.
for (Function::const_arg_iterator i = CallerF.arg_begin(),
e = CallerF.arg_end();
- i != e; ++i)
+ i != e; ++i) {
if (i->hasByValAttr())
return false;
+ // On Windows, "inreg" attributes signify non-aggregate indirect returns.
+ // In this case, it is necessary to save/restore X0 in the callee. Tail
+ // call opt interferes with this. So we disable tail call opt when the
+ // caller has an argument with "inreg" attribute.
+
+ // FIXME: Check whether the callee also has an "inreg" argument.
+ if (i->hasInRegAttr())
+ return false;
+ }
+
if (getTargetMachine().Options.GuaranteedTailCallOpt)
return canGuaranteeTCO(CalleeCC) && CCMatch;
@@ -3924,6 +3954,9 @@ AArch64TargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
const SDLoc &DL, SelectionDAG &DAG) const {
+ auto &MF = DAG.getMachineFunction();
+ auto *FuncInfo = MF.getInfo<AArch64FunctionInfo>();
+
CCAssignFn *RetCC = CallConv == CallingConv::WebKit_JS
? RetCC_AArch64_WebKit_JS
: RetCC_AArch64_AAPCS;
@@ -3962,6 +3995,23 @@ AArch64TargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
Flag = Chain.getValue(1);
RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT()));
}
+
+ // Windows AArch64 ABIs require that for returning structs by value we copy
+ // the sret argument into X0 for the return.
+ // We saved the argument into a virtual register in the entry block,
+ // so now we copy the value out and into X0.
+ if (unsigned SRetReg = FuncInfo->getSRetReturnReg()) {
+ SDValue Val = DAG.getCopyFromReg(RetOps[0], DL, SRetReg,
+ getPointerTy(MF.getDataLayout()));
+
+ unsigned RetValReg = AArch64::X0;
+ Chain = DAG.getCopyToReg(Chain, DL, RetValReg, Val, Flag);
+ Flag = Chain.getValue(1);
+
+ RetOps.push_back(
+ DAG.getRegister(RetValReg, getPointerTy(DAG.getDataLayout())));
+ }
+
const AArch64RegisterInfo *TRI = Subtarget->getRegisterInfo();
const MCPhysReg *I =
TRI->getCalleeSavedRegsViaCopy(&DAG.getMachineFunction());
diff --git a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h
index 5ab511ee88a..f4e810fa454 100644
--- a/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h
+++ b/llvm/lib/Target/AArch64/AArch64MachineFunctionInfo.h
@@ -91,6 +91,11 @@ class AArch64FunctionInfo final : public MachineFunctionInfo {
/// other stack allocations.
bool CalleeSaveStackHasFreeSpace = false;
+ /// SRetReturnReg - sret lowering includes returning the value of the
+ /// returned struct in a register. This field holds the virtual register into
+ /// which the sret argument is passed.
+ unsigned SRetReturnReg = 0;
+
/// Has a value when it is known whether or not the function uses a
/// redzone, and no value otherwise.
/// Initialized during frame lowering, unless the function has the noredzone
@@ -165,6 +170,9 @@ public:
unsigned getVarArgsFPRSize() const { return VarArgsFPRSize; }
void setVarArgsFPRSize(unsigned Size) { VarArgsFPRSize = Size; }
+ unsigned getSRetReturnReg() const { return SRetReturnReg; }
+ void setSRetReturnReg(unsigned Reg) { SRetReturnReg = Reg; }
+
unsigned getJumpTableEntrySize(int Idx) const {
auto It = JumpTableEntryInfo.find(Idx);
if (It != JumpTableEntryInfo.end())
OpenPOWER on IntegriCloud