setOperationAction(ISD::ExternalSymbol, MVTPtr, Custom);
setOperationAction(ISD::JumpTable, MVTPtr, Custom);
+ // Take the default expansion for va_arg, va_copy, and va_end. There is no
+ // default action for va_start, so we do that custom.
+ setOperationAction(ISD::VASTART, MVT::Other, Custom);
+ setOperationAction(ISD::VAARG, MVT::Other, Expand);
+ setOperationAction(ISD::VACOPY, MVT::Other, Expand);
+ setOperationAction(ISD::VAEND, MVT::Other, Expand);
+
for (auto T : {MVT::f32, MVT::f64}) {
// Don't expand the floating-point types to constant pools.
setOperationAction(ISD::ConstantFP, T, Legal);
}
bool IsVarArg = CLI.IsVarArg;
- if (IsVarArg)
- fail(DL, DAG, "WebAssembly doesn't support varargs yet");
+ unsigned NumFixedArgs = CLI.NumFixedArgs;
+ auto PtrVT = getPointerTy(MF.getDataLayout());
// Analyze operands of the call, assigning locations to each operand.
SmallVector<CCValAssign, 16> ArgLocs;
CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
- unsigned NumBytes = CCInfo.getNextStackOffset();
- auto PtrVT = getPointerTy(MF.getDataLayout());
- auto Zero = DAG.getConstant(0, DL, PtrVT, true);
+ if (IsVarArg) {
+ // Outgoing non-fixed arguments are placed at the top of the stack. First
+ // compute their offsets and the total amount of argument stack space
+ // needed.
+ for (SDValue Arg :
+ make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) {
+ EVT VT = Arg.getValueType();
+ assert(VT != MVT::iPTR && "Legalized args should be concrete");
+ Type *Ty = VT.getTypeForEVT(*DAG.getContext());
+ unsigned Offset =
+ CCInfo.AllocateStack(MF.getDataLayout().getTypeAllocSize(Ty),
+ MF.getDataLayout().getABITypeAlignment(Ty));
+ CCInfo.addLoc(CCValAssign::getMem(ArgLocs.size(), VT.getSimpleVT(),
+ Offset, VT.getSimpleVT(),
+ CCValAssign::Full));
+ }
+ }
+
+ unsigned NumBytes = CCInfo.getAlignedCallFrameSize();
+
auto NB = DAG.getConstant(NumBytes, DL, PtrVT, true);
Chain = DAG.getCALLSEQ_START(Chain, NB, DL);
+ if (IsVarArg) {
+ // For non-fixed arguments, next emit stores to store the argument values
+ // to the stack at the offsets computed above.
+ SDValue SP = DAG.getCopyFromReg(
+ Chain, DL, getStackPointerRegisterToSaveRestore(), PtrVT);
+ unsigned ValNo = 0;
+ SmallVector<SDValue, 8> Chains;
+ for (SDValue Arg :
+ make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) {
+ assert(ArgLocs[ValNo].getValNo() == ValNo &&
+ "ArgLocs should remain in order and only hold varargs args");
+ unsigned Offset = ArgLocs[ValNo++].getLocMemOffset();
+ SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, SP,
+ DAG.getConstant(Offset, DL, PtrVT));
+ Chains.push_back(DAG.getStore(Chain, DL, Arg, Add,
+ MachinePointerInfo::getStack(MF, Offset),
+ false, false, 0));
+ }
+ if (!Chains.empty())
+ Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains);
+ }
+
+ // Compute the operands for the CALLn node.
SmallVector<SDValue, 16> Ops;
Ops.push_back(Chain);
Ops.push_back(Callee);
- Ops.append(OutVals.begin(), OutVals.end());
+
+ // Add all fixed arguments. Note that for non-varargs calls, NumFixedArgs
+ // isn't reliable.
+ Ops.append(OutVals.begin(),
+ IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end());
SmallVector<EVT, 8> Tys;
for (const auto &In : Ins) {
Chain = Res.getValue(1);
}
- Chain = DAG.getCALLSEQ_END(Chain, NB, Zero, SDValue(), DL);
+ SDValue Unused = DAG.getUNDEF(PtrVT);
+ Chain = DAG.getCALLSEQ_END(Chain, NB, Unused, SDValue(), DL);
return Chain;
}
}
SDValue WebAssemblyTargetLowering::LowerReturn(
- SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
+ SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals, SDLoc DL,
SelectionDAG &DAG) const {
assert(Outs.size() <= 1 && "WebAssembly can only return up to one value");
if (!CallingConvSupported(CallConv))
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
- if (IsVarArg)
- fail(DL, DAG, "WebAssembly doesn't support varargs yet");
SmallVector<SDValue, 4> RetOps(1, Chain);
RetOps.append(OutVals.begin(), OutVals.end());
for (const ISD::OutputArg &Out : Outs) {
assert(!Out.Flags.isByVal() && "byval is not valid for return values");
assert(!Out.Flags.isNest() && "nest is not valid for return values");
+ assert(Out.IsFixed && "non-fixed return value is not valid");
if (Out.Flags.isInAlloca())
fail(DL, DAG, "WebAssembly hasn't implemented inalloca results");
if (Out.Flags.isInConsecutiveRegs())
fail(DL, DAG, "WebAssembly hasn't implemented cons regs results");
if (Out.Flags.isInConsecutiveRegsLast())
fail(DL, DAG, "WebAssembly hasn't implemented cons regs last results");
- if (!Out.IsFixed)
- fail(DL, DAG, "WebAssembly doesn't support non-fixed results yet");
}
return Chain;
}
SDValue WebAssemblyTargetLowering::LowerFormalArguments(
- SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
+ SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/,
const SmallVectorImpl<ISD::InputArg> &Ins, SDLoc DL, SelectionDAG &DAG,
SmallVectorImpl<SDValue> &InVals) const {
MachineFunction &MF = DAG.getMachineFunction();
if (!CallingConvSupported(CallConv))
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
- if (IsVarArg)
- fail(DL, DAG, "WebAssembly doesn't support varargs yet");
// Set up the incoming ARGUMENTS value, which serves to represent the liveness
// of the incoming values before they're represented by virtual registers.
MF.getInfo<WebAssemblyFunctionInfo>()->addParam(In.VT);
}
+ // Incoming varargs arguments are on the stack and will be accessed through
+ // va_arg, so we don't need to do anything for them here.
+
return Chain;
}
return LowerJumpTable(Op, DAG);
case ISD::BR_JT:
return LowerBR_JT(Op, DAG);
+ case ISD::VASTART:
+ return LowerVASTART(Op, DAG);
}
}
return DAG.getNode(WebAssemblyISD::TABLESWITCH, DL, MVT::Other, Ops);
}
+SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op,
+ SelectionDAG &DAG) const {
+ SDLoc DL(Op);
+ EVT PtrVT = getPointerTy(DAG.getMachineFunction().getDataLayout());
+
+ // The incoming non-fixed arguments are placed on the top of the stack, with
+ // natural alignment, at the point of the call, so the base pointer is just
+ // the current frame pointer.
+ DAG.getMachineFunction().getFrameInfo()->setFrameAddressIsTaken(true);
+ unsigned FP =
+ static_cast<const WebAssemblyRegisterInfo *>(Subtarget->getRegisterInfo())
+ ->getFrameRegister(DAG.getMachineFunction());
+ SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FP, PtrVT);
+ const Value *SV = cast<SrcValueSDNode>(Op.getOperand(2))->getValue();
+ return DAG.getStore(Op.getOperand(0), DL, FrameAddr, Op.getOperand(1),
+ MachinePointerInfo(SV), false, false, 0);
+}
+
//===----------------------------------------------------------------------===//
// WebAssembly Optimization Hooks
//===----------------------------------------------------------------------===//