1 /*
2 * Copyright 2017 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <mutex>
18 #include <set>
19 #include <sstream>
20 #include <unordered_set>
21
22 #include "ir/features.h"
23 #include "ir/global-utils.h"
24 #include "ir/module-utils.h"
25 #include "ir/stack-utils.h"
26 #include "ir/utils.h"
27 #include "support/colors.h"
28 #include "wasm-printing.h"
29 #include "wasm-validator.h"
30 #include "wasm.h"
31
32 namespace wasm {
33
34 // Print anything that can be streamed to an ostream
35 template<typename T,
36 typename std::enable_if<!std::is_base_of<
37 Expression,
38 typename std::remove_pointer<T>::type>::value>::type* = nullptr>
printModuleComponent(T curr,std::ostream & stream)39 inline std::ostream& printModuleComponent(T curr, std::ostream& stream) {
40 stream << curr << std::endl;
41 return stream;
42 }
43
44 // Extra overload for Expressions, to print type info too
printModuleComponent(Expression * curr,std::ostream & stream)45 inline std::ostream& printModuleComponent(Expression* curr,
46 std::ostream& stream) {
47 WasmPrinter::printExpression(curr, stream, false, true) << std::endl;
48 return stream;
49 }
50
51 // For parallel validation, we have a helper struct for coordination
52 struct ValidationInfo {
53 bool validateWeb;
54 bool validateGlobally;
55 bool quiet;
56
57 std::atomic<bool> valid;
58
59 // a stream of error test for each function. we print in the right order at
60 // the end, for deterministic output
61 // note errors are rare/unexpected, so it's ok to use a slow mutex here
62 std::mutex mutex;
63 std::unordered_map<Function*, std::unique_ptr<std::ostringstream>> outputs;
64
ValidationInfowasm::ValidationInfo65 ValidationInfo() { valid.store(true); }
66
getStreamwasm::ValidationInfo67 std::ostringstream& getStream(Function* func) {
68 std::unique_lock<std::mutex> lock(mutex);
69 auto iter = outputs.find(func);
70 if (iter != outputs.end()) {
71 return *(iter->second.get());
72 }
73 auto& ret = outputs[func] = make_unique<std::ostringstream>();
74 return *ret.get();
75 }
76
77 // printing and error handling support
78
79 template<typename T, typename S>
failwasm::ValidationInfo80 std::ostream& fail(S text, T curr, Function* func) {
81 valid.store(false);
82 auto& stream = getStream(func);
83 if (quiet) {
84 return stream;
85 }
86 auto& ret = printFailureHeader(func);
87 ret << text << ", on \n";
88 return printModuleComponent(curr, ret);
89 }
90
printFailureHeaderwasm::ValidationInfo91 std::ostream& printFailureHeader(Function* func) {
92 auto& stream = getStream(func);
93 if (quiet) {
94 return stream;
95 }
96 Colors::red(stream);
97 if (func) {
98 stream << "[wasm-validator error in function ";
99 Colors::green(stream);
100 stream << func->name;
101 Colors::red(stream);
102 stream << "] ";
103 } else {
104 stream << "[wasm-validator error in module] ";
105 }
106 Colors::normal(stream);
107 return stream;
108 }
109
110 // checking utilities
111
112 template<typename T>
shouldBeTruewasm::ValidationInfo113 bool shouldBeTrue(bool result,
114 T curr,
115 const char* text,
116 Function* func = nullptr) {
117 if (!result) {
118 fail("unexpected false: " + std::string(text), curr, func);
119 return false;
120 }
121 return result;
122 }
123 template<typename T>
shouldBeFalsewasm::ValidationInfo124 bool shouldBeFalse(bool result,
125 T curr,
126 const char* text,
127 Function* func = nullptr) {
128 if (result) {
129 fail("unexpected true: " + std::string(text), curr, func);
130 return false;
131 }
132 return result;
133 }
134
135 template<typename T, typename S>
shouldBeEqualwasm::ValidationInfo136 bool shouldBeEqual(
137 S left, S right, T curr, const char* text, Function* func = nullptr) {
138 if (left != right) {
139 std::ostringstream ss;
140 ss << left << " != " << right << ": " << text;
141 fail(ss.str(), curr, func);
142 return false;
143 }
144 return true;
145 }
146
147 template<typename T, typename S>
shouldBeEqualOrFirstIsUnreachablewasm::ValidationInfo148 bool shouldBeEqualOrFirstIsUnreachable(
149 S left, S right, T curr, const char* text, Function* func = nullptr) {
150 if (left != Type::unreachable && left != right) {
151 std::ostringstream ss;
152 ss << left << " != " << right << ": " << text;
153 fail(ss.str(), curr, func);
154 return false;
155 }
156 return true;
157 }
158
159 template<typename T, typename S>
shouldBeUnequalwasm::ValidationInfo160 bool shouldBeUnequal(
161 S left, S right, T curr, const char* text, Function* func = nullptr) {
162 if (left == right) {
163 std::ostringstream ss;
164 ss << left << " == " << right << ": " << text;
165 fail(ss.str(), curr, func);
166 return false;
167 }
168 return true;
169 }
170
shouldBeIntOrUnreachablewasm::ValidationInfo171 void shouldBeIntOrUnreachable(Type ty,
172 Expression* curr,
173 const char* text,
174 Function* func = nullptr) {
175 switch (ty.getBasic()) {
176 case Type::i32:
177 case Type::i64:
178 case Type::unreachable: {
179 break;
180 }
181 default:
182 fail(text, curr, func);
183 }
184 }
185
186 // Type 'left' should be a subtype of 'right'.
shouldBeSubTypewasm::ValidationInfo187 bool shouldBeSubType(Type left,
188 Type right,
189 Expression* curr,
190 const char* text,
191 Function* func = nullptr) {
192 if (Type::isSubType(left, right)) {
193 return true;
194 }
195 fail(text, curr, func);
196 return false;
197 }
198
199 // Type 'left' should be a subtype of 'right', or unreachable.
shouldBeSubTypeOrFirstIsUnreachablewasm::ValidationInfo200 bool shouldBeSubTypeOrFirstIsUnreachable(Type left,
201 Type right,
202 Expression* curr,
203 const char* text,
204 Function* func = nullptr) {
205 if (left == Type::unreachable) {
206 return true;
207 }
208 return shouldBeSubType(left, right, curr, text, func);
209 }
210 };
211
212 struct FunctionValidator : public WalkerPass<PostWalker<FunctionValidator>> {
isFunctionParallelwasm::FunctionValidator213 bool isFunctionParallel() override { return true; }
214
createwasm::FunctionValidator215 Pass* create() override { return new FunctionValidator(&info); }
216
modifiesBinaryenIRwasm::FunctionValidator217 bool modifiesBinaryenIR() override { return false; }
218
219 ValidationInfo& info;
220
FunctionValidatorwasm::FunctionValidator221 FunctionValidator(ValidationInfo* info) : info(*info) {}
222
223 struct BreakInfo {
224 enum { UnsetArity = Index(-1), PoisonArity = Index(-2) };
225
226 Type type;
227 Index arity;
BreakInfowasm::FunctionValidator::BreakInfo228 BreakInfo() : arity(UnsetArity) {}
BreakInfowasm::FunctionValidator::BreakInfo229 BreakInfo(Type type, Index arity) : type(type), arity(arity) {}
230
hasBeenSetwasm::FunctionValidator::BreakInfo231 bool hasBeenSet() {
232 // Compare to the impossible value.
233 return arity != UnsetArity;
234 }
235 };
236
237 std::unordered_map<Name, BreakInfo> breakInfos;
238
239 std::set<Type> returnTypes; // types used in returns
240
241 // Binaryen IR requires that label names must be unique - IR generators must
242 // ensure that
243 std::unordered_set<Name> labelNames;
244
245 void noteLabelName(Name name);
246
247 public:
248 // visitors
249
250 void validatePoppyExpression(Expression* curr);
251
visitPoppyExpressionwasm::FunctionValidator252 static void visitPoppyExpression(FunctionValidator* self,
253 Expression** currp) {
254 self->validatePoppyExpression(*currp);
255 }
256
visitPreBlockwasm::FunctionValidator257 static void visitPreBlock(FunctionValidator* self, Expression** currp) {
258 auto* curr = (*currp)->cast<Block>();
259 if (curr->name.is()) {
260 self->breakInfos[curr->name];
261 }
262 }
263
264 void visitBlock(Block* curr);
265 void validateNormalBlockElements(Block* curr);
266 void validatePoppyBlockElements(Block* curr);
267
visitPreLoopwasm::FunctionValidator268 static void visitPreLoop(FunctionValidator* self, Expression** currp) {
269 auto* curr = (*currp)->cast<Loop>();
270 if (curr->name.is()) {
271 self->breakInfos[curr->name];
272 }
273 }
274
275 void visitLoop(Loop* curr);
276 void visitIf(If* curr);
277
278 // override scan to add a pre and a post check task to all nodes
scanwasm::FunctionValidator279 static void scan(FunctionValidator* self, Expression** currp) {
280 PostWalker<FunctionValidator>::scan(self, currp);
281
282 auto* curr = *currp;
283 if (curr->is<Block>()) {
284 self->pushTask(visitPreBlock, currp);
285 }
286 if (curr->is<Loop>()) {
287 self->pushTask(visitPreLoop, currp);
288 }
289 if (auto* func = self->getFunction()) {
290 if (func->profile == IRProfile::Poppy) {
291 self->pushTask(visitPoppyExpression, currp);
292 }
293 }
294 }
295
296 void noteBreak(Name name, Expression* value, Expression* curr);
297 void noteBreak(Name name, Type valueType, Expression* curr);
298 void visitBreak(Break* curr);
299 void visitSwitch(Switch* curr);
300 void visitCall(Call* curr);
301 void visitCallIndirect(CallIndirect* curr);
302 void visitConst(Const* curr);
303 void visitLocalGet(LocalGet* curr);
304 void visitLocalSet(LocalSet* curr);
305 void visitGlobalGet(GlobalGet* curr);
306 void visitGlobalSet(GlobalSet* curr);
307 void visitLoad(Load* curr);
308 void visitStore(Store* curr);
309 void visitAtomicRMW(AtomicRMW* curr);
310 void visitAtomicCmpxchg(AtomicCmpxchg* curr);
311 void visitAtomicWait(AtomicWait* curr);
312 void visitAtomicNotify(AtomicNotify* curr);
313 void visitAtomicFence(AtomicFence* curr);
314 void visitSIMDExtract(SIMDExtract* curr);
315 void visitSIMDReplace(SIMDReplace* curr);
316 void visitSIMDShuffle(SIMDShuffle* curr);
317 void visitSIMDTernary(SIMDTernary* curr);
318 void visitSIMDShift(SIMDShift* curr);
319 void visitSIMDLoad(SIMDLoad* curr);
320 void visitMemoryInit(MemoryInit* curr);
321 void visitDataDrop(DataDrop* curr);
322 void visitMemoryCopy(MemoryCopy* curr);
323 void visitMemoryFill(MemoryFill* curr);
324 void visitBinary(Binary* curr);
325 void visitUnary(Unary* curr);
326 void visitSelect(Select* curr);
327 void visitDrop(Drop* curr);
328 void visitReturn(Return* curr);
329 void visitMemorySize(MemorySize* curr);
330 void visitMemoryGrow(MemoryGrow* curr);
331 void visitRefNull(RefNull* curr);
332 void visitRefIsNull(RefIsNull* curr);
333 void visitRefFunc(RefFunc* curr);
334 void visitRefEq(RefEq* curr);
335 void visitTry(Try* curr);
336 void visitThrow(Throw* curr);
337 void visitRethrow(Rethrow* curr);
338 void visitBrOnExn(BrOnExn* curr);
339 void visitTupleMake(TupleMake* curr);
340 void visitTupleExtract(TupleExtract* curr);
341 void visitI31New(I31New* curr);
342 void visitI31Get(I31Get* curr);
343 void visitRefTest(RefTest* curr);
344 void visitRefCast(RefCast* curr);
345 void visitBrOnCast(BrOnCast* curr);
346 void visitRttCanon(RttCanon* curr);
347 void visitRttSub(RttSub* curr);
348 void visitStructNew(StructNew* curr);
349 void visitStructGet(StructGet* curr);
350 void visitStructSet(StructSet* curr);
351 void visitArrayNew(ArrayNew* curr);
352 void visitArrayGet(ArrayGet* curr);
353 void visitArraySet(ArraySet* curr);
354 void visitArrayLen(ArrayLen* curr);
355 void visitFunction(Function* curr);
356
357 // helpers
358 private:
getStreamwasm::FunctionValidator359 std::ostream& getStream() { return info.getStream(getFunction()); }
360
361 template<typename T>
shouldBeTruewasm::FunctionValidator362 bool shouldBeTrue(bool result, T curr, const char* text) {
363 return info.shouldBeTrue(result, curr, text, getFunction());
364 }
365 template<typename T>
shouldBeFalsewasm::FunctionValidator366 bool shouldBeFalse(bool result, T curr, const char* text) {
367 return info.shouldBeFalse(result, curr, text, getFunction());
368 }
369
370 template<typename T, typename S>
shouldBeEqualwasm::FunctionValidator371 bool shouldBeEqual(S left, S right, T curr, const char* text) {
372 return info.shouldBeEqual(left, right, curr, text, getFunction());
373 }
374
375 template<typename T, typename S>
376 bool
shouldBeEqualOrFirstIsUnreachablewasm::FunctionValidator377 shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text) {
378 return info.shouldBeEqualOrFirstIsUnreachable(
379 left, right, curr, text, getFunction());
380 }
381
382 template<typename T, typename S>
shouldBeUnequalwasm::FunctionValidator383 bool shouldBeUnequal(S left, S right, T curr, const char* text) {
384 return info.shouldBeUnequal(left, right, curr, text, getFunction());
385 }
386
shouldBeIntOrUnreachablewasm::FunctionValidator387 void shouldBeIntOrUnreachable(Type ty, Expression* curr, const char* text) {
388 return info.shouldBeIntOrUnreachable(ty, curr, text, getFunction());
389 }
390
391 bool
shouldBeSubTypewasm::FunctionValidator392 shouldBeSubType(Type left, Type right, Expression* curr, const char* text) {
393 return info.shouldBeSubType(left, right, curr, text, getFunction());
394 }
395
shouldBeSubTypeOrFirstIsUnreachablewasm::FunctionValidator396 bool shouldBeSubTypeOrFirstIsUnreachable(Type left,
397 Type right,
398 Expression* curr,
399 const char* text) {
400 return info.shouldBeSubTypeOrFirstIsUnreachable(
401 left, right, curr, text, getFunction());
402 }
403
404 void validateAlignment(
405 size_t align, Type type, Index bytes, bool isAtomic, Expression* curr);
406 void validateMemBytes(uint8_t bytes, Type type, Expression* curr);
407
indexTypewasm::FunctionValidator408 Type indexType() { return getModule()->memory.indexType; }
409 };
410
noteLabelName(Name name)411 void FunctionValidator::noteLabelName(Name name) {
412 if (!name.is()) {
413 return;
414 }
415 bool inserted;
416 std::tie(std::ignore, inserted) = labelNames.insert(name);
417 shouldBeTrue(
418 inserted,
419 name,
420 "names in Binaryen IR must be unique - IR generators must ensure that");
421 }
422
validatePoppyExpression(Expression * curr)423 void FunctionValidator::validatePoppyExpression(Expression* curr) {
424 if (curr->type == Type::unreachable) {
425 shouldBeTrue(StackUtils::mayBeUnreachable(curr),
426 curr,
427 "Only control flow structures and unreachable polymorphic"
428 " instructions may be unreachable in Poppy IR");
429 }
430 if (Properties::isControlFlowStructure(curr)) {
431 // Check that control flow children (except If conditions) are blocks
432 if (auto* if_ = curr->dynCast<If>()) {
433 shouldBeTrue(
434 if_->condition->is<Pop>(), curr, "Expected condition to be a Pop");
435 shouldBeTrue(if_->ifTrue->is<Block>(),
436 curr,
437 "Expected control flow child to be a block");
438 shouldBeTrue(!if_->ifFalse || if_->ifFalse->is<Block>(),
439 curr,
440 "Expected control flow child to be a block");
441 } else if (!curr->is<Block>()) {
442 for (auto* child : ChildIterator(curr)) {
443 shouldBeTrue(child->is<Block>(),
444 curr,
445 "Expected control flow child to be a block");
446 }
447 }
448 } else {
449 // Check that all children are Pops
450 for (auto* child : ChildIterator(curr)) {
451 shouldBeTrue(child->is<Pop>(), curr, "Unexpected non-Pop child");
452 }
453 }
454 }
455
visitBlock(Block * curr)456 void FunctionValidator::visitBlock(Block* curr) {
457 if (!getModule()->features.hasMultivalue()) {
458 shouldBeTrue(!curr->type.isTuple(),
459 curr,
460 "Multivalue block type (multivalue is not enabled)");
461 }
462 // if we are break'ed to, then the value must be right for us
463 if (curr->name.is()) {
464 noteLabelName(curr->name);
465 auto iter = breakInfos.find(curr->name);
466 assert(iter != breakInfos.end()); // we set it ourselves
467 auto& info = iter->second;
468 if (info.hasBeenSet()) {
469 if (curr->type.isConcrete()) {
470 shouldBeTrue(info.arity != 0,
471 curr,
472 "break arities must be > 0 if block has a value");
473 } else {
474 shouldBeTrue(info.arity == 0,
475 curr,
476 "break arities must be 0 if block has no value");
477 }
478 // none or unreachable means a poison value that we should ignore - if
479 // consumed, it will error
480 if (info.type.isConcrete() && curr->type.isConcrete()) {
481 shouldBeSubType(
482 info.type,
483 curr->type,
484 curr,
485 "block+breaks must have right type if breaks return a value");
486 }
487 if (curr->type.isConcrete() && info.arity &&
488 info.type != Type::unreachable) {
489 shouldBeSubType(
490 info.type,
491 curr->type,
492 curr,
493 "block+breaks must have right type if breaks have arity");
494 }
495 shouldBeTrue(
496 info.arity != BreakInfo::PoisonArity, curr, "break arities must match");
497 if (curr->list.size() > 0) {
498 auto last = curr->list.back()->type;
499 if (last == Type::none) {
500 shouldBeTrue(info.arity == Index(0),
501 curr,
502 "if block ends with a none, breaks cannot send a value "
503 "of any type");
504 }
505 }
506 }
507 breakInfos.erase(iter);
508 }
509 switch (getFunction()->profile) {
510 case IRProfile::Normal:
511 validateNormalBlockElements(curr);
512 break;
513 case IRProfile::Poppy:
514 validatePoppyBlockElements(curr);
515 break;
516 }
517 }
518
validateNormalBlockElements(Block * curr)519 void FunctionValidator::validateNormalBlockElements(Block* curr) {
520 if (curr->list.size() > 1) {
521 for (Index i = 0; i < curr->list.size() - 1; i++) {
522 if (!shouldBeTrue(
523 !curr->list[i]->type.isConcrete(),
524 curr,
525 "non-final block elements returning a value must be drop()ed "
526 "(binaryen's autodrop option might help you)") &&
527 !info.quiet) {
528 getStream() << "(on index " << i << ":\n"
529 << curr->list[i] << "\n), type: " << curr->list[i]->type
530 << "\n";
531 }
532 }
533 }
534 if (curr->list.size() > 0) {
535 auto backType = curr->list.back()->type;
536 if (!curr->type.isConcrete()) {
537 shouldBeFalse(backType.isConcrete(),
538 curr,
539 "if block is not returning a value, final element should "
540 "not flow out a value");
541 } else {
542 if (backType.isConcrete()) {
543 shouldBeSubType(
544 backType,
545 curr->type,
546 curr,
547 "block with value and last element with value must match types");
548 } else {
549 shouldBeUnequal(
550 backType,
551 Type(Type::none),
552 curr,
553 "block with value must not have last element that is none");
554 }
555 }
556 }
557 if (curr->type.isConcrete()) {
558 shouldBeTrue(
559 curr->list.size() > 0, curr, "block with a value must not be empty");
560 }
561 }
562
validatePoppyBlockElements(Block * curr)563 void FunctionValidator::validatePoppyBlockElements(Block* curr) {
564 StackSignature blockSig;
565 for (size_t i = 0; i < curr->list.size(); ++i) {
566 Expression* expr = curr->list[i];
567 if (!shouldBeTrue(
568 !expr->is<Pop>(), expr, "Unexpected top-level pop in block")) {
569 return;
570 }
571 StackSignature sig(expr);
572 if (!shouldBeTrue(blockSig.composes(sig),
573 curr,
574 "block element has incompatible type") &&
575 !info.quiet) {
576 getStream() << "(on index " << i << ":\n"
577 << expr << "\n), required: " << sig.params << ", available: ";
578 if (blockSig.unreachable) {
579 getStream() << "unreachable, ";
580 }
581 getStream() << blockSig.results << "\n";
582 return;
583 }
584 blockSig += sig;
585 }
586 if (curr->type == Type::unreachable) {
587 shouldBeTrue(blockSig.unreachable,
588 curr,
589 "unreachable block should have unreachable element");
590 } else {
591 if (!shouldBeTrue(blockSig.satisfies(Signature(Type::none, curr->type)),
592 curr,
593 "block contents should satisfy block type") &&
594 !info.quiet) {
595 getStream() << "contents: " << blockSig.results
596 << (blockSig.unreachable ? " [unreachable]" : "") << "\n"
597 << "expected: " << curr->type << "\n";
598 }
599 }
600 }
601
visitLoop(Loop * curr)602 void FunctionValidator::visitLoop(Loop* curr) {
603 if (curr->name.is()) {
604 noteLabelName(curr->name);
605 auto iter = breakInfos.find(curr->name);
606 assert(iter != breakInfos.end()); // we set it ourselves
607 auto& info = iter->second;
608 if (info.hasBeenSet()) {
609 shouldBeEqual(
610 info.arity, Index(0), curr, "breaks to a loop cannot pass a value");
611 }
612 breakInfos.erase(iter);
613 }
614 if (curr->type == Type::none) {
615 shouldBeFalse(curr->body->type.isConcrete(),
616 curr,
617 "bad body for a loop that has no value");
618 }
619
620 // When there are multiple instructions within a loop, they are wrapped in a
621 // Block internally, so visitBlock can take care of verification. Here we
622 // check cases when there is only one instruction in a Loop.
623 if (!curr->body->is<Block>()) {
624 if (!curr->type.isConcrete()) {
625 shouldBeFalse(curr->body->type.isConcrete(),
626 curr,
627 "if loop is not returning a value, final element should "
628 "not flow out a value");
629 } else {
630 shouldBeSubTypeOrFirstIsUnreachable(
631 curr->body->type,
632 curr->type,
633 curr,
634 "loop with value and body must match types");
635 }
636 }
637 }
638
visitIf(If * curr)639 void FunctionValidator::visitIf(If* curr) {
640 shouldBeTrue(curr->condition->type == Type::unreachable ||
641 curr->condition->type == Type::i32,
642 curr,
643 "if condition must be valid");
644 if (!curr->ifFalse) {
645 shouldBeFalse(curr->ifTrue->type.isConcrete(),
646 curr,
647 "if without else must not return a value in body");
648 if (curr->condition->type != Type::unreachable) {
649 shouldBeEqual(curr->type,
650 Type(Type::none),
651 curr,
652 "if without else and reachable condition must be none");
653 }
654 } else {
655 if (curr->type != Type::unreachable) {
656 shouldBeSubTypeOrFirstIsUnreachable(
657 curr->ifTrue->type,
658 curr->type,
659 curr,
660 "returning if-else's true must have right type");
661 shouldBeSubTypeOrFirstIsUnreachable(
662 curr->ifFalse->type,
663 curr->type,
664 curr,
665 "returning if-else's false must have right type");
666 } else {
667 if (curr->condition->type != Type::unreachable) {
668 shouldBeEqual(curr->ifTrue->type,
669 Type(Type::unreachable),
670 curr,
671 "unreachable if-else must have unreachable true");
672 shouldBeEqual(curr->ifFalse->type,
673 Type(Type::unreachable),
674 curr,
675 "unreachable if-else must have unreachable false");
676 }
677 }
678 if (curr->ifTrue->type.isConcrete()) {
679 shouldBeSubType(curr->ifTrue->type,
680 curr->type,
681 curr,
682 "if type must match concrete ifTrue");
683 }
684 if (curr->ifFalse->type.isConcrete()) {
685 shouldBeSubType(curr->ifFalse->type,
686 curr->type,
687 curr,
688 "if type must match concrete ifFalse");
689 }
690 }
691 }
692
noteBreak(Name name,Expression * value,Expression * curr)693 void FunctionValidator::noteBreak(Name name,
694 Expression* value,
695 Expression* curr) {
696 if (value) {
697 shouldBeUnequal(
698 value->type, Type(Type::none), curr, "breaks must have a valid value");
699 }
700 noteBreak(name, value ? value->type : Type::none, curr);
701 }
702
noteBreak(Name name,Type valueType,Expression * curr)703 void FunctionValidator::noteBreak(Name name, Type valueType, Expression* curr) {
704 Index arity = 0;
705 if (valueType != Type::none) {
706 arity = 1;
707 }
708 auto iter = breakInfos.find(name);
709 if (!shouldBeTrue(
710 iter != breakInfos.end(), curr, "all break targets must be valid")) {
711 return;
712 }
713 auto& info = iter->second;
714 if (!info.hasBeenSet()) {
715 info = BreakInfo(valueType, arity);
716 } else {
717 info.type = Type::getLeastUpperBound(info.type, valueType);
718 if (arity != info.arity) {
719 info.arity = BreakInfo::PoisonArity;
720 }
721 }
722 }
visitBreak(Break * curr)723 void FunctionValidator::visitBreak(Break* curr) {
724 noteBreak(curr->name, curr->value, curr);
725 if (curr->value) {
726 shouldBeTrue(curr->value->type != Type::none,
727 curr,
728 "break value must not have none type");
729 }
730 if (curr->condition) {
731 shouldBeTrue(curr->condition->type == Type::unreachable ||
732 curr->condition->type == Type::i32,
733 curr,
734 "break condition must be i32");
735 }
736 }
737
visitSwitch(Switch * curr)738 void FunctionValidator::visitSwitch(Switch* curr) {
739 for (auto& target : curr->targets) {
740 noteBreak(target, curr->value, curr);
741 }
742 noteBreak(curr->default_, curr->value, curr);
743 shouldBeTrue(curr->condition->type == Type::unreachable ||
744 curr->condition->type == Type::i32,
745 curr,
746 "br_table condition must be i32");
747 }
748
visitCall(Call * curr)749 void FunctionValidator::visitCall(Call* curr) {
750 shouldBeTrue(!curr->isReturn || getModule()->features.hasTailCall(),
751 curr,
752 "return_call requires tail calls to be enabled");
753 if (!info.validateGlobally) {
754 return;
755 }
756 auto* target = getModule()->getFunctionOrNull(curr->target);
757 if (!shouldBeTrue(!!target, curr, "call target must exist")) {
758 return;
759 }
760 if (!shouldBeTrue(curr->operands.size() == target->sig.params.size(),
761 curr,
762 "call param number must match")) {
763 return;
764 }
765 size_t i = 0;
766 for (const auto& param : target->sig.params) {
767 if (!shouldBeSubTypeOrFirstIsUnreachable(curr->operands[i]->type,
768 param,
769 curr,
770 "call param types must match") &&
771 !info.quiet) {
772 getStream() << "(on argument " << i << ")\n";
773 }
774 ++i;
775 }
776 if (curr->isReturn) {
777 shouldBeEqual(curr->type,
778 Type(Type::unreachable),
779 curr,
780 "return_call should have unreachable type");
781 shouldBeEqual(
782 getFunction()->sig.results,
783 target->sig.results,
784 curr,
785 "return_call callee return type must match caller return type");
786 } else {
787 if (curr->type == Type::unreachable) {
788 bool hasUnreachableOperand = std::any_of(
789 curr->operands.begin(), curr->operands.end(), [](Expression* op) {
790 return op->type == Type::unreachable;
791 });
792 shouldBeTrue(
793 hasUnreachableOperand,
794 curr,
795 "calls may only be unreachable if they have unreachable operands");
796 } else {
797 shouldBeEqual(curr->type,
798 target->sig.results,
799 curr,
800 "call type must match callee return type");
801 }
802 }
803 }
804
visitCallIndirect(CallIndirect * curr)805 void FunctionValidator::visitCallIndirect(CallIndirect* curr) {
806 shouldBeTrue(!curr->isReturn || getModule()->features.hasTailCall(),
807 curr,
808 "return_call_indirect requires tail calls to be enabled");
809 if (!info.validateGlobally) {
810 return;
811 }
812 shouldBeEqualOrFirstIsUnreachable(curr->target->type,
813 Type(Type::i32),
814 curr,
815 "indirect call target must be an i32");
816 if (!shouldBeTrue(curr->operands.size() == curr->sig.params.size(),
817 curr,
818 "call param number must match")) {
819 return;
820 }
821 size_t i = 0;
822 for (const auto& param : curr->sig.params) {
823 if (!shouldBeSubTypeOrFirstIsUnreachable(curr->operands[i]->type,
824 param,
825 curr,
826 "call param types must match") &&
827 !info.quiet) {
828 getStream() << "(on argument " << i << ")\n";
829 }
830 ++i;
831 }
832 if (curr->isReturn) {
833 shouldBeEqual(curr->type,
834 Type(Type::unreachable),
835 curr,
836 "return_call_indirect should have unreachable type");
837 shouldBeEqual(
838 getFunction()->sig.results,
839 curr->sig.results,
840 curr,
841 "return_call_indirect callee return type must match caller return type");
842 } else {
843 if (curr->type == Type::unreachable) {
844 if (curr->target->type != Type::unreachable) {
845 bool hasUnreachableOperand = std::any_of(
846 curr->operands.begin(), curr->operands.end(), [](Expression* op) {
847 return op->type == Type::unreachable;
848 });
849 shouldBeTrue(hasUnreachableOperand,
850 curr,
851 "call_indirects may only be unreachable if they have "
852 "unreachable operands");
853 }
854 } else {
855 shouldBeEqual(curr->type,
856 curr->sig.results,
857 curr,
858 "call_indirect type must match callee return type");
859 }
860 }
861 }
862
visitConst(Const * curr)863 void FunctionValidator::visitConst(Const* curr) {
864 shouldBeTrue(curr->type.getFeatures() <= getModule()->features,
865 curr,
866 "all used features should be allowed");
867 }
868
visitLocalGet(LocalGet * curr)869 void FunctionValidator::visitLocalGet(LocalGet* curr) {
870 shouldBeTrue(curr->type.isConcrete(),
871 curr,
872 "local.get must have a valid type - check what you provided "
873 "when you constructed the node");
874 if (shouldBeTrue(curr->index < getFunction()->getNumLocals(),
875 curr,
876 "local.get index must be small enough")) {
877 shouldBeTrue(curr->type == getFunction()->getLocalType(curr->index),
878 curr,
879 "local.get must have proper type");
880 }
881 }
882
visitLocalSet(LocalSet * curr)883 void FunctionValidator::visitLocalSet(LocalSet* curr) {
884 if (shouldBeTrue(curr->index < getFunction()->getNumLocals(),
885 curr,
886 "local.set index must be small enough")) {
887 if (curr->value->type != Type::unreachable) {
888 if (curr->type != Type::none) { // tee is ok anyhow
889 shouldBeEqual(getFunction()->getLocalType(curr->index),
890 curr->type,
891 curr,
892 "local.set type must be correct");
893 }
894 shouldBeSubType(curr->value->type,
895 getFunction()->getLocalType(curr->index),
896 curr,
897 "local.set's value type must be correct");
898 }
899 }
900 }
901
visitGlobalGet(GlobalGet * curr)902 void FunctionValidator::visitGlobalGet(GlobalGet* curr) {
903 if (!info.validateGlobally) {
904 return;
905 }
906 shouldBeTrue(getModule()->getGlobalOrNull(curr->name),
907 curr,
908 "global.get name must be valid");
909 }
910
visitGlobalSet(GlobalSet * curr)911 void FunctionValidator::visitGlobalSet(GlobalSet* curr) {
912 if (!info.validateGlobally) {
913 return;
914 }
915 auto* global = getModule()->getGlobalOrNull(curr->name);
916 if (shouldBeTrue(global,
917 curr,
918 "global.set name must be valid (and not an import; imports "
919 "can't be modified)")) {
920 shouldBeTrue(global->mutable_, curr, "global.set global must be mutable");
921 shouldBeSubTypeOrFirstIsUnreachable(
922 curr->value->type,
923 global->type,
924 curr,
925 "global.set value must have right type");
926 }
927 }
928
visitLoad(Load * curr)929 void FunctionValidator::visitLoad(Load* curr) {
930 shouldBeTrue(
931 getModule()->memory.exists, curr, "Memory operations require a memory");
932 if (curr->isAtomic) {
933 shouldBeTrue(getModule()->features.hasAtomics(),
934 curr,
935 "Atomic operation (atomics are disabled)");
936 shouldBeTrue(curr->type == Type::i32 || curr->type == Type::i64 ||
937 curr->type == Type::unreachable,
938 curr,
939 "Atomic load should be i32 or i64");
940 }
941 if (curr->type == Type::v128) {
942 shouldBeTrue(getModule()->features.hasSIMD(),
943 curr,
944 "SIMD operation (SIMD is disabled)");
945 }
946 validateMemBytes(curr->bytes, curr->type, curr);
947 validateAlignment(curr->align, curr->type, curr->bytes, curr->isAtomic, curr);
948 shouldBeEqualOrFirstIsUnreachable(
949 curr->ptr->type,
950 indexType(),
951 curr,
952 "load pointer type must match memory index type");
953 if (curr->isAtomic) {
954 shouldBeFalse(curr->signed_, curr, "atomic loads must be unsigned");
955 shouldBeIntOrUnreachable(
956 curr->type, curr, "atomic loads must be of integers");
957 }
958 }
959
visitStore(Store * curr)960 void FunctionValidator::visitStore(Store* curr) {
961 shouldBeTrue(
962 getModule()->memory.exists, curr, "Memory operations require a memory");
963 if (curr->isAtomic) {
964 shouldBeTrue(getModule()->features.hasAtomics(),
965 curr,
966 "Atomic operation (atomics are disabled)");
967 shouldBeTrue(curr->valueType == Type::i32 || curr->valueType == Type::i64 ||
968 curr->valueType == Type::unreachable,
969 curr,
970 "Atomic store should be i32 or i64");
971 }
972 if (curr->valueType == Type::v128) {
973 shouldBeTrue(getModule()->features.hasSIMD(),
974 curr,
975 "SIMD operation (SIMD is disabled)");
976 }
977 validateMemBytes(curr->bytes, curr->valueType, curr);
978 validateAlignment(
979 curr->align, curr->valueType, curr->bytes, curr->isAtomic, curr);
980 shouldBeEqualOrFirstIsUnreachable(
981 curr->ptr->type,
982 indexType(),
983 curr,
984 "store pointer must match memory index type");
985 shouldBeUnequal(curr->value->type,
986 Type(Type::none),
987 curr,
988 "store value type must not be none");
989 shouldBeEqualOrFirstIsUnreachable(
990 curr->value->type, curr->valueType, curr, "store value type must match");
991 if (curr->isAtomic) {
992 shouldBeIntOrUnreachable(
993 curr->valueType, curr, "atomic stores must be of integers");
994 }
995 }
996
visitAtomicRMW(AtomicRMW * curr)997 void FunctionValidator::visitAtomicRMW(AtomicRMW* curr) {
998 shouldBeTrue(
999 getModule()->memory.exists, curr, "Memory operations require a memory");
1000 shouldBeTrue(getModule()->features.hasAtomics(),
1001 curr,
1002 "Atomic operation (atomics are disabled)");
1003 validateMemBytes(curr->bytes, curr->type, curr);
1004 shouldBeEqualOrFirstIsUnreachable(
1005 curr->ptr->type,
1006 indexType(),
1007 curr,
1008 "AtomicRMW pointer type must match memory index type");
1009 shouldBeEqualOrFirstIsUnreachable(curr->type,
1010 curr->value->type,
1011 curr,
1012 "AtomicRMW result type must match operand");
1013 shouldBeIntOrUnreachable(
1014 curr->type, curr, "Atomic operations are only valid on int types");
1015 }
1016
visitAtomicCmpxchg(AtomicCmpxchg * curr)1017 void FunctionValidator::visitAtomicCmpxchg(AtomicCmpxchg* curr) {
1018 shouldBeTrue(
1019 getModule()->memory.exists, curr, "Memory operations require a memory");
1020 shouldBeTrue(getModule()->features.hasAtomics(),
1021 curr,
1022 "Atomic operation (atomics are disabled)");
1023 validateMemBytes(curr->bytes, curr->type, curr);
1024 shouldBeEqualOrFirstIsUnreachable(
1025 curr->ptr->type,
1026 indexType(),
1027 curr,
1028 "cmpxchg pointer must match memory index type");
1029 if (curr->expected->type != Type::unreachable &&
1030 curr->replacement->type != Type::unreachable) {
1031 shouldBeEqual(curr->expected->type,
1032 curr->replacement->type,
1033 curr,
1034 "cmpxchg operand types must match");
1035 }
1036 shouldBeEqualOrFirstIsUnreachable(curr->type,
1037 curr->expected->type,
1038 curr,
1039 "Cmpxchg result type must match expected");
1040 shouldBeEqualOrFirstIsUnreachable(
1041 curr->type,
1042 curr->replacement->type,
1043 curr,
1044 "Cmpxchg result type must match replacement");
1045 shouldBeIntOrUnreachable(curr->expected->type,
1046 curr,
1047 "Atomic operations are only valid on int types");
1048 }
1049
visitAtomicWait(AtomicWait * curr)1050 void FunctionValidator::visitAtomicWait(AtomicWait* curr) {
1051 shouldBeTrue(
1052 getModule()->memory.exists, curr, "Memory operations require a memory");
1053 shouldBeTrue(getModule()->features.hasAtomics(),
1054 curr,
1055 "Atomic operation (atomics are disabled)");
1056 shouldBeEqualOrFirstIsUnreachable(
1057 curr->type, Type(Type::i32), curr, "AtomicWait must have type i32");
1058 shouldBeEqualOrFirstIsUnreachable(
1059 curr->ptr->type,
1060 indexType(),
1061 curr,
1062 "AtomicWait pointer must match memory index type");
1063 shouldBeIntOrUnreachable(
1064 curr->expected->type, curr, "AtomicWait expected type must be int");
1065 shouldBeEqualOrFirstIsUnreachable(
1066 curr->expected->type,
1067 curr->expectedType,
1068 curr,
1069 "AtomicWait expected type must match operand");
1070 shouldBeEqualOrFirstIsUnreachable(curr->timeout->type,
1071 Type(Type::i64),
1072 curr,
1073 "AtomicWait timeout type must be i64");
1074 }
1075
visitAtomicNotify(AtomicNotify * curr)1076 void FunctionValidator::visitAtomicNotify(AtomicNotify* curr) {
1077 shouldBeTrue(
1078 getModule()->memory.exists, curr, "Memory operations require a memory");
1079 shouldBeTrue(getModule()->features.hasAtomics(),
1080 curr,
1081 "Atomic operation (atomics are disabled)");
1082 shouldBeEqualOrFirstIsUnreachable(
1083 curr->type, Type(Type::i32), curr, "AtomicNotify must have type i32");
1084 shouldBeEqualOrFirstIsUnreachable(
1085 curr->ptr->type,
1086 indexType(),
1087 curr,
1088 "AtomicNotify pointer must match memory index type");
1089 shouldBeEqualOrFirstIsUnreachable(
1090 curr->notifyCount->type,
1091 Type(Type::i32),
1092 curr,
1093 "AtomicNotify notifyCount type must be i32");
1094 }
1095
visitAtomicFence(AtomicFence * curr)1096 void FunctionValidator::visitAtomicFence(AtomicFence* curr) {
1097 shouldBeTrue(
1098 getModule()->memory.exists, curr, "Memory operations require a memory");
1099 shouldBeTrue(getModule()->features.hasAtomics(),
1100 curr,
1101 "Atomic operation (atomics are disabled)");
1102 shouldBeTrue(curr->order == 0,
1103 curr,
1104 "Currently only sequentially consistent atomics are supported, "
1105 "so AtomicFence's order should be 0");
1106 }
1107
visitSIMDExtract(SIMDExtract * curr)1108 void FunctionValidator::visitSIMDExtract(SIMDExtract* curr) {
1109 shouldBeTrue(
1110 getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
1111 shouldBeEqualOrFirstIsUnreachable(curr->vec->type,
1112 Type(Type::v128),
1113 curr,
1114 "extract_lane must operate on a v128");
1115 Type lane_t = Type::none;
1116 size_t lanes = 0;
1117 switch (curr->op) {
1118 case ExtractLaneSVecI8x16:
1119 case ExtractLaneUVecI8x16:
1120 lane_t = Type::i32;
1121 lanes = 16;
1122 break;
1123 case ExtractLaneSVecI16x8:
1124 case ExtractLaneUVecI16x8:
1125 lane_t = Type::i32;
1126 lanes = 8;
1127 break;
1128 case ExtractLaneVecI32x4:
1129 lane_t = Type::i32;
1130 lanes = 4;
1131 break;
1132 case ExtractLaneVecI64x2:
1133 lane_t = Type::i64;
1134 lanes = 2;
1135 break;
1136 case ExtractLaneVecF32x4:
1137 lane_t = Type::f32;
1138 lanes = 4;
1139 break;
1140 case ExtractLaneVecF64x2:
1141 lane_t = Type::f64;
1142 lanes = 2;
1143 break;
1144 }
1145 shouldBeEqualOrFirstIsUnreachable(
1146 curr->type,
1147 lane_t,
1148 curr,
1149 "extract_lane must have same type as vector lane");
1150 shouldBeTrue(curr->index < lanes, curr, "invalid lane index");
1151 }
1152
visitSIMDReplace(SIMDReplace * curr)1153 void FunctionValidator::visitSIMDReplace(SIMDReplace* curr) {
1154 shouldBeTrue(
1155 getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
1156 shouldBeEqualOrFirstIsUnreachable(
1157 curr->type, Type(Type::v128), curr, "replace_lane must have type v128");
1158 shouldBeEqualOrFirstIsUnreachable(curr->vec->type,
1159 Type(Type::v128),
1160 curr,
1161 "replace_lane must operate on a v128");
1162 Type lane_t = Type::none;
1163 size_t lanes = 0;
1164 switch (curr->op) {
1165 case ReplaceLaneVecI8x16:
1166 lane_t = Type::i32;
1167 lanes = 16;
1168 break;
1169 case ReplaceLaneVecI16x8:
1170 lane_t = Type::i32;
1171 lanes = 8;
1172 break;
1173 case ReplaceLaneVecI32x4:
1174 lane_t = Type::i32;
1175 lanes = 4;
1176 break;
1177 case ReplaceLaneVecI64x2:
1178 lane_t = Type::i64;
1179 lanes = 2;
1180 break;
1181 case ReplaceLaneVecF32x4:
1182 lane_t = Type::f32;
1183 lanes = 4;
1184 break;
1185 case ReplaceLaneVecF64x2:
1186 lane_t = Type::f64;
1187 lanes = 2;
1188 break;
1189 }
1190 shouldBeEqualOrFirstIsUnreachable(
1191 curr->value->type, lane_t, curr, "unexpected value type");
1192 shouldBeTrue(curr->index < lanes, curr, "invalid lane index");
1193 }
1194
visitSIMDShuffle(SIMDShuffle * curr)1195 void FunctionValidator::visitSIMDShuffle(SIMDShuffle* curr) {
1196 shouldBeTrue(
1197 getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
1198 shouldBeEqualOrFirstIsUnreachable(
1199 curr->type, Type(Type::v128), curr, "v128.shuffle must have type v128");
1200 shouldBeEqualOrFirstIsUnreachable(
1201 curr->left->type, Type(Type::v128), curr, "expected operand of type v128");
1202 shouldBeEqualOrFirstIsUnreachable(
1203 curr->right->type, Type(Type::v128), curr, "expected operand of type v128");
1204 for (uint8_t index : curr->mask) {
1205 shouldBeTrue(index < 32, curr, "Invalid lane index in mask");
1206 }
1207 }
1208
visitSIMDTernary(SIMDTernary * curr)1209 void FunctionValidator::visitSIMDTernary(SIMDTernary* curr) {
1210 shouldBeTrue(
1211 getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
1212 shouldBeEqualOrFirstIsUnreachable(
1213 curr->type, Type(Type::v128), curr, "SIMD ternary must have type v128");
1214 shouldBeEqualOrFirstIsUnreachable(
1215 curr->a->type, Type(Type::v128), curr, "expected operand of type v128");
1216 shouldBeEqualOrFirstIsUnreachable(
1217 curr->b->type, Type(Type::v128), curr, "expected operand of type v128");
1218 shouldBeEqualOrFirstIsUnreachable(
1219 curr->c->type, Type(Type::v128), curr, "expected operand of type v128");
1220 }
1221
visitSIMDShift(SIMDShift * curr)1222 void FunctionValidator::visitSIMDShift(SIMDShift* curr) {
1223 shouldBeTrue(
1224 getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
1225 shouldBeEqualOrFirstIsUnreachable(
1226 curr->type, Type(Type::v128), curr, "vector shift must have type v128");
1227 shouldBeEqualOrFirstIsUnreachable(
1228 curr->vec->type, Type(Type::v128), curr, "expected operand of type v128");
1229 shouldBeEqualOrFirstIsUnreachable(curr->shift->type,
1230 Type(Type::i32),
1231 curr,
1232 "expected shift amount to have type i32");
1233 }
1234
visitSIMDLoad(SIMDLoad * curr)1235 void FunctionValidator::visitSIMDLoad(SIMDLoad* curr) {
1236 shouldBeTrue(
1237 getModule()->memory.exists, curr, "Memory operations require a memory");
1238 shouldBeTrue(
1239 getModule()->features.hasSIMD(), curr, "SIMD operation (SIMD is disabled)");
1240 shouldBeEqualOrFirstIsUnreachable(
1241 curr->type, Type(Type::v128), curr, "load_splat must have type v128");
1242 shouldBeEqualOrFirstIsUnreachable(
1243 curr->ptr->type,
1244 indexType(),
1245 curr,
1246 "load_splat address must match memory index type");
1247 Type memAlignType = Type::none;
1248 switch (curr->op) {
1249 case LoadSplatVec8x16:
1250 case LoadSplatVec16x8:
1251 case LoadSplatVec32x4:
1252 case Load32Zero:
1253 memAlignType = Type::i32;
1254 break;
1255 case LoadSplatVec64x2:
1256 case LoadExtSVec8x8ToVecI16x8:
1257 case LoadExtUVec8x8ToVecI16x8:
1258 case LoadExtSVec16x4ToVecI32x4:
1259 case LoadExtUVec16x4ToVecI32x4:
1260 case LoadExtSVec32x2ToVecI64x2:
1261 case LoadExtUVec32x2ToVecI64x2:
1262 case Load64Zero:
1263 memAlignType = Type::i64;
1264 break;
1265 }
1266 Index bytes = curr->getMemBytes();
1267 validateAlignment(curr->align, memAlignType, bytes, /*isAtomic=*/false, curr);
1268 }
1269
visitMemoryInit(MemoryInit * curr)1270 void FunctionValidator::visitMemoryInit(MemoryInit* curr) {
1271 shouldBeTrue(getModule()->features.hasBulkMemory(),
1272 curr,
1273 "Bulk memory operation (bulk memory is disabled)");
1274 shouldBeEqualOrFirstIsUnreachable(
1275 curr->type, Type(Type::none), curr, "memory.init must have type none");
1276 shouldBeEqualOrFirstIsUnreachable(
1277 curr->dest->type,
1278 indexType(),
1279 curr,
1280 "memory.init dest must match memory index type");
1281 shouldBeEqualOrFirstIsUnreachable(curr->offset->type,
1282 Type(Type::i32),
1283 curr,
1284 "memory.init offset must be an i32");
1285 shouldBeEqualOrFirstIsUnreachable(
1286 curr->size->type, Type(Type::i32), curr, "memory.init size must be an i32");
1287 if (!shouldBeTrue(getModule()->memory.exists,
1288 curr,
1289 "Memory operations require a memory")) {
1290 return;
1291 }
1292 shouldBeTrue(curr->segment < getModule()->memory.segments.size(),
1293 curr,
1294 "memory.init segment index out of bounds");
1295 }
1296
visitDataDrop(DataDrop * curr)1297 void FunctionValidator::visitDataDrop(DataDrop* curr) {
1298 shouldBeTrue(getModule()->features.hasBulkMemory(),
1299 curr,
1300 "Bulk memory operation (bulk memory is disabled)");
1301 shouldBeEqualOrFirstIsUnreachable(
1302 curr->type, Type(Type::none), curr, "data.drop must have type none");
1303 if (!shouldBeTrue(getModule()->memory.exists,
1304 curr,
1305 "Memory operations require a memory")) {
1306 return;
1307 }
1308 shouldBeTrue(curr->segment < getModule()->memory.segments.size(),
1309 curr,
1310 "data.drop segment index out of bounds");
1311 }
1312
visitMemoryCopy(MemoryCopy * curr)1313 void FunctionValidator::visitMemoryCopy(MemoryCopy* curr) {
1314 shouldBeTrue(getModule()->features.hasBulkMemory(),
1315 curr,
1316 "Bulk memory operation (bulk memory is disabled)");
1317 shouldBeEqualOrFirstIsUnreachable(
1318 curr->type, Type(Type::none), curr, "memory.copy must have type none");
1319 shouldBeEqualOrFirstIsUnreachable(
1320 curr->dest->type,
1321 indexType(),
1322 curr,
1323 "memory.copy dest must match memory index type");
1324 shouldBeEqualOrFirstIsUnreachable(
1325 curr->source->type,
1326 indexType(),
1327 curr,
1328 "memory.copy source must match memory index type");
1329 shouldBeEqualOrFirstIsUnreachable(
1330 curr->size->type,
1331 indexType(),
1332 curr,
1333 "memory.copy size must match memory index type");
1334 shouldBeTrue(
1335 getModule()->memory.exists, curr, "Memory operations require a memory");
1336 }
1337
visitMemoryFill(MemoryFill * curr)1338 void FunctionValidator::visitMemoryFill(MemoryFill* curr) {
1339 shouldBeTrue(getModule()->features.hasBulkMemory(),
1340 curr,
1341 "Bulk memory operation (bulk memory is disabled)");
1342 shouldBeEqualOrFirstIsUnreachable(
1343 curr->type, Type(Type::none), curr, "memory.fill must have type none");
1344 shouldBeEqualOrFirstIsUnreachable(
1345 curr->dest->type,
1346 indexType(),
1347 curr,
1348 "memory.fill dest must match memory index type");
1349 shouldBeEqualOrFirstIsUnreachable(curr->value->type,
1350 Type(Type::i32),
1351 curr,
1352 "memory.fill value must be an i32");
1353 shouldBeEqualOrFirstIsUnreachable(
1354 curr->size->type,
1355 indexType(),
1356 curr,
1357 "memory.fill size must match memory index type");
1358 shouldBeTrue(
1359 getModule()->memory.exists, curr, "Memory operations require a memory");
1360 }
1361
validateMemBytes(uint8_t bytes,Type type,Expression * curr)1362 void FunctionValidator::validateMemBytes(uint8_t bytes,
1363 Type type,
1364 Expression* curr) {
1365 switch (type.getBasic()) {
1366 case Type::i32:
1367 shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4,
1368 curr,
1369 "expected i32 operation to touch 1, 2, or 4 bytes");
1370 break;
1371 case Type::i64:
1372 shouldBeTrue(bytes == 1 || bytes == 2 || bytes == 4 || bytes == 8,
1373 curr,
1374 "expected i64 operation to touch 1, 2, 4, or 8 bytes");
1375 break;
1376 case Type::f32:
1377 shouldBeEqual(
1378 bytes, uint8_t(4), curr, "expected f32 operation to touch 4 bytes");
1379 break;
1380 case Type::f64:
1381 shouldBeEqual(
1382 bytes, uint8_t(8), curr, "expected f64 operation to touch 8 bytes");
1383 break;
1384 case Type::v128:
1385 shouldBeEqual(
1386 bytes, uint8_t(16), curr, "expected v128 operation to touch 16 bytes");
1387 break;
1388 case Type::unreachable:
1389 break;
1390 case Type::funcref:
1391 case Type::externref:
1392 case Type::exnref:
1393 case Type::anyref:
1394 case Type::eqref:
1395 case Type::i31ref:
1396 case Type::none:
1397 WASM_UNREACHABLE("unexpected type");
1398 }
1399 }
1400
visitBinary(Binary * curr)1401 void FunctionValidator::visitBinary(Binary* curr) {
1402 if (curr->left->type != Type::unreachable &&
1403 curr->right->type != Type::unreachable) {
1404 shouldBeEqual(curr->left->type,
1405 curr->right->type,
1406 curr,
1407 "binary child types must be equal");
1408 }
1409 switch (curr->op) {
1410 case AddInt32:
1411 case SubInt32:
1412 case MulInt32:
1413 case DivSInt32:
1414 case DivUInt32:
1415 case RemSInt32:
1416 case RemUInt32:
1417 case AndInt32:
1418 case OrInt32:
1419 case XorInt32:
1420 case ShlInt32:
1421 case ShrUInt32:
1422 case ShrSInt32:
1423 case RotLInt32:
1424 case RotRInt32:
1425 case EqInt32:
1426 case NeInt32:
1427 case LtSInt32:
1428 case LtUInt32:
1429 case LeSInt32:
1430 case LeUInt32:
1431 case GtSInt32:
1432 case GtUInt32:
1433 case GeSInt32:
1434 case GeUInt32: {
1435 shouldBeEqualOrFirstIsUnreachable(
1436 curr->left->type, Type(Type::i32), curr, "i32 op");
1437 break;
1438 }
1439 case AddInt64:
1440 case SubInt64:
1441 case MulInt64:
1442 case DivSInt64:
1443 case DivUInt64:
1444 case RemSInt64:
1445 case RemUInt64:
1446 case AndInt64:
1447 case OrInt64:
1448 case XorInt64:
1449 case ShlInt64:
1450 case ShrUInt64:
1451 case ShrSInt64:
1452 case RotLInt64:
1453 case RotRInt64:
1454 case EqInt64:
1455 case NeInt64:
1456 case LtSInt64:
1457 case LtUInt64:
1458 case LeSInt64:
1459 case LeUInt64:
1460 case GtSInt64:
1461 case GtUInt64:
1462 case GeSInt64:
1463 case GeUInt64: {
1464 shouldBeEqualOrFirstIsUnreachable(
1465 curr->left->type, Type(Type::i64), curr, "i64 op");
1466 break;
1467 }
1468 case AddFloat32:
1469 case SubFloat32:
1470 case MulFloat32:
1471 case DivFloat32:
1472 case CopySignFloat32:
1473 case MinFloat32:
1474 case MaxFloat32:
1475 case EqFloat32:
1476 case NeFloat32:
1477 case LtFloat32:
1478 case LeFloat32:
1479 case GtFloat32:
1480 case GeFloat32: {
1481 shouldBeEqualOrFirstIsUnreachable(
1482 curr->left->type, Type(Type::f32), curr, "f32 op");
1483 break;
1484 }
1485 case AddFloat64:
1486 case SubFloat64:
1487 case MulFloat64:
1488 case DivFloat64:
1489 case CopySignFloat64:
1490 case MinFloat64:
1491 case MaxFloat64:
1492 case EqFloat64:
1493 case NeFloat64:
1494 case LtFloat64:
1495 case LeFloat64:
1496 case GtFloat64:
1497 case GeFloat64: {
1498 shouldBeEqualOrFirstIsUnreachable(
1499 curr->left->type, Type(Type::f64), curr, "f64 op");
1500 break;
1501 }
1502 case EqVecI8x16:
1503 case NeVecI8x16:
1504 case LtSVecI8x16:
1505 case LtUVecI8x16:
1506 case LeSVecI8x16:
1507 case LeUVecI8x16:
1508 case GtSVecI8x16:
1509 case GtUVecI8x16:
1510 case GeSVecI8x16:
1511 case GeUVecI8x16:
1512 case EqVecI16x8:
1513 case NeVecI16x8:
1514 case LtSVecI16x8:
1515 case LtUVecI16x8:
1516 case LeSVecI16x8:
1517 case LeUVecI16x8:
1518 case GtSVecI16x8:
1519 case GtUVecI16x8:
1520 case GeSVecI16x8:
1521 case GeUVecI16x8:
1522 case EqVecI32x4:
1523 case NeVecI32x4:
1524 case LtSVecI32x4:
1525 case LtUVecI32x4:
1526 case LeSVecI32x4:
1527 case LeUVecI32x4:
1528 case GtSVecI32x4:
1529 case GtUVecI32x4:
1530 case GeSVecI32x4:
1531 case GeUVecI32x4:
1532 case EqVecF32x4:
1533 case NeVecF32x4:
1534 case LtVecF32x4:
1535 case LeVecF32x4:
1536 case GtVecF32x4:
1537 case GeVecF32x4:
1538 case EqVecF64x2:
1539 case NeVecF64x2:
1540 case LtVecF64x2:
1541 case LeVecF64x2:
1542 case GtVecF64x2:
1543 case GeVecF64x2:
1544 case AndVec128:
1545 case OrVec128:
1546 case XorVec128:
1547 case AndNotVec128:
1548 case AddVecI8x16:
1549 case AddSatSVecI8x16:
1550 case AddSatUVecI8x16:
1551 case SubVecI8x16:
1552 case SubSatSVecI8x16:
1553 case SubSatUVecI8x16:
1554 case MulVecI8x16:
1555 case MinSVecI8x16:
1556 case MinUVecI8x16:
1557 case MaxSVecI8x16:
1558 case MaxUVecI8x16:
1559 case AvgrUVecI8x16:
1560 case AddVecI16x8:
1561 case AddSatSVecI16x8:
1562 case AddSatUVecI16x8:
1563 case SubVecI16x8:
1564 case SubSatSVecI16x8:
1565 case SubSatUVecI16x8:
1566 case MulVecI16x8:
1567 case MinSVecI16x8:
1568 case MinUVecI16x8:
1569 case MaxSVecI16x8:
1570 case MaxUVecI16x8:
1571 case AvgrUVecI16x8:
1572 case AddVecI32x4:
1573 case SubVecI32x4:
1574 case MulVecI32x4:
1575 case MinSVecI32x4:
1576 case MinUVecI32x4:
1577 case MaxSVecI32x4:
1578 case MaxUVecI32x4:
1579 case DotSVecI16x8ToVecI32x4:
1580 case AddVecI64x2:
1581 case SubVecI64x2:
1582 case MulVecI64x2:
1583 case AddVecF32x4:
1584 case SubVecF32x4:
1585 case MulVecF32x4:
1586 case DivVecF32x4:
1587 case MinVecF32x4:
1588 case MaxVecF32x4:
1589 case PMinVecF32x4:
1590 case PMaxVecF32x4:
1591 case AddVecF64x2:
1592 case SubVecF64x2:
1593 case MulVecF64x2:
1594 case DivVecF64x2:
1595 case MinVecF64x2:
1596 case MaxVecF64x2:
1597 case PMinVecF64x2:
1598 case PMaxVecF64x2:
1599 case NarrowSVecI16x8ToVecI8x16:
1600 case NarrowUVecI16x8ToVecI8x16:
1601 case NarrowSVecI32x4ToVecI16x8:
1602 case NarrowUVecI32x4ToVecI16x8:
1603 case SwizzleVec8x16: {
1604 shouldBeEqualOrFirstIsUnreachable(
1605 curr->left->type, Type(Type::v128), curr, "v128 op");
1606 shouldBeEqualOrFirstIsUnreachable(
1607 curr->right->type, Type(Type::v128), curr, "v128 op");
1608 break;
1609 }
1610 case InvalidBinary:
1611 WASM_UNREACHABLE("invliad binary op");
1612 }
1613 shouldBeTrue(Features::get(curr->op) <= getModule()->features,
1614 curr,
1615 "all used features should be allowed");
1616 }
1617
visitUnary(Unary * curr)1618 void FunctionValidator::visitUnary(Unary* curr) {
1619 shouldBeUnequal(curr->value->type,
1620 Type(Type::none),
1621 curr,
1622 "unaries must not receive a none as their input");
1623 if (curr->value->type == Type::unreachable) {
1624 return; // nothing to check
1625 }
1626 switch (curr->op) {
1627 case ClzInt32:
1628 case CtzInt32:
1629 case PopcntInt32: {
1630 shouldBeEqual(curr->value->type,
1631 Type(Type::i32),
1632 curr,
1633 "i32 unary value type must be correct");
1634 break;
1635 }
1636 case ClzInt64:
1637 case CtzInt64:
1638 case PopcntInt64: {
1639 shouldBeEqual(curr->value->type,
1640 Type(Type::i64),
1641 curr,
1642 "i64 unary value type must be correct");
1643 break;
1644 }
1645 case NegFloat32:
1646 case AbsFloat32:
1647 case CeilFloat32:
1648 case FloorFloat32:
1649 case TruncFloat32:
1650 case NearestFloat32:
1651 case SqrtFloat32: {
1652 shouldBeEqual(curr->value->type,
1653 Type(Type::f32),
1654 curr,
1655 "f32 unary value type must be correct");
1656 break;
1657 }
1658 case NegFloat64:
1659 case AbsFloat64:
1660 case CeilFloat64:
1661 case FloorFloat64:
1662 case TruncFloat64:
1663 case NearestFloat64:
1664 case SqrtFloat64: {
1665 shouldBeEqual(curr->value->type,
1666 Type(Type::f64),
1667 curr,
1668 "f64 unary value type must be correct");
1669 break;
1670 }
1671 case EqZInt32: {
1672 shouldBeTrue(
1673 curr->value->type == Type::i32, curr, "i32.eqz input must be i32");
1674 break;
1675 }
1676 case EqZInt64: {
1677 shouldBeTrue(curr->value->type == Type(Type::i64),
1678 curr,
1679 "i64.eqz input must be i64");
1680 break;
1681 }
1682 case ExtendSInt32:
1683 case ExtendUInt32:
1684 case ExtendS8Int32:
1685 case ExtendS16Int32: {
1686 shouldBeEqual(curr->value->type,
1687 Type(Type::i32),
1688 curr,
1689 "extend type must be correct");
1690 break;
1691 }
1692 case ExtendS8Int64:
1693 case ExtendS16Int64:
1694 case ExtendS32Int64: {
1695 shouldBeEqual(curr->value->type,
1696 Type(Type::i64),
1697 curr,
1698 "extend type must be correct");
1699 break;
1700 }
1701 case WrapInt64: {
1702 shouldBeEqual(
1703 curr->value->type, Type(Type::i64), curr, "wrap type must be correct");
1704 break;
1705 }
1706 case TruncSFloat32ToInt32:
1707 case TruncSFloat32ToInt64:
1708 case TruncUFloat32ToInt32:
1709 case TruncUFloat32ToInt64: {
1710 shouldBeEqual(
1711 curr->value->type, Type(Type::f32), curr, "trunc type must be correct");
1712 break;
1713 }
1714 case TruncSatSFloat32ToInt32:
1715 case TruncSatSFloat32ToInt64:
1716 case TruncSatUFloat32ToInt32:
1717 case TruncSatUFloat32ToInt64: {
1718 shouldBeEqual(
1719 curr->value->type, Type(Type::f32), curr, "trunc type must be correct");
1720 break;
1721 }
1722 case TruncSFloat64ToInt32:
1723 case TruncSFloat64ToInt64:
1724 case TruncUFloat64ToInt32:
1725 case TruncUFloat64ToInt64: {
1726 shouldBeEqual(
1727 curr->value->type, Type(Type::f64), curr, "trunc type must be correct");
1728 break;
1729 }
1730 case TruncSatSFloat64ToInt32:
1731 case TruncSatSFloat64ToInt64:
1732 case TruncSatUFloat64ToInt32:
1733 case TruncSatUFloat64ToInt64: {
1734 shouldBeEqual(
1735 curr->value->type, Type(Type::f64), curr, "trunc type must be correct");
1736 break;
1737 }
1738 case ReinterpretFloat32: {
1739 shouldBeEqual(curr->value->type,
1740 Type(Type::f32),
1741 curr,
1742 "reinterpret/f32 type must be correct");
1743 break;
1744 }
1745 case ReinterpretFloat64: {
1746 shouldBeEqual(curr->value->type,
1747 Type(Type::f64),
1748 curr,
1749 "reinterpret/f64 type must be correct");
1750 break;
1751 }
1752 case ConvertUInt32ToFloat32:
1753 case ConvertUInt32ToFloat64:
1754 case ConvertSInt32ToFloat32:
1755 case ConvertSInt32ToFloat64: {
1756 shouldBeEqual(curr->value->type,
1757 Type(Type::i32),
1758 curr,
1759 "convert type must be correct");
1760 break;
1761 }
1762 case ConvertUInt64ToFloat32:
1763 case ConvertUInt64ToFloat64:
1764 case ConvertSInt64ToFloat32:
1765 case ConvertSInt64ToFloat64: {
1766 shouldBeEqual(curr->value->type,
1767 Type(Type::i64),
1768 curr,
1769 "convert type must be correct");
1770 break;
1771 }
1772 case PromoteFloat32: {
1773 shouldBeEqual(curr->value->type,
1774 Type(Type::f32),
1775 curr,
1776 "promote type must be correct");
1777 break;
1778 }
1779 case DemoteFloat64: {
1780 shouldBeEqual(curr->value->type,
1781 Type(Type::f64),
1782 curr,
1783 "demote type must be correct");
1784 break;
1785 }
1786 case ReinterpretInt32: {
1787 shouldBeEqual(curr->value->type,
1788 Type(Type::i32),
1789 curr,
1790 "reinterpret/i32 type must be correct");
1791 break;
1792 }
1793 case ReinterpretInt64: {
1794 shouldBeEqual(curr->value->type,
1795 Type(Type::i64),
1796 curr,
1797 "reinterpret/i64 type must be correct");
1798 break;
1799 }
1800 case SplatVecI8x16:
1801 case SplatVecI16x8:
1802 case SplatVecI32x4:
1803 shouldBeEqual(
1804 curr->type, Type(Type::v128), curr, "expected splat to have v128 type");
1805 shouldBeEqual(
1806 curr->value->type, Type(Type::i32), curr, "expected i32 splat value");
1807 break;
1808 case SplatVecI64x2:
1809 shouldBeEqual(
1810 curr->type, Type(Type::v128), curr, "expected splat to have v128 type");
1811 shouldBeEqual(
1812 curr->value->type, Type(Type::i64), curr, "expected i64 splat value");
1813 break;
1814 case SplatVecF32x4:
1815 shouldBeEqual(
1816 curr->type, Type(Type::v128), curr, "expected splat to have v128 type");
1817 shouldBeEqual(
1818 curr->value->type, Type(Type::f32), curr, "expected f32 splat value");
1819 break;
1820 case SplatVecF64x2:
1821 shouldBeEqual(
1822 curr->type, Type(Type::v128), curr, "expected splat to have v128 type");
1823 shouldBeEqual(
1824 curr->value->type, Type(Type::f64), curr, "expected f64 splat value");
1825 break;
1826 case NotVec128:
1827 case AbsVecI8x16:
1828 case AbsVecI16x8:
1829 case AbsVecI32x4:
1830 case NegVecI8x16:
1831 case NegVecI16x8:
1832 case NegVecI32x4:
1833 case NegVecI64x2:
1834 case AbsVecF32x4:
1835 case NegVecF32x4:
1836 case SqrtVecF32x4:
1837 case CeilVecF32x4:
1838 case FloorVecF32x4:
1839 case TruncVecF32x4:
1840 case NearestVecF32x4:
1841 case AbsVecF64x2:
1842 case NegVecF64x2:
1843 case SqrtVecF64x2:
1844 case CeilVecF64x2:
1845 case FloorVecF64x2:
1846 case TruncVecF64x2:
1847 case NearestVecF64x2:
1848 case TruncSatSVecF32x4ToVecI32x4:
1849 case TruncSatUVecF32x4ToVecI32x4:
1850 case TruncSatSVecF64x2ToVecI64x2:
1851 case TruncSatUVecF64x2ToVecI64x2:
1852 case ConvertSVecI32x4ToVecF32x4:
1853 case ConvertUVecI32x4ToVecF32x4:
1854 case ConvertSVecI64x2ToVecF64x2:
1855 case ConvertUVecI64x2ToVecF64x2:
1856 case WidenLowSVecI8x16ToVecI16x8:
1857 case WidenHighSVecI8x16ToVecI16x8:
1858 case WidenLowUVecI8x16ToVecI16x8:
1859 case WidenHighUVecI8x16ToVecI16x8:
1860 case WidenLowSVecI16x8ToVecI32x4:
1861 case WidenHighSVecI16x8ToVecI32x4:
1862 case WidenLowUVecI16x8ToVecI32x4:
1863 case WidenHighUVecI16x8ToVecI32x4:
1864 shouldBeEqual(curr->type, Type(Type::v128), curr, "expected v128 type");
1865 shouldBeEqual(
1866 curr->value->type, Type(Type::v128), curr, "expected v128 operand");
1867 break;
1868 case AnyTrueVecI8x16:
1869 case AnyTrueVecI16x8:
1870 case AnyTrueVecI32x4:
1871 case AnyTrueVecI64x2:
1872 case AllTrueVecI8x16:
1873 case AllTrueVecI16x8:
1874 case AllTrueVecI32x4:
1875 case AllTrueVecI64x2:
1876 case BitmaskVecI8x16:
1877 case BitmaskVecI16x8:
1878 case BitmaskVecI32x4:
1879 shouldBeEqual(curr->type, Type(Type::i32), curr, "expected i32 type");
1880 shouldBeEqual(
1881 curr->value->type, Type(Type::v128), curr, "expected v128 operand");
1882 break;
1883 case InvalidUnary:
1884 WASM_UNREACHABLE("invalid unary op");
1885 }
1886 shouldBeTrue(Features::get(curr->op) <= getModule()->features,
1887 curr,
1888 "all used features should be allowed");
1889 }
1890
visitSelect(Select * curr)1891 void FunctionValidator::visitSelect(Select* curr) {
1892 shouldBeUnequal(
1893 curr->ifFalse->type, Type(Type::none), curr, "select right must be valid");
1894 shouldBeUnequal(
1895 curr->type, Type(Type::none), curr, "select type must be valid");
1896 shouldBeTrue(curr->condition->type == Type::unreachable ||
1897 curr->condition->type == Type::i32,
1898 curr,
1899 "select condition must be valid");
1900 if (curr->ifTrue->type != Type::unreachable) {
1901 shouldBeFalse(
1902 curr->ifTrue->type.isTuple(), curr, "select value may not be a tuple");
1903 }
1904 if (curr->ifFalse->type != Type::unreachable) {
1905 shouldBeFalse(
1906 curr->ifFalse->type.isTuple(), curr, "select value may not be a tuple");
1907 }
1908 if (curr->type != Type::unreachable) {
1909 shouldBeTrue(Type::isSubType(curr->ifTrue->type, curr->type),
1910 curr,
1911 "select's left expression must be subtype of select's type");
1912 shouldBeTrue(Type::isSubType(curr->ifFalse->type, curr->type),
1913 curr,
1914 "select's right expression must be subtype of select's type");
1915 }
1916 }
1917
visitDrop(Drop * curr)1918 void FunctionValidator::visitDrop(Drop* curr) {
1919 shouldBeTrue(curr->value->type.isConcrete() ||
1920 curr->value->type == Type::unreachable,
1921 curr,
1922 "can only drop a valid value");
1923 }
1924
visitReturn(Return * curr)1925 void FunctionValidator::visitReturn(Return* curr) {
1926 returnTypes.insert(curr->value ? curr->value->type : Type::none);
1927 }
1928
visitMemorySize(MemorySize * curr)1929 void FunctionValidator::visitMemorySize(MemorySize* curr) {
1930 shouldBeTrue(
1931 getModule()->memory.exists, curr, "Memory operations require a memory");
1932 }
1933
visitMemoryGrow(MemoryGrow * curr)1934 void FunctionValidator::visitMemoryGrow(MemoryGrow* curr) {
1935 shouldBeTrue(
1936 getModule()->memory.exists, curr, "Memory operations require a memory");
1937 shouldBeEqualOrFirstIsUnreachable(curr->delta->type,
1938 indexType(),
1939 curr,
1940 "memory.grow must match memory index type");
1941 }
1942
visitRefNull(RefNull * curr)1943 void FunctionValidator::visitRefNull(RefNull* curr) {
1944 shouldBeTrue(getModule()->features.hasReferenceTypes(),
1945 curr,
1946 "ref.null requires reference-types to be enabled");
1947 }
1948
visitRefIsNull(RefIsNull * curr)1949 void FunctionValidator::visitRefIsNull(RefIsNull* curr) {
1950 shouldBeTrue(getModule()->features.hasReferenceTypes(),
1951 curr,
1952 "ref.is_null requires reference-types to be enabled");
1953 shouldBeTrue(curr->value->type == Type::unreachable ||
1954 curr->value->type.isRef(),
1955 curr->value,
1956 "ref.is_null's argument should be a reference type");
1957 }
1958
visitRefFunc(RefFunc * curr)1959 void FunctionValidator::visitRefFunc(RefFunc* curr) {
1960 shouldBeTrue(getModule()->features.hasReferenceTypes(),
1961 curr,
1962 "ref.func requires reference-types to be enabled");
1963 auto* func = getModule()->getFunctionOrNull(curr->func);
1964 shouldBeTrue(!!func, curr, "function argument of ref.func must exist");
1965 }
1966
visitRefEq(RefEq * curr)1967 void FunctionValidator::visitRefEq(RefEq* curr) {
1968 shouldBeTrue(
1969 getModule()->features.hasGC(), curr, "ref.eq requires gc to be enabled");
1970 shouldBeSubTypeOrFirstIsUnreachable(
1971 curr->left->type,
1972 Type::eqref,
1973 curr->left,
1974 "ref.eq's left argument should be a subtype of eqref");
1975 shouldBeSubTypeOrFirstIsUnreachable(
1976 curr->right->type,
1977 Type::eqref,
1978 curr->right,
1979 "ref.eq's right argument should be a subtype of eqref");
1980 }
1981
visitTry(Try * curr)1982 void FunctionValidator::visitTry(Try* curr) {
1983 shouldBeTrue(getModule()->features.hasExceptionHandling(),
1984 curr,
1985 "try requires exception-handling to be enabled");
1986 if (curr->type != Type::unreachable) {
1987 shouldBeSubTypeOrFirstIsUnreachable(
1988 curr->body->type,
1989 curr->type,
1990 curr->body,
1991 "try's type does not match try body's type");
1992 shouldBeSubTypeOrFirstIsUnreachable(
1993 curr->catchBody->type,
1994 curr->type,
1995 curr->catchBody,
1996 "try's type does not match catch's body type");
1997 } else {
1998 shouldBeEqual(curr->body->type,
1999 Type(Type::unreachable),
2000 curr,
2001 "unreachable try-catch must have unreachable try body");
2002 shouldBeEqual(curr->catchBody->type,
2003 Type(Type::unreachable),
2004 curr,
2005 "unreachable try-catch must have unreachable catch body");
2006 }
2007 }
2008
visitThrow(Throw * curr)2009 void FunctionValidator::visitThrow(Throw* curr) {
2010 shouldBeTrue(getModule()->features.hasExceptionHandling(),
2011 curr,
2012 "throw requires exception-handling to be enabled");
2013 if (!info.validateGlobally) {
2014 return;
2015 }
2016 shouldBeEqual(curr->type,
2017 Type(Type::unreachable),
2018 curr,
2019 "throw's type must be unreachable");
2020 auto* event = getModule()->getEventOrNull(curr->event);
2021 if (!shouldBeTrue(!!event, curr, "throw's event must exist")) {
2022 return;
2023 }
2024 if (!shouldBeTrue(curr->operands.size() == event->sig.params.size(),
2025 curr,
2026 "event's param numbers must match")) {
2027 return;
2028 }
2029 size_t i = 0;
2030 for (const auto& param : event->sig.params) {
2031 if (!shouldBeSubTypeOrFirstIsUnreachable(curr->operands[i]->type,
2032 param,
2033 curr->operands[i],
2034 "event param types must match") &&
2035 !info.quiet) {
2036 getStream() << "(on argument " << i << ")\n";
2037 }
2038 ++i;
2039 }
2040 }
2041
visitRethrow(Rethrow * curr)2042 void FunctionValidator::visitRethrow(Rethrow* curr) {
2043 shouldBeTrue(getModule()->features.hasExceptionHandling(),
2044 curr,
2045 "rethrow requires exception-handling to be enabled");
2046 shouldBeEqual(curr->type,
2047 Type(Type::unreachable),
2048 curr,
2049 "rethrow's type must be unreachable");
2050 shouldBeSubTypeOrFirstIsUnreachable(
2051 curr->exnref->type,
2052 Type::exnref,
2053 curr->exnref,
2054 "rethrow's argument must be exnref type or its subtype");
2055 }
2056
visitBrOnExn(BrOnExn * curr)2057 void FunctionValidator::visitBrOnExn(BrOnExn* curr) {
2058 shouldBeTrue(getModule()->features.hasExceptionHandling(),
2059 curr,
2060 "br_on_exn requires exception-handling to be enabled");
2061 Event* event = getModule()->getEventOrNull(curr->event);
2062 shouldBeTrue(event != nullptr, curr, "br_on_exn's event must exist");
2063 shouldBeTrue(event->sig.params == curr->sent,
2064 curr,
2065 "br_on_exn's event params and event's params are different");
2066 noteBreak(curr->name, curr->sent, curr);
2067 shouldBeSubTypeOrFirstIsUnreachable(
2068 curr->exnref->type,
2069 Type::exnref,
2070 curr,
2071 "br_on_exn's argument must be unreachable or exnref type or its subtype");
2072 if (curr->exnref->type == Type::unreachable) {
2073 shouldBeTrue(curr->type == Type::unreachable,
2074 curr,
2075 "If exnref argument's type is unreachable, br_on_exn should "
2076 "be unreachable too");
2077 } else {
2078 shouldBeTrue(curr->type == Type::exnref,
2079 curr,
2080 "br_on_exn's type should be exnref unless its exnref argument "
2081 "is unreachable");
2082 }
2083 }
2084
visitTupleMake(TupleMake * curr)2085 void FunctionValidator::visitTupleMake(TupleMake* curr) {
2086 shouldBeTrue(getModule()->features.hasMultivalue(),
2087 curr,
2088 "Tuples are not allowed unless multivalue is enabled");
2089 shouldBeTrue(
2090 curr->operands.size() > 1, curr, "tuple.make must have multiple operands");
2091 std::vector<Type> types;
2092 for (auto* op : curr->operands) {
2093 if (op->type == Type::unreachable) {
2094 shouldBeTrue(
2095 curr->type == Type::unreachable,
2096 curr,
2097 "If tuple.make has an unreachable operand, it must be unreachable");
2098 return;
2099 }
2100 types.push_back(op->type);
2101 }
2102 shouldBeSubType(Type(types),
2103 curr->type,
2104 curr,
2105 "Type of tuple.make does not match types of its operands");
2106 }
2107
visitTupleExtract(TupleExtract * curr)2108 void FunctionValidator::visitTupleExtract(TupleExtract* curr) {
2109 shouldBeTrue(getModule()->features.hasMultivalue(),
2110 curr,
2111 "Tuples are not allowed unless multivalue is enabled");
2112 if (curr->tuple->type == Type::unreachable) {
2113 shouldBeTrue(
2114 curr->type == Type::unreachable,
2115 curr,
2116 "If tuple.extract has an unreachable operand, it must be unreachable");
2117 } else {
2118 bool inBounds = curr->index < curr->tuple->type.size();
2119 shouldBeTrue(inBounds, curr, "tuple.extract index out of bounds");
2120 if (inBounds) {
2121 shouldBeSubType(
2122 curr->tuple->type[curr->index],
2123 curr->type,
2124 curr,
2125 "tuple.extract type does not match the type of the extracted element");
2126 }
2127 }
2128 }
2129
visitI31New(I31New * curr)2130 void FunctionValidator::visitI31New(I31New* curr) {
2131 shouldBeTrue(
2132 getModule()->features.hasGC(), curr, "i31.new requires gc to be enabled");
2133 shouldBeSubTypeOrFirstIsUnreachable(curr->value->type,
2134 Type::i32,
2135 curr->value,
2136 "i31.new's argument should be i32");
2137 }
2138
visitI31Get(I31Get * curr)2139 void FunctionValidator::visitI31Get(I31Get* curr) {
2140 shouldBeTrue(getModule()->features.hasGC(),
2141 curr,
2142 "i31.get_s/u requires gc to be enabled");
2143 shouldBeSubTypeOrFirstIsUnreachable(
2144 curr->i31->type,
2145 Type::i31ref,
2146 curr->i31,
2147 "i31.get_s/u's argument should be i31ref");
2148 }
2149
visitRefTest(RefTest * curr)2150 void FunctionValidator::visitRefTest(RefTest* curr) {
2151 shouldBeTrue(
2152 getModule()->features.hasGC(), curr, "ref.test requires gc to be enabled");
2153 WASM_UNREACHABLE("TODO (gc): ref.test");
2154 }
2155
visitRefCast(RefCast * curr)2156 void FunctionValidator::visitRefCast(RefCast* curr) {
2157 shouldBeTrue(
2158 getModule()->features.hasGC(), curr, "ref.cast requires gc to be enabled");
2159 WASM_UNREACHABLE("TODO (gc): ref.cast");
2160 }
2161
visitBrOnCast(BrOnCast * curr)2162 void FunctionValidator::visitBrOnCast(BrOnCast* curr) {
2163 shouldBeTrue(getModule()->features.hasGC(),
2164 curr,
2165 "br_on_cast requires gc to be enabled");
2166 WASM_UNREACHABLE("TODO (gc): br_on_cast");
2167 }
2168
visitRttCanon(RttCanon * curr)2169 void FunctionValidator::visitRttCanon(RttCanon* curr) {
2170 shouldBeTrue(
2171 getModule()->features.hasGC(), curr, "rtt.canon requires gc to be enabled");
2172 WASM_UNREACHABLE("TODO (gc): rtt.canon");
2173 }
2174
visitRttSub(RttSub * curr)2175 void FunctionValidator::visitRttSub(RttSub* curr) {
2176 shouldBeTrue(
2177 getModule()->features.hasGC(), curr, "rtt.sub requires gc to be enabled");
2178 WASM_UNREACHABLE("TODO (gc): rtt.sub");
2179 }
2180
visitStructNew(StructNew * curr)2181 void FunctionValidator::visitStructNew(StructNew* curr) {
2182 shouldBeTrue(getModule()->features.hasGC(),
2183 curr,
2184 "struct.new requires gc to be enabled");
2185 WASM_UNREACHABLE("TODO (gc): struct.new");
2186 }
2187
visitStructGet(StructGet * curr)2188 void FunctionValidator::visitStructGet(StructGet* curr) {
2189 shouldBeTrue(getModule()->features.hasGC(),
2190 curr,
2191 "struct.get requires gc to be enabled");
2192 WASM_UNREACHABLE("TODO (gc): struct.get");
2193 }
2194
visitStructSet(StructSet * curr)2195 void FunctionValidator::visitStructSet(StructSet* curr) {
2196 shouldBeTrue(getModule()->features.hasGC(),
2197 curr,
2198 "struct.set requires gc to be enabled");
2199 WASM_UNREACHABLE("TODO (gc): struct.set");
2200 }
2201
visitArrayNew(ArrayNew * curr)2202 void FunctionValidator::visitArrayNew(ArrayNew* curr) {
2203 shouldBeTrue(
2204 getModule()->features.hasGC(), curr, "array.new requires gc to be enabled");
2205 WASM_UNREACHABLE("TODO (gc): array.new");
2206 }
2207
visitArrayGet(ArrayGet * curr)2208 void FunctionValidator::visitArrayGet(ArrayGet* curr) {
2209 shouldBeTrue(
2210 getModule()->features.hasGC(), curr, "array.get requires gc to be enabled");
2211 WASM_UNREACHABLE("TODO (gc): array.get");
2212 }
2213
visitArraySet(ArraySet * curr)2214 void FunctionValidator::visitArraySet(ArraySet* curr) {
2215 shouldBeTrue(
2216 getModule()->features.hasGC(), curr, "array.set requires gc to be enabled");
2217 WASM_UNREACHABLE("TODO (gc): array.set");
2218 }
2219
visitArrayLen(ArrayLen * curr)2220 void FunctionValidator::visitArrayLen(ArrayLen* curr) {
2221 shouldBeTrue(
2222 getModule()->features.hasGC(), curr, "array.len requires gc to be enabled");
2223 WASM_UNREACHABLE("TODO (gc): array.len");
2224 }
2225
visitFunction(Function * curr)2226 void FunctionValidator::visitFunction(Function* curr) {
2227 if (curr->sig.results.isTuple()) {
2228 shouldBeTrue(getModule()->features.hasMultivalue(),
2229 curr->body,
2230 "Multivalue function results (multivalue is not enabled)");
2231 }
2232 FeatureSet features;
2233 for (const auto& param : curr->sig.params) {
2234 features |= param.getFeatures();
2235 shouldBeTrue(param.isConcrete(), curr, "params must be concretely typed");
2236 }
2237 for (const auto& result : curr->sig.results) {
2238 features |= result.getFeatures();
2239 shouldBeTrue(result.isConcrete(), curr, "results must be concretely typed");
2240 }
2241 for (const auto& var : curr->vars) {
2242 features |= var.getFeatures();
2243 shouldBeTrue(var.isConcrete(), curr, "vars must be concretely typed");
2244 }
2245 shouldBeTrue(features <= getModule()->features,
2246 curr,
2247 "all used types should be allowed");
2248 if (curr->profile == IRProfile::Poppy) {
2249 shouldBeTrue(
2250 curr->body->is<Block>(), curr->body, "Function body must be a block");
2251 }
2252 // if function has no result, it is ignored
2253 // if body is unreachable, it might be e.g. a return
2254 shouldBeSubTypeOrFirstIsUnreachable(
2255 curr->body->type,
2256 curr->sig.results,
2257 curr->body,
2258 "function body type must match, if function returns");
2259 for (Type returnType : returnTypes) {
2260 shouldBeSubTypeOrFirstIsUnreachable(
2261 returnType,
2262 curr->sig.results,
2263 curr->body,
2264 "function result must match, if function has returns");
2265 }
2266
2267 shouldBeTrue(
2268 breakInfos.empty(), curr->body, "all named break targets must exist");
2269 returnTypes.clear();
2270 labelNames.clear();
2271 // validate optional local names
2272 std::set<Name> seen;
2273 for (auto& pair : curr->localNames) {
2274 Name name = pair.second;
2275 shouldBeTrue(seen.insert(name).second, name, "local names must be unique");
2276 }
2277 }
2278
checkSegmentOffset(Expression * curr,Address add,Address max)2279 static bool checkSegmentOffset(Expression* curr, Address add, Address max) {
2280 if (curr->is<GlobalGet>()) {
2281 return true;
2282 }
2283 auto* c = curr->dynCast<Const>();
2284 if (!c) {
2285 return false;
2286 }
2287 uint64_t raw = c->value.getInteger();
2288 if (raw > std::numeric_limits<Address::address32_t>::max()) {
2289 return false;
2290 }
2291 if (raw + uint64_t(add) > std::numeric_limits<Address::address32_t>::max()) {
2292 return false;
2293 }
2294 Address offset = raw;
2295 return offset + add <= max;
2296 }
2297
validateAlignment(size_t align,Type type,Index bytes,bool isAtomic,Expression * curr)2298 void FunctionValidator::validateAlignment(
2299 size_t align, Type type, Index bytes, bool isAtomic, Expression* curr) {
2300 if (isAtomic) {
2301 shouldBeEqual(align,
2302 (size_t)bytes,
2303 curr,
2304 "atomic accesses must have natural alignment");
2305 return;
2306 }
2307 switch (align) {
2308 case 1:
2309 case 2:
2310 case 4:
2311 case 8:
2312 case 16:
2313 break;
2314 default: {
2315 info.fail("bad alignment: " + std::to_string(align), curr, getFunction());
2316 break;
2317 }
2318 }
2319 shouldBeTrue(align <= bytes, curr, "alignment must not exceed natural");
2320 TODO_SINGLE_COMPOUND(type);
2321 switch (type.getBasic()) {
2322 case Type::i32:
2323 case Type::f32: {
2324 shouldBeTrue(align <= 4, curr, "alignment must not exceed natural");
2325 break;
2326 }
2327 case Type::i64:
2328 case Type::f64: {
2329 shouldBeTrue(align <= 8, curr, "alignment must not exceed natural");
2330 break;
2331 }
2332 case Type::v128:
2333 case Type::unreachable:
2334 break;
2335 case Type::funcref:
2336 case Type::externref:
2337 case Type::exnref:
2338 case Type::anyref:
2339 case Type::eqref:
2340 case Type::i31ref:
2341 case Type::none:
2342 WASM_UNREACHABLE("invalid type");
2343 }
2344 }
2345
validateBinaryenIR(Module & wasm,ValidationInfo & info)2346 static void validateBinaryenIR(Module& wasm, ValidationInfo& info) {
2347 struct BinaryenIRValidator
2348 : public PostWalker<BinaryenIRValidator,
2349 UnifiedExpressionVisitor<BinaryenIRValidator>> {
2350 ValidationInfo& info;
2351
2352 std::unordered_set<Expression*> seen;
2353
2354 BinaryenIRValidator(ValidationInfo& info) : info(info) {}
2355
2356 void visitExpression(Expression* curr) {
2357 auto scope = getFunction() ? getFunction()->name : Name("(global scope)");
2358 // check if a node type is 'stale', i.e., we forgot to finalize() the
2359 // node.
2360 auto oldType = curr->type;
2361 ReFinalizeNode().visit(curr);
2362 auto newType = curr->type;
2363 if (newType != oldType) {
2364 // We accept concrete => undefined,
2365 // e.g.
2366 //
2367 // (drop (block (result i32) (unreachable)))
2368 //
2369 // The block has an added type, not derived from the ast itself, so it
2370 // is ok for it to be either i32 or unreachable.
2371 if (!Type::isSubType(newType, oldType) &&
2372 !(oldType.isConcrete() && newType == Type::unreachable)) {
2373 std::ostringstream ss;
2374 ss << "stale type found in " << scope << " on " << curr
2375 << "\n(marked as " << oldType << ", should be " << newType
2376 << ")\n";
2377 info.fail(ss.str(), curr, getFunction());
2378 }
2379 curr->type = oldType;
2380 }
2381 // check if a node is a duplicate - expressions must not be seen more than
2382 // once
2383 bool inserted;
2384 std::tie(std::ignore, inserted) = seen.insert(curr);
2385 if (!inserted) {
2386 std::ostringstream ss;
2387 ss << "expression seen more than once in the tree in " << scope
2388 << " on " << curr << '\n';
2389 info.fail(ss.str(), curr, getFunction());
2390 }
2391 }
2392 };
2393 BinaryenIRValidator binaryenIRValidator(info);
2394 binaryenIRValidator.walkModule(&wasm);
2395 }
2396
2397 // Main validator class
2398
validateImports(Module & module,ValidationInfo & info)2399 static void validateImports(Module& module, ValidationInfo& info) {
2400 ModuleUtils::iterImportedFunctions(module, [&](Function* curr) {
2401 if (curr->sig.results.isTuple()) {
2402 info.shouldBeTrue(module.features.hasMultivalue(),
2403 curr->name,
2404 "Imported multivalue function "
2405 "(multivalue is not enabled)");
2406 }
2407 if (info.validateWeb) {
2408 for (const auto& param : curr->sig.params) {
2409 info.shouldBeUnequal(param,
2410 Type(Type::i64),
2411 curr->name,
2412 "Imported function must not have i64 parameters");
2413 }
2414 for (const auto& result : curr->sig.results) {
2415 info.shouldBeUnequal(result,
2416 Type(Type::i64),
2417 curr->name,
2418 "Imported function must not have i64 results");
2419 }
2420 }
2421 });
2422 ModuleUtils::iterImportedGlobals(module, [&](Global* curr) {
2423 if (!module.features.hasMutableGlobals()) {
2424 info.shouldBeFalse(
2425 curr->mutable_, curr->name, "Imported global cannot be mutable");
2426 }
2427 info.shouldBeFalse(
2428 curr->type.isTuple(), curr->name, "Imported global cannot be tuple");
2429 });
2430 }
2431
validateExports(Module & module,ValidationInfo & info)2432 static void validateExports(Module& module, ValidationInfo& info) {
2433 for (auto& curr : module.exports) {
2434 if (curr->kind == ExternalKind::Function) {
2435 if (info.validateWeb) {
2436 Function* f = module.getFunction(curr->value);
2437 for (const auto& param : f->sig.params) {
2438 info.shouldBeUnequal(
2439 param,
2440 Type(Type::i64),
2441 f->name,
2442 "Exported function must not have i64 parameters");
2443 }
2444 for (const auto& result : f->sig.results) {
2445 info.shouldBeUnequal(result,
2446 Type(Type::i64),
2447 f->name,
2448 "Exported function must not have i64 results");
2449 }
2450 }
2451 } else if (curr->kind == ExternalKind::Global) {
2452 if (Global* g = module.getGlobalOrNull(curr->value)) {
2453 if (!module.features.hasMutableGlobals()) {
2454 info.shouldBeFalse(
2455 g->mutable_, g->name, "Exported global cannot be mutable");
2456 }
2457 info.shouldBeFalse(
2458 g->type.isTuple(), g->name, "Exported global cannot be tuple");
2459 }
2460 }
2461 }
2462 std::unordered_set<Name> exportNames;
2463 for (auto& exp : module.exports) {
2464 Name name = exp->value;
2465 if (exp->kind == ExternalKind::Function) {
2466 info.shouldBeTrue(module.getFunctionOrNull(name),
2467 name,
2468 "module function exports must be found");
2469 } else if (exp->kind == ExternalKind::Global) {
2470 info.shouldBeTrue(module.getGlobalOrNull(name),
2471 name,
2472 "module global exports must be found");
2473 } else if (exp->kind == ExternalKind::Table) {
2474 info.shouldBeTrue(name == Name("0") || name == module.table.name,
2475 name,
2476 "module table exports must be found");
2477 } else if (exp->kind == ExternalKind::Memory) {
2478 info.shouldBeTrue(name == Name("0") || name == module.memory.name,
2479 name,
2480 "module memory exports must be found");
2481 } else if (exp->kind == ExternalKind::Event) {
2482 info.shouldBeTrue(module.getEventOrNull(name),
2483 name,
2484 "module event exports must be found");
2485 } else {
2486 WASM_UNREACHABLE("invalid ExternalKind");
2487 }
2488 Name exportName = exp->name;
2489 info.shouldBeFalse(exportNames.count(exportName) > 0,
2490 exportName,
2491 "module exports must be unique");
2492 exportNames.insert(exportName);
2493 }
2494 }
2495
validateGlobals(Module & module,ValidationInfo & info)2496 static void validateGlobals(Module& module, ValidationInfo& info) {
2497 ModuleUtils::iterDefinedGlobals(module, [&](Global* curr) {
2498 info.shouldBeTrue(curr->type.getFeatures() <= module.features,
2499 curr->name,
2500 "all used types should be allowed");
2501 info.shouldBeTrue(
2502 curr->init != nullptr, curr->name, "global init must be non-null");
2503 assert(curr->init);
2504 info.shouldBeTrue(GlobalUtils::canInitializeGlobal(curr->init),
2505 curr->name,
2506 "global init must be valid");
2507
2508 if (!info.shouldBeSubType(curr->init->type,
2509 curr->type,
2510 curr->init,
2511 "global init must have correct type") &&
2512 !info.quiet) {
2513 info.getStream(nullptr) << "(on global " << curr->name << ")\n";
2514 }
2515 });
2516 }
2517
validateMemory(Module & module,ValidationInfo & info)2518 static void validateMemory(Module& module, ValidationInfo& info) {
2519 auto& curr = module.memory;
2520 info.shouldBeFalse(
2521 curr.initial > curr.max, "memory", "memory max >= initial");
2522 if (curr.is64()) {
2523 info.shouldBeTrue(module.features.hasMemory64(),
2524 "memory",
2525 "memory is 64-bit, but memory64 is disabled");
2526 } else {
2527 info.shouldBeTrue(curr.initial <= Memory::kMaxSize32,
2528 "memory",
2529 "initial memory must be <= 4GB");
2530 info.shouldBeTrue(!curr.hasMax() || curr.max <= Memory::kMaxSize32,
2531 "memory",
2532 "max memory must be <= 4GB, or unlimited");
2533 }
2534 info.shouldBeTrue(!curr.shared || curr.hasMax(),
2535 "memory",
2536 "shared memory must have max size");
2537 if (curr.shared) {
2538 info.shouldBeTrue(module.features.hasAtomics(),
2539 "memory",
2540 "memory is shared, but atomics are disabled");
2541 }
2542 for (auto& segment : curr.segments) {
2543 Index size = segment.data.size();
2544 if (segment.isPassive) {
2545 info.shouldBeTrue(module.features.hasBulkMemory(),
2546 segment.offset,
2547 "nonzero segment flags (bulk memory is disabled)");
2548 info.shouldBeEqual(segment.offset,
2549 (Expression*)nullptr,
2550 segment.offset,
2551 "passive segment should not have an offset");
2552 } else {
2553 if (!info.shouldBeEqual(segment.offset->type,
2554 Type(Type::i32),
2555 segment.offset,
2556 "segment offset should be i32")) {
2557 continue;
2558 }
2559 info.shouldBeTrue(checkSegmentOffset(segment.offset,
2560 segment.data.size(),
2561 curr.initial * Memory::kPageSize),
2562 segment.offset,
2563 "memory segment offset should be reasonable");
2564 if (segment.offset->is<Const>()) {
2565 Index start = segment.offset->cast<Const>()->value.geti32();
2566 Index end = start + size;
2567 info.shouldBeTrue(end <= curr.initial * Memory::kPageSize,
2568 segment.data.size(),
2569 "segment size should fit in memory (end)");
2570 }
2571 }
2572 // If the memory is imported we don't actually know its initial size.
2573 // Specifically wasm dll's import a zero sized memory which is perfectly
2574 // valid.
2575 if (!curr.imported()) {
2576 info.shouldBeTrue(size <= curr.initial * Memory::kPageSize,
2577 segment.data.size(),
2578 "segment size should fit in memory (initial)");
2579 }
2580 }
2581 }
2582
validateTable(Module & module,ValidationInfo & info)2583 static void validateTable(Module& module, ValidationInfo& info) {
2584 auto& curr = module.table;
2585 for (auto& segment : curr.segments) {
2586 info.shouldBeEqual(segment.offset->type,
2587 Type(Type::i32),
2588 segment.offset,
2589 "segment offset should be i32");
2590 info.shouldBeTrue(
2591 checkSegmentOffset(segment.offset,
2592 segment.data.size(),
2593 module.table.initial * Table::kPageSize),
2594 segment.offset,
2595 "table segment offset should be reasonable");
2596 for (auto name : segment.data) {
2597 info.shouldBeTrue(
2598 module.getFunctionOrNull(name), name, "segment name should be valid");
2599 }
2600 }
2601 }
2602
validateEvents(Module & module,ValidationInfo & info)2603 static void validateEvents(Module& module, ValidationInfo& info) {
2604 if (!module.events.empty()) {
2605 info.shouldBeTrue(module.features.hasExceptionHandling(),
2606 module.events[0]->name,
2607 "Module has events (event-handling is disabled)");
2608 }
2609 for (auto& curr : module.events) {
2610 info.shouldBeEqual(curr->attribute,
2611 (unsigned)0,
2612 curr->attribute,
2613 "Currently only attribute 0 is supported");
2614 info.shouldBeEqual(curr->sig.results,
2615 Type(Type::none),
2616 curr->name,
2617 "Event type's result type should be none");
2618 if (curr->sig.params.isTuple()) {
2619 info.shouldBeTrue(module.features.hasMultivalue(),
2620 curr->name,
2621 "Multivalue event type (multivalue is not enabled)");
2622 }
2623 for (const auto& param : curr->sig.params) {
2624 info.shouldBeTrue(param.isConcrete(),
2625 curr->name,
2626 "Values in an event should have concrete types");
2627 }
2628 }
2629 }
2630
validateModule(Module & module,ValidationInfo & info)2631 static void validateModule(Module& module, ValidationInfo& info) {
2632 // start
2633 if (module.start.is()) {
2634 auto func = module.getFunctionOrNull(module.start);
2635 if (info.shouldBeTrue(
2636 func != nullptr, module.start, "start must be found")) {
2637 info.shouldBeTrue(func->sig.params == Type::none,
2638 module.start,
2639 "start must have 0 params");
2640 info.shouldBeTrue(func->sig.results == Type::none,
2641 module.start,
2642 "start must not return a value");
2643 }
2644 }
2645 }
2646
validateFeatures(Module & module,ValidationInfo & info)2647 static void validateFeatures(Module& module, ValidationInfo& info) {
2648 if (module.features.hasGC()) {
2649 info.shouldBeTrue(module.features.hasReferenceTypes(),
2650 module.features,
2651 "--enable-gc requires --enable-reference-types");
2652 }
2653 if (module.features.hasExceptionHandling()) { // implies exnref
2654 info.shouldBeTrue(
2655 module.features.hasReferenceTypes(),
2656 module.features,
2657 "--enable-exception-handling requires --enable-reference-types");
2658 }
2659 }
2660
2661 // TODO: If we want the validator to be part of libwasm rather than libpasses,
2662 // then Using PassRunner::getPassDebug causes a circular dependence. We should
2663 // fix that, perhaps by moving some of the pass infrastructure into libsupport.
validate(Module & module,Flags flags)2664 bool WasmValidator::validate(Module& module, Flags flags) {
2665 ValidationInfo info;
2666 info.validateWeb = (flags & Web) != 0;
2667 info.validateGlobally = (flags & Globally) != 0;
2668 info.quiet = (flags & Quiet) != 0;
2669 // parallel wasm logic validation
2670 PassRunner runner(&module);
2671 FunctionValidator(&info).run(&runner, &module);
2672 // validate globally
2673 if (info.validateGlobally) {
2674 validateImports(module, info);
2675 validateExports(module, info);
2676 validateGlobals(module, info);
2677 validateMemory(module, info);
2678 validateTable(module, info);
2679 validateEvents(module, info);
2680 validateModule(module, info);
2681 validateFeatures(module, info);
2682 }
2683 // validate additional internal IR details when in pass-debug mode
2684 if (PassRunner::getPassDebug()) {
2685 validateBinaryenIR(module, info);
2686 }
2687 // print all the data
2688 if (!info.valid.load() && !info.quiet) {
2689 for (auto& func : module.functions) {
2690 std::cerr << info.getStream(func.get()).str();
2691 }
2692 std::cerr << info.getStream(nullptr).str();
2693 }
2694 return info.valid.load();
2695 }
2696
2697 } // namespace wasm
2698