summaryrefslogtreecommitdiffstats
path: root/llvm/lib/Target/Mips/MipsISelLowering.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Target/Mips/MipsISelLowering.cpp')
-rw-r--r--llvm/lib/Target/Mips/MipsISelLowering.cpp51
1 files changed, 43 insertions, 8 deletions
diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp
index 7e6ce442625..73c305042c7 100644
--- a/llvm/lib/Target/Mips/MipsISelLowering.cpp
+++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp
@@ -2949,12 +2949,44 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, *DAG.getContext(),
MipsCCState::getSpecialCallingConvForCallee(Callee.getNode(), Subtarget));
+ const ExternalSymbolSDNode *ES =
+ dyn_cast_or_null<const ExternalSymbolSDNode>(Callee.getNode());
+
+ // There is one case where CALLSEQ_START..CALLSEQ_END can be nested, which
+ // is during the lowering of a call with a byval argument which produces
+ // a call to memcpy. For the O32 case, this causes the caller to allocate
+ // stack space for the reserved argument area for the callee, then recursively
+ // again for the memcpy call. In the NEWABI case, this doesn't occur as those
+ // ABIs mandate that the callee allocates the reserved argument area. We do
+ // still produce nested CALLSEQ_START..CALLSEQ_END with zero space though.
+ //
+ // If the callee has a byval argument and memcpy is used, we are mandated
+ // to already have produced a reserved argument area for the callee for O32.
+ // Therefore, the reserved argument area can be reused for both calls.
+ //
+ // Other cases of calling memcpy cannot have a chain with a CALLSEQ_START
+ // present, as we have yet to hook that node onto the chain.
+ //
+ // Hence, the CALLSEQ_START and CALLSEQ_END nodes can be eliminated in this
+ // case. GCC does a similar trick, in that wherever possible, it calculates
+ // the maximum out going argument area (including the reserved area), and
+ // preallocates the stack space on entrance to the caller.
+ //
+ // FIXME: We should do the same for efficency and space.
+
+ // Note: The check on the calling convention below must match
+ // MipsABIInfo::GetCalleeAllocdArgSizeInBytes().
+ bool MemcpyInByVal = ES &&
+ StringRef(ES->getSymbol()) == StringRef("memcpy") &&
+ CallConv != CallingConv::Fast &&
+ Chain.getOpcode() == ISD::CALLSEQ_START;
+
// Allocate the reserved argument area. It seems strange to do this from the
// caller side but removing it breaks the frame size calculation.
- CCInfo.AllocateStack(ABI.GetCalleeAllocdArgSizeInBytes(CallConv), 1);
+ unsigned ReservedArgArea =
+ MemcpyInByVal ? 0 : ABI.GetCalleeAllocdArgSizeInBytes(CallConv);
+ CCInfo.AllocateStack(ReservedArgArea, 1);
- const ExternalSymbolSDNode *ES =
- dyn_cast_or_null<const ExternalSymbolSDNode>(Callee.getNode());
CCInfo.AnalyzeCallOperands(Outs, CC_Mips, CLI.getArgs(),
ES ? ES->getSymbol() : nullptr);
@@ -2989,7 +3021,7 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
NextStackOffset = alignTo(NextStackOffset, StackAlignment);
SDValue NextStackOffsetVal = DAG.getIntPtrConstant(NextStackOffset, DL, true);
- if (!IsTailCall)
+ if (!(IsTailCall || MemcpyInByVal))
Chain = DAG.getCALLSEQ_START(Chain, NextStackOffset, 0, DL);
SDValue StackPtr =
@@ -3197,10 +3229,13 @@ MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
Chain = DAG.getNode(MipsISD::JmpLink, DL, NodeTys, Ops);
SDValue InFlag = Chain.getValue(1);
- // Create the CALLSEQ_END node.
- Chain = DAG.getCALLSEQ_END(Chain, NextStackOffsetVal,
- DAG.getIntPtrConstant(0, DL, true), InFlag, DL);
- InFlag = Chain.getValue(1);
+ // Create the CALLSEQ_END node in the case of where it is not a call to
+ // memcpy.
+ if (!(MemcpyInByVal)) {
+ Chain = DAG.getCALLSEQ_END(Chain, NextStackOffsetVal,
+ DAG.getIntPtrConstant(0, DL, true), InFlag, DL);
+ InFlag = Chain.getValue(1);
+ }
// Handle result values, copying them out of physregs into vregs that we
// return.
OpenPOWER on IntegriCloud