Index: lib/Target/WebAssembly/WebAssemblyFrameLowering.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyFrameLowering.h +++ lib/Target/WebAssembly/WebAssemblyFrameLowering.h @@ -19,9 +19,12 @@ #include "llvm/Target/TargetFrameLowering.h" namespace llvm { +class MachineFrameInfo; class WebAssemblyFrameLowering final : public TargetFrameLowering { public: + static const size_t RedZoneSize = 128; + WebAssemblyFrameLowering() : TargetFrameLowering(StackGrowsDown, /*StackAlignment=*/16, /*LocalAreaOffset=*/0, @@ -38,6 +41,11 @@ bool hasFP(const MachineFunction &MF) const override; bool hasReservedCallFrame(const MachineFunction &MF) const override; + + private: + bool needsSP(const MachineFunction &MF, const MachineFrameInfo &MFI) const; + bool needsSPWriteback(const MachineFunction &MF, + const MachineFrameInfo &MFI) const; }; } // end namespace llvm Index: lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp +++ lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp @@ -34,7 +34,6 @@ #define DEBUG_TYPE "wasm-frame-info" -// TODO: Implement a red zone? // TODO: wasm64 // TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions @@ -59,6 +58,17 @@ return !MF.getFrameInfo()->hasVarSizedObjects(); } +bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF, + const MachineFrameInfo &MFI) const { + return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF); +} + +bool WebAssemblyFrameLowering::needsSPWriteback( + const MachineFunction &MF, const MachineFrameInfo &MFI) const { + return MFI.getStackSize() > RedZoneSize || MFI.hasCalls() || + MF.getFunction()->hasFnAttribute(Attribute::NoRedZone); +} + static void writeSPToMemory(unsigned SrcReg, MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator &InsertPt, @@ -88,7 +98,8 @@ assert(!I->getOperand(0).getImm() && hasFP(MF) && "Call frame pseudos should only be used for dynamic stack adjustment"); const auto *TII = MF.getSubtarget().getInstrInfo(); - if (I->getOpcode() == TII->getCallFrameDestroyOpcode()) { + if (I->getOpcode() == TII->getCallFrameDestroyOpcode() && + needsSPWriteback(MF, *MF.getFrameInfo())) { DebugLoc DL = I->getDebugLoc(); writeSPToMemory(WebAssembly::SP32, MF, MBB, I, DL); } @@ -103,8 +114,8 @@ "WebAssembly should not have callee-saved registers"); auto *WFI = MF.getInfo(); + if (!needsSP(MF, *MFI)) return; uint64_t StackSize = MFI->getStackSize(); - if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return; const auto *TII = MF.getSubtarget().getInstrInfo(); auto &MRI = MF.getRegInfo(); @@ -152,7 +163,7 @@ WebAssembly::FP32) .addReg(WebAssembly::SP32); } - if (StackSize) { + if (StackSize && needsSPWriteback(MF, *MFI)) { writeSPToMemory(WebAssembly::SP32, MF, MBB, InsertPt, DL); } } @@ -161,7 +172,7 @@ MachineBasicBlock &MBB) const { auto *MFI = MF.getFrameInfo(); uint64_t StackSize = MFI->getStackSize(); - if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return; + if (!needsSP(MF, *MFI) || !needsSPWriteback(MF, *MFI)) return; auto *WFI = MF.getInfo(); const auto *TII = MF.getSubtarget().getInstrInfo(); auto &MRI = MF.getRegInfo(); Index: test/CodeGen/WebAssembly/userstack.ll =================================================================== --- test/CodeGen/WebAssembly/userstack.ll +++ test/CodeGen/WebAssembly/userstack.ll @@ -5,10 +5,13 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown" +declare void @ext_func(i64* %ptr) +declare void @ext_func_i32(i32* %ptr) + ; CHECK-LABEL: alloca32: ; Check that there is an extra local for the stack pointer. ; CHECK: .local i32{{$}} -define void @alloca32() { +define void @alloca32() noredzone { ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer{{$}} ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]]) ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16 @@ -33,20 +36,15 @@ ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]]) ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16 ; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]] - ; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer{{$}} - ; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]] %r1 = alloca i32 %r2 = alloca double - ; CHECK: i32.const $push[[L3:.+]]=, 0 - ; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]] + ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 0 + ; CHECK-NEXT: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]] store i32 0, i32* %r1 - ; CHECK: i64.const $push[[L0:.+]]=, 0 - ; CHECK: i64.store {{.*}}=, 0([[SP]]), $pop[[L0]] + ; CHECK-NEXT: i64.const $push[[L0:.+]]=, 0 + ; CHECK-NEXT: i64.store {{.*}}=, 0([[SP]]), $pop[[L0]] store double 0.0, double* %r2 - ; CHECK: i32.const $push[[L5:.+]]=, 16 - ; CHECK-NEXT: i32.add [[SP]]=, [[SP]], $pop[[L5]] - ; CHECK-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer - ; CHECK-NEXT: i32.store $discard=, 0($pop[[L6]]), [[SP]] + ; CHECK-NEXT: return ret void } @@ -55,11 +53,11 @@ define void @allocarray() { ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]]) - ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 32{{$}} + ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 144{{$}} ; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]] ; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer{{$}} ; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]] - %r = alloca [5 x i32] + %r = alloca [33 x i32] ; CHECK-NEXT: i32.const $push[[L4:.+]]=, 12 ; CHECK-NEXT: i32.const [[L5:.+]]=, 12 @@ -68,19 +66,18 @@ ; CHECK-NEXT: i32.const $push[[L9:.+]]=, 1{{$}} ; CHECK-NEXT: i32.store $push[[L10:.+]]=, 12([[SP]]), $pop[[L9]]{{$}} ; CHECK-NEXT: i32.store $discard=, 0($pop3), $pop[[L10]]{{$}} - %p = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 0 + %p = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 0 store i32 1, i32* %p - %p2 = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 3 + %p2 = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 3 store i32 1, i32* %p2 - ; CHECK: i32.const $push[[L11:.+]]=, 32 + ; CHECK: i32.const $push[[L11:.+]]=, 144 ; CHECK-NEXT: i32.add [[SP]]=, [[SP]], $pop[[L11]] ; CHECK-NEXT: i32.const $push[[L12:.+]]=, __stack_pointer ; CHECK-NEXT: i32.store $discard=, 0($pop[[L12]]), [[SP]] ret void } -declare void @ext_func(i64* %ptr) ; CHECK-LABEL: non_mem_use define void @non_mem_use(i8** %addr) { ; CHECK: i32.const $push[[L1:.+]]=, 48 @@ -122,6 +119,7 @@ ; CHECK-NEXT: i32.store {{.*}}=, 24([[SP]]), $pop %p2 = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 3 store i32 1, i32* %p2 + call void @ext_func(i64* null); ; CHECK: i32.const $push[[L5:.+]]=, 32 ; CHECK-NEXT: i32.add [[SP]]=, [[SP]], $pop[[L5]] ; CHECK-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer @@ -142,14 +140,32 @@ ; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]] %r = alloca i32, i32 %alloc ; Target-independent codegen also calculates the store addr - store i32 0, i32* %r + ; CHECK: call ext_func_i32@FUNCTION + call void @ext_func_i32(i32* %r) ; CHECK: i32.const $push[[L3:.+]]=, __stack_pointer ; CHECK-NEXT: i32.store [[SP]]=, 0($pop[[L3]]), [[FP]] ret void } +; CHECK-LABEL: dynamic_alloca_redzone: +define void @dynamic_alloca_redzone(i32 %alloc) { + ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer + ; CHECK-NEXT: i32.load [[SP:.+]]=, 0($pop[[L1]]) + ; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]] + ; Target independent codegen bumps the stack pointer + ; CHECK: i32.sub [[R:.+]]=, + ; CHECK-NEXT: copy_local [[SP]]=, [[R]] + %r = alloca i32, i32 %alloc + ; check-next here asserts that SP is not written back. + ; CHECK-NEXT: i32.const $push[[ZERO:.+]]=, 0 + ; CHECK-NEXT: i32.store $discard=, 0([[R]]), $pop[[ZERO]] + store i32 0, i32* %r + ; CHECK-NEXT: return + ret void +} + ; CHECK-LABEL: dynamic_static_alloca: -define void @dynamic_static_alloca(i32 %alloc) { +define void @dynamic_static_alloca(i32 %alloc) noredzone { ; Decrement SP in the prolog by the static amount and writeback to memory. ; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer ; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]]) @@ -204,8 +220,8 @@ ; CHECK-NEXT: i32.load [[SP:.+]]=, 0($pop[[L1]]) ; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]] ; CHECK-NEXT: call use_i8_star@FUNCTION, [[FP]] -; CHECK-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer -; CHECK-NEXT: i32.store [[SP]]=, 0($pop[[L6]]), [[FP]] +; CHEC K-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer +; CHEC K-NEXT: i32.store [[SP]]=, 0($pop[[L6]]), [[FP]] define void @frameaddress_0() { %t = call i8* @llvm.frameaddress(i32 0) call void @use_i8_star(i8* %t)