Skip to content

Commit 4b3bb21

Browse files
committedFeb 23, 2016
[WebAssembly] Implement red zone for user stack
Implements a mostly-conventional redzone for the userspace stack. Because we have unsigned load/store offsets we continue to use a local SP subtracted from the incoming SP but do not write it back to memory. Differential Revision: http://reviews.llvm.org/D17525 llvm-svn: 261662
1 parent 1b9fae5 commit 4b3bb21

File tree

3 files changed

+68
-26
lines changed

3 files changed

+68
-26
lines changed
 

‎llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.cpp

+23-5
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ using namespace llvm;
3434

3535
#define DEBUG_TYPE "wasm-frame-info"
3636

37-
// TODO: Implement a red zone?
3837
// TODO: wasm64
3938
// TODO: Emit TargetOpcode::CFI_INSTRUCTION instructions
4039

@@ -59,6 +58,24 @@ bool WebAssemblyFrameLowering::hasReservedCallFrame(
5958
return !MF.getFrameInfo()->hasVarSizedObjects();
6059
}
6160

61+
62+
/// Returns true if this function needs a local user-space stack pointer.
63+
/// Unlike a machine stack pointer, the wasm user stack pointer is a global
64+
/// variable, so it is loaded into a register in the prolog.
65+
bool WebAssemblyFrameLowering::needsSP(const MachineFunction &MF,
66+
const MachineFrameInfo &MFI) const {
67+
return MFI.getStackSize() || MFI.adjustsStack() || hasFP(MF);
68+
}
69+
70+
/// Returns true if the local user-space stack pointer needs to be written back
71+
/// to memory by this function (this is not meaningful if needsSP is false). If
72+
/// false, the stack red zone can be used and only a local SP is needed.
73+
bool WebAssemblyFrameLowering::needsSPWriteback(
74+
const MachineFunction &MF, const MachineFrameInfo &MFI) const {
75+
return MFI.getStackSize() > RedZoneSize || MFI.hasCalls() ||
76+
MF.getFunction()->hasFnAttribute(Attribute::NoRedZone);
77+
}
78+
6279
static void writeSPToMemory(unsigned SrcReg, MachineFunction &MF,
6380
MachineBasicBlock &MBB,
6481
MachineBasicBlock::iterator &InsertPt,
@@ -88,7 +105,8 @@ void WebAssemblyFrameLowering::eliminateCallFramePseudoInstr(
88105
assert(!I->getOperand(0).getImm() && hasFP(MF) &&
89106
"Call frame pseudos should only be used for dynamic stack adjustment");
90107
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
91-
if (I->getOpcode() == TII->getCallFrameDestroyOpcode()) {
108+
if (I->getOpcode() == TII->getCallFrameDestroyOpcode() &&
109+
needsSPWriteback(MF, *MF.getFrameInfo())) {
92110
DebugLoc DL = I->getDebugLoc();
93111
writeSPToMemory(WebAssembly::SP32, MF, MBB, I, DL);
94112
}
@@ -103,8 +121,8 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
103121
"WebAssembly should not have callee-saved registers");
104122
auto *WFI = MF.getInfo<WebAssemblyFunctionInfo>();
105123

124+
if (!needsSP(MF, *MFI)) return;
106125
uint64_t StackSize = MFI->getStackSize();
107-
if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return;
108126

109127
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
110128
auto &MRI = MF.getRegInfo();
@@ -152,7 +170,7 @@ void WebAssemblyFrameLowering::emitPrologue(MachineFunction &MF,
152170
WebAssembly::FP32)
153171
.addReg(WebAssembly::SP32);
154172
}
155-
if (StackSize) {
173+
if (StackSize && needsSPWriteback(MF, *MFI)) {
156174
writeSPToMemory(WebAssembly::SP32, MF, MBB, InsertPt, DL);
157175
}
158176
}
@@ -161,7 +179,7 @@ void WebAssemblyFrameLowering::emitEpilogue(MachineFunction &MF,
161179
MachineBasicBlock &MBB) const {
162180
auto *MFI = MF.getFrameInfo();
163181
uint64_t StackSize = MFI->getStackSize();
164-
if (!StackSize && !MFI->adjustsStack() && !hasFP(MF)) return;
182+
if (!needsSP(MF, *MFI) || !needsSPWriteback(MF, *MFI)) return;
165183
auto *WFI = MF.getInfo<WebAssemblyFunctionInfo>();
166184
const auto *TII = MF.getSubtarget<WebAssemblySubtarget>().getInstrInfo();
167185
auto &MRI = MF.getRegInfo();

‎llvm/lib/Target/WebAssembly/WebAssemblyFrameLowering.h

+8
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
#include "llvm/Target/TargetFrameLowering.h"
2020

2121
namespace llvm {
22+
class MachineFrameInfo;
2223

2324
class WebAssemblyFrameLowering final : public TargetFrameLowering {
2425
public:
26+
static const size_t RedZoneSize = 128;
27+
2528
WebAssemblyFrameLowering()
2629
: TargetFrameLowering(StackGrowsDown, /*StackAlignment=*/16,
2730
/*LocalAreaOffset=*/0,
@@ -38,6 +41,11 @@ class WebAssemblyFrameLowering final : public TargetFrameLowering {
3841

3942
bool hasFP(const MachineFunction &MF) const override;
4043
bool hasReservedCallFrame(const MachineFunction &MF) const override;
44+
45+
private:
46+
bool needsSP(const MachineFunction &MF, const MachineFrameInfo &MFI) const;
47+
bool needsSPWriteback(const MachineFunction &MF,
48+
const MachineFrameInfo &MFI) const;
4149
};
4250

4351
} // end namespace llvm

‎llvm/test/CodeGen/WebAssembly/userstack.ll

+37-21
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
66
target triple = "wasm32-unknown-unknown"
77

8+
declare void @ext_func(i64* %ptr)
9+
declare void @ext_func_i32(i32* %ptr)
10+
811
; CHECK-LABEL: alloca32:
912
; Check that there is an extra local for the stack pointer.
1013
; CHECK: .local i32{{$}}
11-
define void @alloca32() {
14+
define void @alloca32() noredzone {
1215
; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer{{$}}
1316
; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
1417
; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16
@@ -33,20 +36,15 @@ define void @alloca3264() {
3336
; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
3437
; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16
3538
; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]]
36-
; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer{{$}}
37-
; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]]
3839
%r1 = alloca i32
3940
%r2 = alloca double
40-
; CHECK: i32.const $push[[L3:.+]]=, 0
41-
; CHECK: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]]
41+
; CHECK-NEXT: i32.const $push[[L3:.+]]=, 0
42+
; CHECK-NEXT: i32.store {{.*}}=, 12([[SP]]), $pop[[L3]]
4243
store i32 0, i32* %r1
43-
; CHECK: i64.const $push[[L0:.+]]=, 0
44-
; CHECK: i64.store {{.*}}=, 0([[SP]]), $pop[[L0]]
44+
; CHECK-NEXT: i64.const $push[[L0:.+]]=, 0
45+
; CHECK-NEXT: i64.store {{.*}}=, 0([[SP]]), $pop[[L0]]
4546
store double 0.0, double* %r2
46-
; CHECK: i32.const $push[[L5:.+]]=, 16
47-
; CHECK-NEXT: i32.add [[SP]]=, [[SP]], $pop[[L5]]
48-
; CHECK-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer
49-
; CHECK-NEXT: i32.store $discard=, 0($pop[[L6]]), [[SP]]
47+
; CHECK-NEXT: return
5048
ret void
5149
}
5250

@@ -55,11 +53,11 @@ define void @alloca3264() {
5553
define void @allocarray() {
5654
; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
5755
; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
58-
; CHECK-NEXT: i32.const $push[[L3:.+]]=, 32{{$}}
56+
; CHECK-NEXT: i32.const $push[[L3:.+]]=, 144{{$}}
5957
; CHECK-NEXT: i32.sub [[SP:.+]]=, $pop[[L2]], $pop[[L3]]
6058
; CHECK-NEXT: i32.const $push[[L4:.+]]=, __stack_pointer{{$}}
6159
; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]]
62-
%r = alloca [5 x i32]
60+
%r = alloca [33 x i32]
6361

6462
; CHECK-NEXT: i32.const $push[[L4:.+]]=, 12
6563
; CHECK-NEXT: i32.const [[L5:.+]]=, 12
@@ -68,19 +66,18 @@ define void @allocarray() {
6866
; CHECK-NEXT: i32.const $push[[L9:.+]]=, 1{{$}}
6967
; CHECK-NEXT: i32.store $push[[L10:.+]]=, 12([[SP]]), $pop[[L9]]{{$}}
7068
; CHECK-NEXT: i32.store $discard=, 0($pop3), $pop[[L10]]{{$}}
71-
%p = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 0
69+
%p = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 0
7270
store i32 1, i32* %p
73-
%p2 = getelementptr [5 x i32], [5 x i32]* %r, i32 0, i32 3
71+
%p2 = getelementptr [33 x i32], [33 x i32]* %r, i32 0, i32 3
7472
store i32 1, i32* %p2
7573

76-
; CHECK: i32.const $push[[L11:.+]]=, 32
74+
; CHECK: i32.const $push[[L11:.+]]=, 144
7775
; CHECK-NEXT: i32.add [[SP]]=, [[SP]], $pop[[L11]]
7876
; CHECK-NEXT: i32.const $push[[L12:.+]]=, __stack_pointer
7977
; CHECK-NEXT: i32.store $discard=, 0($pop[[L12]]), [[SP]]
8078
ret void
8179
}
8280

83-
declare void @ext_func(i64* %ptr)
8481
; CHECK-LABEL: non_mem_use
8582
define void @non_mem_use(i8** %addr) {
8683
; CHECK: i32.const $push[[L1:.+]]=, 48
@@ -122,6 +119,7 @@ define void @allocarray_inbounds() {
122119
; CHECK-NEXT: i32.store {{.*}}=, 24([[SP]]), $pop
123120
%p2 = getelementptr inbounds [5 x i32], [5 x i32]* %r, i32 0, i32 3
124121
store i32 1, i32* %p2
122+
call void @ext_func(i64* null);
125123
; CHECK: i32.const $push[[L5:.+]]=, 32
126124
; CHECK-NEXT: i32.add [[SP]]=, [[SP]], $pop[[L5]]
127125
; CHECK-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer
@@ -142,14 +140,32 @@ define void @dynamic_alloca(i32 %alloc) {
142140
; CHECK-NEXT: i32.store $discard=, 0($pop[[L4]]), [[SP]]
143141
%r = alloca i32, i32 %alloc
144142
; Target-independent codegen also calculates the store addr
145-
store i32 0, i32* %r
143+
; CHECK: call ext_func_i32@FUNCTION
144+
call void @ext_func_i32(i32* %r)
146145
; CHECK: i32.const $push[[L3:.+]]=, __stack_pointer
147146
; CHECK-NEXT: i32.store [[SP]]=, 0($pop[[L3]]), [[FP]]
148147
ret void
149148
}
150149

150+
; CHECK-LABEL: dynamic_alloca_redzone:
151+
define void @dynamic_alloca_redzone(i32 %alloc) {
152+
; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
153+
; CHECK-NEXT: i32.load [[SP:.+]]=, 0($pop[[L1]])
154+
; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]]
155+
; Target independent codegen bumps the stack pointer
156+
; CHECK: i32.sub [[R:.+]]=,
157+
; CHECK-NEXT: copy_local [[SP]]=, [[R]]
158+
%r = alloca i32, i32 %alloc
159+
; check-next here asserts that SP is not written back.
160+
; CHECK-NEXT: i32.const $push[[ZERO:.+]]=, 0
161+
; CHECK-NEXT: i32.store $discard=, 0([[R]]), $pop[[ZERO]]
162+
store i32 0, i32* %r
163+
; CHECK-NEXT: return
164+
ret void
165+
}
166+
151167
; CHECK-LABEL: dynamic_static_alloca:
152-
define void @dynamic_static_alloca(i32 %alloc) {
168+
define void @dynamic_static_alloca(i32 %alloc) noredzone {
153169
; Decrement SP in the prolog by the static amount and writeback to memory.
154170
; CHECK: i32.const $push[[L1:.+]]=, __stack_pointer
155171
; CHECK-NEXT: i32.load $push[[L2:.+]]=, 0($pop[[L1]])
@@ -204,8 +220,8 @@ declare i8* @llvm.frameaddress(i32)
204220
; CHECK-NEXT: i32.load [[SP:.+]]=, 0($pop[[L1]])
205221
; CHECK-NEXT: copy_local [[FP:.+]]=, [[SP]]
206222
; CHECK-NEXT: call use_i8_star@FUNCTION, [[FP]]
207-
; CHECK-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer
208-
; CHECK-NEXT: i32.store [[SP]]=, 0($pop[[L6]]), [[FP]]
223+
; CHEC K-NEXT: i32.const $push[[L6:.+]]=, __stack_pointer
224+
; CHEC K-NEXT: i32.store [[SP]]=, 0($pop[[L6]]), [[FP]]
209225
define void @frameaddress_0() {
210226
%t = call i8* @llvm.frameaddress(i32 0)
211227
call void @use_i8_star(i8* %t)

0 commit comments

Comments
 (0)