31
31
32
32
namespace wasm {
33
33
34
+ // The base is where the stack begins. As it goes down, that is the highest
35
+ // valid address.
36
+ static Name STACK_BASE (" __stack_base" );
37
+ // The limit is the farthest it can grow to, which is the lowest valid address.
34
38
static Name STACK_LIMIT (" __stack_limit" );
39
+ // Old version, which sets the limit.
40
+ // TODO: remove this
35
41
static Name SET_STACK_LIMIT (" __set_stack_limit" );
42
+ // New version, which sets the base and the limit.
43
+ static Name SET_STACK_LIMITS (" __set_stack_limits" );
36
44
37
45
static void importStackOverflowHandler (Module& module, Name name) {
38
46
ImportInfo info (module);
@@ -55,34 +63,43 @@ static void addExportedFunction(Module& module, Function* function) {
55
63
module.addExport (export_);
56
64
}
57
65
58
- static void generateSetStackLimitFunction (Module& module) {
66
+ static void generateSetStackLimitFunctions (Module& module) {
59
67
Builder builder (module);
60
- Function* function =
68
+ // One-parameter version
69
+ Function* limitFunc =
61
70
builder.makeFunction (SET_STACK_LIMIT, Signature (Type::i32, Type::none), {});
62
71
LocalGet* getArg = builder.makeLocalGet (0 , Type::i32);
63
72
Expression* store = builder.makeGlobalSet (STACK_LIMIT, getArg);
64
- function->body = store;
65
- addExportedFunction (module, function);
73
+ limitFunc->body = store;
74
+ addExportedFunction (module, limitFunc);
75
+ // Two-parameter version
76
+ Function* limitsFunc = builder.makeFunction (
77
+ SET_STACK_LIMITS, Signature ({Type::i32, Type::i32}, Type::none), {});
78
+ LocalGet* getBase = builder.makeLocalGet (0 , Type::i32);
79
+ Expression* storeBase = builder.makeGlobalSet (STACK_BASE, getBase);
80
+ LocalGet* getLimit = builder.makeLocalGet (1 , Type::i32);
81
+ Expression* storeLimit = builder.makeGlobalSet (STACK_LIMIT, getLimit);
82
+ limitsFunc->body = builder.makeBlock ({storeBase, storeLimit});
83
+ addExportedFunction (module, limitsFunc);
66
84
}
67
85
68
- struct EnforceStackLimit : public WalkerPass <PostWalker<EnforceStackLimit>> {
69
- EnforceStackLimit (Global* stackPointer,
70
- Global* stackLimit,
71
- Builder& builder,
72
- Name handler)
73
- : stackPointer(stackPointer), stackLimit(stackLimit), builder(builder),
74
- handler (handler) {}
86
+ struct EnforceStackLimits : public WalkerPass <PostWalker<EnforceStackLimits>> {
87
+ EnforceStackLimits (Global* stackPointer,
88
+ Global* stackBase,
89
+ Global* stackLimit,
90
+ Builder& builder,
91
+ Name handler)
92
+ : stackPointer(stackPointer), stackBase(stackBase), stackLimit(stackLimit),
93
+ builder (builder), handler(handler) {}
75
94
76
95
bool isFunctionParallel () override { return true ; }
77
96
78
97
Pass* create () override {
79
- return new EnforceStackLimit (stackPointer, stackLimit, builder, handler);
98
+ return new EnforceStackLimits (
99
+ stackPointer, stackBase, stackLimit, builder, handler);
80
100
}
81
101
82
- Expression* stackBoundsCheck (Function* func,
83
- Expression* value,
84
- Global* stackPointer,
85
- Global* stackLimit) {
102
+ Expression* stackBoundsCheck (Function* func, Expression* value) {
86
103
// Add a local to store the value of the expression. We need the value
87
104
// twice: once to check if it has overflowed, and again to assign to store
88
105
// it.
@@ -95,12 +112,18 @@ struct EnforceStackLimit : public WalkerPass<PostWalker<EnforceStackLimit>> {
95
112
} else {
96
113
handlerExpr = builder.makeUnreachable ();
97
114
}
98
- // (if (i32.lt_u (local.tee $newSP (...val...)) (global.get $__stack_limit))
115
+ // If it is >= the base or <= the limit, then error.
99
116
auto check = builder.makeIf (
100
117
builder.makeBinary (
101
- BinaryOp::LtUInt32,
102
- builder.makeLocalTee (newSP, value, stackPointer->type ),
103
- builder.makeGlobalGet (stackLimit->name , stackLimit->type )),
118
+ BinaryOp::OrInt32,
119
+ builder.makeBinary (
120
+ BinaryOp::GtUInt32,
121
+ builder.makeLocalTee (newSP, value, stackPointer->type ),
122
+ builder.makeGlobalGet (stackBase->name , stackBase->type )),
123
+ builder.makeBinary (
124
+ BinaryOp::LtUInt32,
125
+ builder.makeLocalGet (newSP, stackPointer->type ),
126
+ builder.makeGlobalGet (stackLimit->name , stackLimit->type ))),
104
127
handlerExpr);
105
128
// (global.set $__stack_pointer (local.get $newSP))
106
129
auto newSet = builder.makeGlobalSet (
@@ -110,13 +133,13 @@ struct EnforceStackLimit : public WalkerPass<PostWalker<EnforceStackLimit>> {
110
133
111
134
void visitGlobalSet (GlobalSet* curr) {
112
135
if (getModule ()->getGlobalOrNull (curr->name ) == stackPointer) {
113
- replaceCurrent (
114
- stackBoundsCheck (getFunction (), curr->value , stackPointer, stackLimit));
136
+ replaceCurrent (stackBoundsCheck (getFunction (), curr->value ));
115
137
}
116
138
}
117
139
118
140
private:
119
141
Global* stackPointer;
142
+ Global* stackBase;
120
143
Global* stackLimit;
121
144
Builder& builder;
122
145
Name handler;
@@ -139,16 +162,22 @@ struct StackCheck : public Pass {
139
162
}
140
163
141
164
Builder builder (*module);
165
+ Global* stackBase = builder.makeGlobal (STACK_BASE,
166
+ stackPointer->type ,
167
+ builder.makeConst (int32_t (0 )),
168
+ Builder::Mutable);
169
+ module->addGlobal (stackBase);
170
+
142
171
Global* stackLimit = builder.makeGlobal (STACK_LIMIT,
143
172
stackPointer->type ,
144
173
builder.makeConst (int32_t (0 )),
145
174
Builder::Mutable);
146
175
module->addGlobal (stackLimit);
147
176
148
177
PassRunner innerRunner (module);
149
- EnforceStackLimit (stackPointer, stackLimit, builder, handler)
178
+ EnforceStackLimits (stackPointer, stackBase , stackLimit, builder, handler)
150
179
.run (&innerRunner, module);
151
- generateSetStackLimitFunction (*module);
180
+ generateSetStackLimitFunctions (*module);
152
181
}
153
182
};
154
183
0 commit comments