Index: lib/Target/WebAssembly/WebAssemblyISelLowering.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -77,6 +77,7 @@ SDValue LowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const; SDValue LowerBR_JT(SDValue Op, SelectionDAG &DAG) const; SDValue LowerJumpTable(SDValue Op, SelectionDAG &DAG) const; + SDValue LowerVASTART(SDValue Op, SelectionDAG &DAG) const; }; namespace WebAssembly { Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -118,6 +118,13 @@ 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); @@ -289,28 +296,92 @@ if (Ins.size() > 1) fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet"); + SmallVectorImpl &Outs = CLI.Outs; + 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"); + 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"); + } + 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 ArgLocs; CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + + 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); + 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.getNextStackOffset(); - auto PtrVT = getPointerTy(MF.getDataLayout()); - auto Zero = DAG.getConstant(0, DL, PtrVT, true); 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 Chains; + for (SDValue Arg : + make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { + assert(ArgLocs[ValNo].getValNo() == ValNo); + 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); + } + SmallVector Ops; Ops.push_back(Chain); Ops.push_back(Callee); - Ops.append(OutVals.begin(), OutVals.end()); + Ops.append(OutVals.begin(), + IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end()); SmallVector Tys; - for (const auto &In : Ins) + for (const auto &In : Ins) { + if (In.Flags.isByVal()) + fail(DL, DAG, "WebAssembly hasn't implemented byval arguments"); + if (In.Flags.isInAlloca()) + fail(DL, DAG, "WebAssembly hasn't implemented inalloca arguments"); + if (In.Flags.isNest()) + fail(DL, DAG, "WebAssembly hasn't implemented nest arguments"); + if (In.Flags.isInConsecutiveRegs()) + fail(DL, DAG, "WebAssembly hasn't implemented cons regs arguments"); + if (In.Flags.isInConsecutiveRegsLast()) + fail(DL, DAG, "WebAssembly hasn't implemented cons regs last arguments"); + // Ignore In.getOrigAlign() because all our arguments are passed in + // registers. Tys.push_back(In.VT); + } Tys.push_back(MVT::Other); SDVTList TyList = DAG.getVTList(Tys); SDValue Res = @@ -323,7 +394,8 @@ 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; } @@ -337,15 +409,16 @@ } SDValue WebAssemblyTargetLowering::LowerReturn( - SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, SDLoc DL, SelectionDAG &DAG) const { assert(Outs.size() <= 1 && "WebAssembly can only return up to one value"); - if (CallConv != CallingConv::C) - fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); - if (IsVarArg) - fail(DL, DAG, "WebAssembly doesn't support varargs yet"); + if (CallConv != CallingConv::C && CallConv != CallingConv::Fast && + CallConv != CallingConv::Cold) + fail(DL, DAG, + "WebAssembly doesn't support language-specific or target-specific " + "calling conventions yet"); SmallVector RetOps(1, Chain); RetOps.append(OutVals.begin(), OutVals.end()); @@ -355,14 +428,13 @@ 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 valid 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; @@ -374,10 +446,11 @@ SmallVectorImpl &InVals) const { MachineFunction &MF = DAG.getMachineFunction(); - if (CallConv != CallingConv::C) - fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); - if (IsVarArg) - fail(DL, DAG, "WebAssembly doesn't support varargs yet"); + if (CallConv != CallingConv::C && CallConv != CallingConv::Fast && + CallConv != CallingConv::Cold) + fail(DL, DAG, + "WebAssembly doesn't support language-specific or target-specific " + "calling conventions yet"); // Set up the incoming ARGUMENTS value, which serves to represent the liveness // of the incoming values before they're represented by virtual registers. @@ -400,7 +473,7 @@ In.Used ? DAG.getNode(WebAssemblyISD::ARGUMENT, DL, In.VT, DAG.getTargetConstant(InVals.size(), DL, MVT::i32)) - : DAG.getNode(ISD::UNDEF, DL, In.VT)); + : DAG.getUNDEF(In.VT)); // Record the number and types of arguments. MF.getInfo()->addParam(In.VT); @@ -427,6 +500,8 @@ return LowerJumpTable(Op, DAG); case ISD::BR_JT: return LowerBR_JT(Op, DAG); + case ISD::VASTART: + return LowerVASTART(Op, DAG); } } @@ -492,6 +567,23 @@ 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 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(Subtarget->getRegisterInfo()) + ->getFrameRegister(DAG.getMachineFunction()); + SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FP, PtrVT); + const Value *SV = cast(Op.getOperand(2))->getValue(); + return DAG.getStore(Op.getOperand(0), DL, FrameAddr, Op.getOperand(1), + MachinePointerInfo(SV), false, false, 0); +} + //===----------------------------------------------------------------------===// // WebAssembly Optimization Hooks //===----------------------------------------------------------------------===// Index: lib/Target/WebAssembly/WebAssemblyInstrCall.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -17,10 +17,10 @@ // The call sequence start/end LLVM-isms isn't useful to WebAssembly since it's // a virtual ISA. let isCodeGenOnly = 1 in { -def : I<(outs), (ins i64imm:$amt), - [(WebAssemblycallseq_start timm:$amt)]>; -def : I<(outs), (ins i64imm:$amt1, i64imm:$amt2), - [(WebAssemblycallseq_end timm:$amt1, timm:$amt2)]>; +def ADJCALLSTACKDOWN : I<(outs), (ins i64imm:$amt), + [(WebAssemblycallseq_start timm:$amt)]>; +def ADJCALLSTACKUP : I<(outs), (ins i64imm:$amt), + [(WebAssemblycallseq_end timm:$amt, undef)]>; } // isCodeGenOnly = 1 multiclass CALL { Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.cpp @@ -28,7 +28,9 @@ #include "WebAssemblyGenInstrInfo.inc" WebAssemblyInstrInfo::WebAssemblyInstrInfo(const WebAssemblySubtarget &STI) - : RI(STI.getTargetTriple()) {} + : WebAssemblyGenInstrInfo(WebAssembly::ADJCALLSTACKDOWN, + WebAssembly::ADJCALLSTACKUP), + RI(STI.getTargetTriple()) {} void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Index: lib/Target/WebAssembly/WebAssemblyRegStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegStackify.cpp +++ lib/Target/WebAssembly/WebAssemblyRegStackify.cpp @@ -203,6 +203,8 @@ if (!MO.isReg()) continue; unsigned VReg = MO.getReg(); + if (!TargetRegisterInfo::isVirtualRegister(VReg)) + continue; if (MFI.isVRegStackified(VReg)) { if (MO.isDef()) Index: lib/Target/WebAssembly/WebAssemblyRegisterInfo.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegisterInfo.h +++ lib/Target/WebAssembly/WebAssemblyRegisterInfo.h @@ -41,6 +41,10 @@ // Debug information queries. unsigned getFrameRegister(const MachineFunction &MF) const override; + + const TargetRegisterClass * + getPointerRegClass(const MachineFunction &MF, + unsigned Kind = 0) const override; }; } // end namespace llvm Index: lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp +++ lib/Target/WebAssembly/WebAssemblyRegisterInfo.cpp @@ -67,3 +67,12 @@ const WebAssemblyFrameLowering *TFI = getFrameLowering(MF); return Regs[TFI->hasFP(MF)][TT.isArch64Bit()]; } + +const TargetRegisterClass * +WebAssemblyRegisterInfo::getPointerRegClass(const MachineFunction &MF, + unsigned Kind) const { + assert(Kind == 0 && "Only one kind of pointer on WebAssembly"); + if (MF.getSubtarget().hasAddr64()) + return &WebAssembly::I64RegClass; + return &WebAssembly::I32RegClass; +}