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
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);
}
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();
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);
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();
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 :
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.
// 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");
"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 {
Chain = Res.getValue(1);
}
- if (NumBytes) {
- SDValue Unused = DAG.getTargetConstant(0, DL, PtrVT);
- Chain = DAG.getCALLSEQ_END(Chain, NB, Unused, SDValue(), DL);
- }
-
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();
+ auto *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
if (!CallingConvSupported(CallConv))
fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions");
: 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;
}
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);
}
// 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);
}
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) {
980709-1.c
990127-1.c
-991216-2.c
frame-address.c
loop-15.c
; 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.
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()
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*)