forked from OSchip/llvm-project
[WebAssembly] Support frame pointer
Add support for frame pointer use in prolog/epilog. Supports dynamic allocas but not yet over-aligned locals. Target-independend CG generates SP updates, but we still need to write back the SP value to memory when necessary. llvm-svn: 259220
This commit is contained in:
parent
0df98ff913
commit
6ea637af35
|
@ -70,6 +70,7 @@ static void adjustStackPointer(unsigned StackSize,
|
|||
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");
|
||||
|
@ -82,8 +83,8 @@ static void adjustStackPointer(unsigned StackSize,
|
|||
auto *LoadMMO = new MachineMemOperand(MachinePointerInfo(),
|
||||
MachineMemOperand::MOLoad, 4, 4);
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32), SPReg)
|
||||
.addImm(0)
|
||||
.addReg(SPReg)
|
||||
.addImm(0) // offset
|
||||
.addReg(SPReg) // addr
|
||||
.addImm(2) // p2align
|
||||
.addMemOperand(LoadMMO);
|
||||
// Add/Subtract the frame size
|
||||
|
@ -116,6 +117,9 @@ void WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
|
|||
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);
|
||||
|
@ -128,23 +132,76 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
|
|||
auto *MFI = MF.getFrameInfo();
|
||||
assert(MFI->getCalleeSavedInfo().empty() &&
|
||||
"WebAssembly should not have callee-saved registers");
|
||||
assert(!hasFP(MF) && "Functions needing frame pointers not yet supported");
|
||||
|
||||
uint64_t StackSize = MFI->getStackSize();
|
||||
if (!StackSize && (!MFI->adjustsStack() || MFI->getMaxCallFrameSize() == 0))
|
||||
if (!StackSize && !MFI->adjustsStack())
|
||||
return;
|
||||
|
||||
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
|
||||
auto &MRI = MF.getRegInfo();
|
||||
|
||||
auto InsertPt = MBB.begin();
|
||||
DebugLoc DL;
|
||||
|
||||
adjustStackPointer(StackSize, false, MF, MBB, TII, InsertPt, DL);
|
||||
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);
|
||||
// Load the SP value.
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::LOAD_I32),
|
||||
StackSize ? SPReg : WebAssembly::SP32)
|
||||
.addImm(0) // offset
|
||||
.addReg(SPReg) // addr
|
||||
.addImm(2) // p2align
|
||||
.addMemOperand(LoadMMO);
|
||||
|
||||
unsigned OffsetReg = 0;
|
||||
if (StackSize) {
|
||||
// Subtract the frame size
|
||||
OffsetReg = MRI.createVirtualRegister(&WebAssembly::I32RegClass);
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
|
||||
.addImm(StackSize);
|
||||
BuildMI(MBB, InsertPt, DL,
|
||||
TII->get(WebAssembly::SUB_I32),
|
||||
WebAssembly::SP32)
|
||||
.addReg(SPReg)
|
||||
.addReg(OffsetReg);
|
||||
}
|
||||
if (hasFP(MF)) {
|
||||
// Unlike most conventional targets (where FP points to the saved FP),
|
||||
// FP points to the bottom of the fixed-size locals, so we can use positive
|
||||
// offsets in load/store instructions.
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::COPY_LOCAL_I32),
|
||||
WebAssembly::FP32)
|
||||
.addReg(WebAssembly::SP32);
|
||||
}
|
||||
if (StackSize) {
|
||||
assert(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::emitEpilogue(MachineFunction &MF,
|
||||
MachineBasicBlock &MBB) const {
|
||||
uint64_t StackSize = MF.getFrameInfo()->getStackSize();
|
||||
if (!StackSize)
|
||||
auto *MFI = MF.getFrameInfo();
|
||||
uint64_t StackSize = MFI->getStackSize();
|
||||
if (!StackSize && !MFI->adjustsStack())
|
||||
return;
|
||||
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
|
||||
auto &MRI = MF.getRegInfo();
|
||||
|
@ -156,14 +213,17 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
|
|||
DL = InsertPt->getDebugLoc();
|
||||
}
|
||||
|
||||
// Restore the stack pointer. Without FP its value is just SP32 - stacksize
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
|
||||
.addImm(StackSize);
|
||||
// Restore the stack pointer. If we had fixed-size locals, add the offset
|
||||
// subtracted in the prolog.
|
||||
if (StackSize) {
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
|
||||
.addImm(StackSize);
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), WebAssembly::SP32)
|
||||
.addReg(hasFP(MF) ? WebAssembly::FP32 : WebAssembly::SP32)
|
||||
.addReg(OffsetReg);
|
||||
}
|
||||
|
||||
auto *SPSymbol = MF.createExternalSymbolName("__stack_pointer");
|
||||
// TODO: Fold this add into the const offset field of the store.
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::ADD_I32), WebAssembly::SP32)
|
||||
.addReg(WebAssembly::SP32)
|
||||
.addReg(OffsetReg);
|
||||
// Re-use OffsetReg to hold the address of the stacktop
|
||||
BuildMI(MBB, InsertPt, DL, TII->get(WebAssembly::CONST_I32), OffsetReg)
|
||||
.addExternalSymbol(SPSymbol);
|
||||
|
@ -173,6 +233,6 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
|
|||
.addImm(0)
|
||||
.addReg(OffsetReg)
|
||||
.addImm(2) // p2align
|
||||
.addReg(WebAssembly::SP32)
|
||||
.addReg((!StackSize && hasFP(MF)) ? WebAssembly::FP32 : WebAssembly::SP32)
|
||||
.addMemOperand(MMO);
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ void WebAssemblyInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
|
|||
const TargetRegisterClass *RC =
|
||||
TargetRegisterInfo::isVirtualRegister(DestReg)
|
||||
? MRI.getRegClass(DestReg)
|
||||
: MRI.getTargetRegisterInfo()->getMinimalPhysRegClass(SrcReg);
|
||||
: MRI.getTargetRegisterInfo()->getMinimalPhysRegClass(DestReg);
|
||||
|
||||
unsigned CopyLocalOpcode;
|
||||
if (RC == &WebAssembly::I32RegClass)
|
||||
|
|
|
@ -108,10 +108,15 @@ bool WebAssemblyRegNumbering::runOnMachineFunction(MachineFunction &MF) {
|
|||
}
|
||||
}
|
||||
// Allocate locals for used physical registers
|
||||
if (FrameInfo.getStackSize() > 0) {
|
||||
if (FrameInfo.getStackSize() > 0 || FrameInfo.adjustsStack()) {
|
||||
DEBUG(dbgs() << "PReg SP " << CurReg << "\n");
|
||||
MFI.addPReg(WebAssembly::SP32, CurReg++);
|
||||
}
|
||||
bool HasFP = MF.getSubtarget().getFrameLowering()->hasFP(MF);
|
||||
if (HasFP) {
|
||||
DEBUG(dbgs() << "PReg FP " << CurReg << "\n");
|
||||
MFI.addPReg(WebAssembly::FP32, CurReg++);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -119,9 +119,36 @@ define void @allocarray_inbounds() {
|
|||
|
||||
; CHECK-LABEL: dynamic_alloca:
|
||||
define void @dynamic_alloca(i32 %alloc) {
|
||||
; TODO: Support frame pointers
|
||||
;%r = alloca i32, i32 %alloc
|
||||
;store i32 0, i32* %r
|
||||
; CHECK: i32.const [[L0:.+]]=, __stack_pointer
|
||||
; CHECK-NEXT: i32.load [[SP:.+]]=, 0([[L0]])
|
||||
; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]]
|
||||
; Target independent codegen bumps the stack pointer
|
||||
; FIXME: we need to write the value back to memory
|
||||
%r = alloca i32, i32 %alloc
|
||||
; Target-independent codegen also calculates the store addr
|
||||
store i32 0, i32* %r
|
||||
; CHECK: i32.const [[L3:.+]]=, __stack_pointer
|
||||
; CHECK-NEXT: i32.store [[SP]]=, 0([[L3]]), [[FP]]
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
; CHECK-LABEL: dynamic_static_alloca:
|
||||
define void @dynamic_static_alloca(i32 %alloc) {
|
||||
; CHECK: i32.const [[L0:.+]]=, __stack_pointer
|
||||
; CHECK-NEXT: i32.load [[L0]]=, 0([[L0]])
|
||||
; CHECK-NEXT: i32.const [[L2:.+]]=, 16
|
||||
; CHECK-NEXT: i32.sub [[SP:.+]]=, [[L0]], [[L2]]
|
||||
; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]]
|
||||
; CHECK-NEXT: i32.const [[L3:.+]]=, __stack_pointer
|
||||
; CHECK-NEXT: i32.store {{.*}}=, 0([[L3]]), [[SP]]
|
||||
%r1 = alloca i32
|
||||
%r = alloca i32, i32 %alloc
|
||||
store i32 0, i32* %r
|
||||
; CHECK: i32.const [[L3:.+]]=, 16
|
||||
; CHECK: i32.add [[SP]]=, [[FP]], [[L3]]
|
||||
; CHECK: i32.const [[L4:.+]]=, __stack_pointer
|
||||
; CHECK-NEXT: i32.store [[SP]]=, 0([[L4]]), [[SP]]
|
||||
ret void
|
||||
}
|
||||
; TODO: test aligned alloc
|
||||
|
|
Loading…
Reference in New Issue