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