[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
This commit is contained in:
Derek Schuff 2016-02-10 19:51:04 +00:00
parent 5cba8d5fb5
commit 27501e2065
5 changed files with 92 additions and 115 deletions

View File

@ -46,9 +46,8 @@ bool WebAssemblyFrameLowering::hasFP(const MachineFunction &MF) const {
const MachineFrameInfo *MFI = MF.getFrameInfo();
const auto *RegInfo =
MF.getSubtarget<WebAssemblySubtarget>().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<WebAssemblySubtarget>().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<WebAssemblySubtarget>().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<WebAssemblySubtarget>().getInstrInfo();
auto &MRI = MF.getRegInfo();
unsigned OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);

View File

@ -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<SDValue, 8> 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<EVT, 8> Tys;
SmallVector<EVT, 8> 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<ISD::InputArg> &Ins, SDLoc DL, SelectionDAG &DAG,
SmallVectorImpl<SDValue> &InVals) const {
MachineFunction &MF = DAG.getMachineFunction();
auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
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<WebAssemblyFunctionInfo>()->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<WebAssemblyFunctionInfo>();
const Value *SV = cast<SrcValueSDNode>(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);
}

View File

@ -42,7 +42,12 @@ class WebAssemblyFunctionInfo final : public MachineFunctionInfo {
// One entry for each possible target reg. we expect it to be small.
std::vector<unsigned> 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<MVT> &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) {

View File

@ -54,7 +54,6 @@ va-arg-22.c
980709-1.c
990127-1.c
991216-2.c
frame-address.c
loop-15.c

View File

@ -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*)