From: Derek Schuff Date: Wed, 10 Feb 2016 19:51:04 +0000 (+0000) Subject: [WebAssembly] Switch varags calling convention to use a register X-Git-Tag: llvmorg-3.9.0-rc1~14548 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=27501e2065f70eb3a1b6bd021e2459102e1f5832;p=platform%2Fupstream%2Fllvm.git [WebAssembly] Switch varags calling convention to use a register Instead of passing varargs directly on the user stack, allocate a buffer in the caller's stack frame and pass a pointer to it. This simplifies the C ABI (e.g. non-C callers of C functions do not need to use C's user stack if they have their own mechanism) and allows further optimizations in the future (e.g. fewer functions may need to use the stack). Differential Revision: http://reviews.llvm.org/D17048 llvm-svn: 260421 --- diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp index e0ff508..c6c473c 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp @@ -46,9 +46,8 @@ bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const { const MachineFrameInfo *MFI = MF.getFrameInfo(); const auto *RegInfo = MF.getSubtarget().getRegisterInfo(); - return MFI->hasVarSizedObjects() || MFI->isFrameAddressTaken() || - MFI->hasStackMap() || MFI->hasPatchPoint() || - RegInfo->needsStackRealignment(MF); + return MFI->hasVarSizedObjects() || MFI->hasStackMap() || + MFI->hasPatchPoint() || RegInfo->needsStackRealignment(MF); } /// Under normal circumstances, when a frame pointer is not required, we reserve @@ -61,68 +60,12 @@ bool WebAssemblyFrameLowering::hasReservedCallFrame( return !MF.getFrameInfo()->hasVarSizedObjects(); } - -/// Adjust the stack pointer by a constant amount. -static void adjustStackPointer(unsigned StackSize, - bool AdjustUp, - MachineFunction& MF, - MachineBasicBlock& MBB, - const TargetInstrInfo* TII, - MachineBasicBlock::iterator InsertPt, - const DebugLoc& DL) { - assert((StackSize || !AdjustUp) && "Adjusting up by 0"); - auto &MRI = MF.getRegInfo(); - unsigned SPReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); - auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer"); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), SPReg) - .addExternalSymbol(SPSymbol); - // This MachinePointerInfo should reference __stack_pointer as well but - // doesn't because MachinePointerInfo() takes a GV which we don't have for - // __stack_pointer. TODO: check if PseudoSourceValue::ExternalSymbolCallEntry - // is appropriate instead. (likewise for EmitEpologue below) - auto *LoadMMO = new MachineMemOperand(MachinePointerInfo(), - MachineMemOperand::MOLoad, 4, 4); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32), SPReg) - .addImm(0) // offset - .addReg(SPReg) // addr - .addImm(2) // p2align - .addMemOperand(LoadMMO); - // Add/Subtract the frame size - unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) - .addImm(StackSize); - BuildMI(MBB, InsertPt, DL, - TII->get(AdjustUp ? WebAssembly::ADD_I32 : WebAssembly::SUB_I32), - WebAssembly::SP32) - .addReg(SPReg) - .addReg(OffsetReg); - // The SP32 register now has the new stacktop. Also write it back to memory. - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg) - .addExternalSymbol(SPSymbol); - auto *MMO = new MachineMemOperand(MachinePointerInfo(), - MachineMemOperand::MOStore, 4, 4); - BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::STORE_I32), WebAssembly::SP32) - .addImm(0) - .addReg(OffsetReg) - .addImm(2) // p2align - .addReg(WebAssembly::SP32) - .addMemOperand(MMO); -} - void WebAssemblyFrameLowering::eliminateCallFramePseudoInstr( MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const { - const auto *TII = MF.getSubtarget().getInstrInfo(); - DebugLoc DL = I->getDebugLoc(); - unsigned Opc = I->getOpcode(); - bool IsDestroy = Opc == TII->getCallFrameDestroyOpcode(); - unsigned Amount = I->getOperand(0).getImm(); - // TODO(dschuff): After we switch varargs to passing an explicit pointer - // rather than using an implicit call frame, assert here that Amount is 0 - // and remove adjustStackPointer altogether. - if (Amount) - adjustStackPointer(Amount, IsDestroy, MF, MBB, - TII, I, DL); + // TODO: can we avoid using call frame pseudos altogether? + assert(!I->getOperand(0).getImm() && + "Stack should not be adjusted around calls"); MBB.erase(I); } @@ -132,10 +75,10 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF, auto *MFI = MF.getFrameInfo(); assert(MFI->getCalleeSavedInfo().empty() && "WebAssembly should not have callee-saved registers"); + assert(!MFI->isFrameAddressTaken()); uint64_t StackSize = MFI->getStackSize(); - if (!StackSize && !MFI->adjustsStack()) - return; + if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return; const auto *TII = MF.getSubtarget().getInstrInfo(); auto &MRI = MF.getRegInfo(); @@ -201,8 +144,7 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const { auto *MFI = MF.getFrameInfo(); uint64_t StackSize = MFI->getStackSize(); - if (!StackSize && !MFI->adjustsStack()) - return; + if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return; const auto *TII = MF.getSubtarget().getInstrInfo(); auto &MRI = MF.getRegInfo(); unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index f8b04c8..d3572b1 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -339,9 +339,8 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, 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. + // Outgoing non-fixed arguments are placed in a buffer. First + // compute their offsets and the total amount of buffer space needed. for (SDValue Arg : make_range(OutVals.begin() + NumFixedArgs, OutVals.end())) { EVT VT = Arg.getValueType(); @@ -358,17 +357,12 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, unsigned NumBytes = CCInfo.getAlignedCallFrameSize(); - SDValue NB; - if (NumBytes) { - NB = DAG.getConstant(NumBytes, DL, PtrVT, true); - Chain = DAG.getCALLSEQ_START(Chain, NB, DL); - } - - if (IsVarArg) { + SDValue FINode; + if (IsVarArg && NumBytes) { // 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); + // to the stack buffer at the offsets computed above. + int FI = MF.getFrameInfo()->CreateStackObject(NumBytes, /*Alignment=*/16, + /*isSS=*/false); unsigned ValNo = 0; SmallVector Chains; for (SDValue Arg : @@ -376,14 +370,17 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, 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, + FINode = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, FINode, DAG.getConstant(Offset, DL, PtrVT)); - Chains.push_back(DAG.getStore(Chain, DL, Arg, Add, - MachinePointerInfo::getStack(MF, Offset), - false, false, 0)); + Chains.push_back(DAG.getStore( + Chain, DL, Arg, Add, + MachinePointerInfo::getFixedStack(MF, FI, Offset), false, false, 0)); } if (!Chains.empty()) Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Chains); + } else if (IsVarArg) { + FINode = DAG.getIntPtrConstant(0, DL); } // Compute the operands for the CALLn node. @@ -395,8 +392,10 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, // isn't reliable. Ops.append(OutVals.begin(), IsVarArg ? OutVals.begin() + NumFixedArgs : OutVals.end()); + // Add a pointer to the vararg buffer. + if (IsVarArg) Ops.push_back(FINode); - SmallVector Tys; + SmallVector InTys; for (const auto &In : Ins) { assert(!In.Flags.isByVal() && "byval is not valid for return values"); assert(!In.Flags.isNest() && "nest is not valid for return values"); @@ -409,13 +408,13 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, "WebAssembly hasn't implemented cons regs last return values"); // Ignore In.getOrigAlign() because all our arguments are passed in // registers. - Tys.push_back(In.VT); + InTys.push_back(In.VT); } - Tys.push_back(MVT::Other); - SDVTList TyList = DAG.getVTList(Tys); + InTys.push_back(MVT::Other); + SDVTList InTyList = DAG.getVTList(InTys); SDValue Res = DAG.getNode(Ins.empty() ? WebAssemblyISD::CALL0 : WebAssemblyISD::CALL1, - DL, TyList, Ops); + DL, InTyList, Ops); if (Ins.empty()) { Chain = Res; } else { @@ -423,11 +422,6 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, Chain = Res.getValue(1); } - if (NumBytes) { - SDValue Unused = DAG.getTargetConstant(0, DL, PtrVT); - Chain = DAG.getCALLSEQ_END(Chain, NB, Unused, SDValue(), DL); - } - return Chain; } @@ -469,10 +463,11 @@ SDValue WebAssemblyTargetLowering::LowerReturn( } SDValue WebAssemblyTargetLowering::LowerFormalArguments( - SDValue Chain, CallingConv::ID CallConv, bool /*IsVarArg*/, + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl &Ins, SDLoc DL, SelectionDAG &DAG, SmallVectorImpl &InVals) const { MachineFunction &MF = DAG.getMachineFunction(); + auto *MFI = MF.getInfo(); if (!CallingConvSupported(CallConv)) fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); @@ -499,11 +494,22 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments( : DAG.getUNDEF(In.VT)); // Record the number and types of arguments. - MF.getInfo()->addParam(In.VT); + MFI->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. + // Varargs are copied into a buffer allocated by the caller, and a pointer to + // the buffer is passed as an argument. + if (IsVarArg) { + MVT PtrVT = getPointerTy(MF.getDataLayout()); + unsigned VarargVreg = + MF.getRegInfo().createVirtualRegister(getRegClassFor(PtrVT)); + MFI->setVarargBufferVreg(VarargVreg); + Chain = DAG.getCopyToReg( + Chain, DL, VarargVreg, + DAG.getNode(WebAssemblyISD::ARGUMENT, DL, PtrVT, + DAG.getTargetConstant(Ins.size(), DL, MVT::i32))); + MFI->addParam(PtrVT); + } return Chain; } @@ -613,15 +619,12 @@ SDValue WebAssemblyTargetLowering::LowerVASTART(SDValue Op, 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 = - Subtarget->getRegisterInfo()->getFrameRegister(DAG.getMachineFunction()); - SDValue FrameAddr = DAG.getCopyFromReg(DAG.getEntryNode(), DL, FP, PtrVT); + auto *MFI = DAG.getMachineFunction().getInfo(); const Value *SV = cast(Op.getOperand(2))->getValue(); - return DAG.getStore(Op.getOperand(0), DL, FrameAddr, Op.getOperand(1), + + SDValue ArgN = DAG.getCopyFromReg(DAG.getEntryNode(), DL, + MFI->getVarargBufferVreg(), PtrVT); + return DAG.getStore(Op.getOperand(0), DL, ArgN, Op.getOperand(1), MachinePointerInfo(SV), false, false, 0); } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h index 53f80ea..28aa111 100644 --- a/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyMachineFunctionInfo.h @@ -42,7 +42,12 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo { // One entry for each possible target reg. we expect it to be small. std::vector PhysRegs; -public: + // A virtual register holding the pointer to the vararg buffer for vararg + // functions. It is created and set in TLI::LowerFormalArguments and read by + // TLI::LowerVASTART + unsigned VarargVreg = -1U; + + public: explicit WebAssemblyFunctionInfo(MachineFunction &MF) : MF(MF) { PhysRegs.resize(WebAssembly::NUM_TARGET_REGS, -1U); } @@ -51,6 +56,12 @@ public: void addParam(MVT VT) { Params.push_back(VT); } const std::vector &getParams() const { return Params; } + unsigned getVarargBufferVreg() const { + assert(VarargVreg != -1U && "Vararg vreg hasn't been set"); + return VarargVreg; + } + void setVarargBufferVreg(unsigned Reg) { VarargVreg = Reg; } + static const unsigned UnusedReg = -1u; void stackifyVReg(unsigned VReg) { diff --git a/llvm/lib/Target/WebAssembly/known_gcc_test_failures.txt b/llvm/lib/Target/WebAssembly/known_gcc_test_failures.txt index 73add14..b540920 100644 --- a/llvm/lib/Target/WebAssembly/known_gcc_test_failures.txt +++ b/llvm/lib/Target/WebAssembly/known_gcc_test_failures.txt @@ -54,7 +54,6 @@ va-arg-22.c 980709-1.c 990127-1.c -991216-2.c frame-address.c loop-15.c diff --git a/llvm/test/CodeGen/WebAssembly/varargs.ll b/llvm/test/CodeGen/WebAssembly/varargs.ll index c5061e8..078691a 100644 --- a/llvm/test/CodeGen/WebAssembly/varargs.ll +++ b/llvm/test/CodeGen/WebAssembly/varargs.ll @@ -8,13 +8,17 @@ target triple = "wasm32-unknown-unknown" ; Test va_start. ; TODO: Test va_start. - -;define void @start(i8** %ap, ...) { -;entry: -; %0 = bitcast i8** %ap to i8* -; call void @llvm.va_start(i8* %0) -; ret void -;} +; CHECK-LABEL: start: +; CHECK-NEXT: .param i32, i32 +; CHECK-NOT: __stack_pointer +define void @start(i8** %ap, ...) { +entry: + %0 = bitcast i8** %ap to i8* +; Store the second argument (the hidden vararg buffer pointer) into ap +; CHECK: i32.store $discard=, 0($0), $1 + call void @llvm.va_start(i8* %0) + ret void +} ; Test va_end. @@ -105,7 +109,8 @@ entry: declare void @callee(...) ; CHECK-LABEL: caller_none: -; CHECK-NEXT: call callee@FUNCTION{{$}} +; CHECK-NEXT: i32.const $push0=, 0 +; CHECK-NEXT: call callee@FUNCTION, $pop0 ; CHECK-NEXT: return{{$}} define void @caller_none() { call void (...) @callee() @@ -125,6 +130,23 @@ define void @caller_some() { ret void } +; Test a va_start call in a non-entry block +; CHECK-LABEL: startbb: +; CHECK: .param i32, i32, i32 +define void @startbb(i1 %cond, i8** %ap, ...) { +entry: + br i1 %cond, label %bb0, label %bb1 +bb0: + ret void +bb1: + %0 = bitcast i8** %ap to i8* +; Store the second argument (the hidden vararg buffer pointer) into ap +; CHECK: i32.store $discard=, 0($1), $2 + call void @llvm.va_start(i8* %0) + ret void +} + + declare void @llvm.va_start(i8*) declare void @llvm.va_end(i8*) declare void @llvm.va_copy(i8*, i8*)