Skip to content

Commit 8b97aba

Browse files
authored
Validation for AtomicRMW and cmpxchg (#1092)
Also fix cases where fail() had the arguments backwards. Wasn't an error because lol templates. Also fix printModuleComponent template to SFINAE on Expression* so we properly get the specialized version.
1 parent 2be8a24 commit 8b97aba

6 files changed

Lines changed: 57 additions & 30 deletions

File tree

src/wasm-validator.h

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@
4646
namespace wasm {
4747

4848
// Print anything that can be streamed to an ostream
49-
template <typename T>
49+
template <typename T,
50+
typename std::enable_if<
51+
!std::is_base_of<Expression, typename std::remove_pointer<T>::type>::value
52+
>::type* = nullptr>
5053
inline std::ostream& printModuleComponent(T curr, std::ostream& stream) {
5154
stream << curr << std::endl;
5255
return stream;
5356
}
54-
// Specialization for Expressions to print type info too
55-
template <>
57+
// Extra overload for Expressions, to print type info too
5658
inline std::ostream& printModuleComponent(Expression* curr, std::ostream& stream) {
5759
WasmPrinter::printExpression(curr, stream, false, true) << std::endl;
5860
return stream;
@@ -162,21 +164,21 @@ struct WasmValidator : public PostWalker<WasmValidator> {
162164
// helpers
163165
private:
164166
template <typename T, typename S>
165-
std::ostream& fail(T curr, S text);
167+
std::ostream& fail(S text, T curr);
166168
std::ostream& printFailureHeader();
167169

168170
template<typename T>
169171
bool shouldBeTrue(bool result, T curr, const char* text) {
170172
if (!result) {
171-
fail(curr, "unexpected false: " + std::string(text));
173+
fail("unexpected false: " + std::string(text), curr);
172174
return false;
173175
}
174176
return result;
175177
}
176178
template<typename T>
177179
bool shouldBeFalse(bool result, T curr, const char* text) {
178180
if (result) {
179-
fail(curr, "unexpected true: " + std::string(text));
181+
fail("unexpected true: " + std::string(text), curr);
180182
return false;
181183
}
182184
return result;
@@ -187,7 +189,7 @@ struct WasmValidator : public PostWalker<WasmValidator> {
187189
if (left != right) {
188190
std::ostringstream ss;
189191
ss << left << " != " << right << ": " << text;
190-
fail(curr, ss.str());
192+
fail(ss.str(), curr);
191193
return false;
192194
}
193195
return true;
@@ -198,7 +200,7 @@ struct WasmValidator : public PostWalker<WasmValidator> {
198200
if (left != unreachable && left != right) {
199201
std::ostringstream ss;
200202
ss << left << " != " << right << ": " << text;
201-
fail(curr, ss.str());
203+
fail(ss.str(), curr);
202204
return false;
203205
}
204206
return true;
@@ -209,12 +211,13 @@ struct WasmValidator : public PostWalker<WasmValidator> {
209211
if (left == right) {
210212
std::ostringstream ss;
211213
ss << left << " == " << right << ": " << text;
212-
fail(curr, ss.str());
214+
fail(ss.str(), curr);
213215
return false;
214216
}
215217
return true;
216218
}
217219

220+
void shouldBeIntOrUnreachable(WasmType ty, Expression* curr, const char* text);
218221
void validateAlignment(size_t align, WasmType type, Index bytes, bool isAtomic,
219222
Expression* curr);
220223
void validateMemBytes(uint8_t bytes, WasmType ty, Expression* curr);

src/wasm/wasm-validator.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,22 +220,46 @@ void WasmValidator::visitSetLocal(SetLocal *curr) {
220220
}
221221
}
222222
void WasmValidator::visitLoad(Load *curr) {
223+
if (curr->isAtomic && !getModule()->memory.shared) fail("Atomic operation with non-shared memory", curr);
223224
validateMemBytes(curr->bytes, curr->type, curr);
224225
validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr);
225226
shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "load pointer type must be i32");
226227
}
227228
void WasmValidator::visitStore(Store *curr) {
229+
if (curr->isAtomic && !getModule()->memory.shared) fail("Atomic operation with non-shared memory", curr);
228230
validateMemBytes(curr->bytes, curr->valueType, curr);
229231
validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr);
230232
shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "store pointer type must be i32");
231233
shouldBeUnequal(curr->value->type, none, curr, "store value type must not be none");
232234
shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->valueType, curr, "store value type must match");
233235
}
236+
void WasmValidator::shouldBeIntOrUnreachable(WasmType ty, Expression* curr, const char* text) {
237+
switch (ty) {
238+
case i32:
239+
case i64:
240+
case unreachable: {
241+
break;
242+
}
243+
default: fail(text, curr);
244+
}
245+
}
234246
void WasmValidator::visitAtomicRMW(AtomicRMW* curr) {
247+
if (!getModule()->memory.shared) fail("Atomic operation with non-shared memory", curr);
235248
validateMemBytes(curr->bytes, curr->type, curr);
249+
shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "AtomicRMW pointer type must be i32");
250+
shouldBeEqualOrFirstIsUnreachable(curr->value->type, curr->type, curr, "AtomicRMW result type must match operand");
251+
shouldBeIntOrUnreachable(curr->type, curr, "Atomic operations are only valid on int types");
236252
}
237253
void WasmValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
254+
if (!getModule()->memory.shared) fail("Atomic operation with non-shared memory", curr);
238255
validateMemBytes(curr->bytes, curr->type, curr);
256+
shouldBeEqualOrFirstIsUnreachable(curr->ptr->type, i32, curr, "cmpxchg pointer type must be i32");
257+
if (curr->expected->type != unreachable && curr->replacement->type != unreachable) {
258+
shouldBeEqual(curr->expected->type, curr->replacement->type, curr, "cmpxchg operand types must match");
259+
}
260+
shouldBeEqualOrFirstIsUnreachable(curr->expected->type, curr->type, curr, "Cmpxchg result type must match expected");
261+
shouldBeEqualOrFirstIsUnreachable(curr->replacement->type, curr->type, curr, "Cmpxchg result type must match replacement");
262+
shouldBeIntOrUnreachable(curr->expected->type, curr, "Atomic operations are only valid on int types");
239263
}
240264
void WasmValidator::validateMemBytes(uint8_t bytes, WasmType ty, Expression* curr) {
241265
switch (bytes) {
@@ -655,7 +679,7 @@ void WasmValidator::validateBinaryenIR(Module& wasm) {
655679
}
656680

657681
template <typename T, typename S>
658-
std::ostream& WasmValidator::fail(T curr, S text) {
682+
std::ostream& WasmValidator::fail(S text, T curr) {
659683
valid = false;
660684
auto& ret = printFailureHeader() << text << ", on \n";
661685
return printModuleComponent(curr, ret);

test/atomics.wast

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
)
105105
(func $atomic-cmpxchg (type $0)
106106
(local $0 i32)
107-
(local $1 i32)
107+
(local $1 i64)
108108
(drop
109109
(i32.atomic.rmw.cmpxchg offset=4
110110
(get_local $0)
@@ -122,15 +122,15 @@
122122
(drop
123123
(i64.atomic.rmw.cmpxchg offset=4
124124
(get_local $0)
125-
(get_local $0)
126-
(get_local $0)
125+
(get_local $1)
126+
(get_local $1)
127127
)
128128
)
129129
(drop
130130
(i64.atomic.rmw32_u.cmpxchg align=4
131131
(get_local $0)
132-
(get_local $0)
133-
(get_local $0)
132+
(get_local $1)
133+
(get_local $1)
134134
)
135135
)
136136
)

test/atomics.wast.from-wast

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
)
105105
(func $atomic-cmpxchg (type $0)
106106
(local $0 i32)
107-
(local $1 i32)
107+
(local $1 i64)
108108
(drop
109109
(i32.atomic.rmw.cmpxchg offset=4
110110
(get_local $0)
@@ -122,15 +122,15 @@
122122
(drop
123123
(i64.atomic.rmw.cmpxchg offset=4
124124
(get_local $0)
125-
(get_local $0)
126-
(get_local $0)
125+
(get_local $1)
126+
(get_local $1)
127127
)
128128
)
129129
(drop
130130
(i64.atomic.rmw32_u.cmpxchg
131131
(get_local $0)
132-
(get_local $0)
133-
(get_local $0)
132+
(get_local $1)
133+
(get_local $1)
134134
)
135135
)
136136
)

test/atomics.wast.fromBinary

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
)
109109
(func $atomic-cmpxchg (type $0)
110110
(local $var$0 i32)
111-
(local $var$1 i32)
111+
(local $var$1 i64)
112112
(block $label$0
113113
(drop
114114
(i32.atomic.rmw.cmpxchg offset=4
@@ -127,15 +127,15 @@
127127
(drop
128128
(i64.atomic.rmw.cmpxchg offset=4
129129
(get_local $var$0)
130-
(get_local $var$0)
131-
(get_local $var$0)
130+
(get_local $var$1)
131+
(get_local $var$1)
132132
)
133133
)
134134
(drop
135135
(i64.atomic.rmw32_u.cmpxchg
136136
(get_local $var$0)
137-
(get_local $var$0)
138-
(get_local $var$0)
137+
(get_local $var$1)
138+
(get_local $var$1)
139139
)
140140
)
141141
)

test/atomics.wast.fromBinary.noDebugInfo

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@
108108
)
109109
(func $2 (type $0)
110110
(local $var$0 i32)
111-
(local $var$1 i32)
111+
(local $var$1 i64)
112112
(block $label$0
113113
(drop
114114
(i32.atomic.rmw.cmpxchg offset=4
@@ -127,15 +127,15 @@
127127
(drop
128128
(i64.atomic.rmw.cmpxchg offset=4
129129
(get_local $var$0)
130-
(get_local $var$0)
131-
(get_local $var$0)
130+
(get_local $var$1)
131+
(get_local $var$1)
132132
)
133133
)
134134
(drop
135135
(i64.atomic.rmw32_u.cmpxchg
136136
(get_local $var$0)
137-
(get_local $var$0)
138-
(get_local $var$0)
137+
(get_local $var$1)
138+
(get_local $var$1)
139139
)
140140
)
141141
)

0 commit comments

Comments
 (0)