|
33 | 33 | // about function B not existing yet, but we would care |
34 | 34 | // if e.g. inside function A an i32.add receives an i64). |
35 | 35 | // |
| 36 | +// * quiet: Whether to log errors verbosely. |
| 37 | +// |
36 | 38 |
|
37 | 39 | #ifndef wasm_wasm_validator_h |
38 | 40 | #define wasm_wasm_validator_h |
|
46 | 48 |
|
47 | 49 | namespace wasm { |
48 | 50 |
|
49 | | -// Print anything that can be streamed to an ostream |
50 | | -template <typename T, |
51 | | - typename std::enable_if< |
52 | | - !std::is_base_of<Expression, typename std::remove_pointer<T>::type>::value |
53 | | - >::type* = nullptr> |
54 | | -inline std::ostream& printModuleComponent(T curr, std::ostream& stream) { |
55 | | - stream << curr << std::endl; |
56 | | - return stream; |
57 | | -} |
58 | | -// Extra overload for Expressions, to print type info too |
59 | | -inline std::ostream& printModuleComponent(Expression* curr, std::ostream& stream) { |
60 | | - WasmPrinter::printExpression(curr, stream, false, true) << std::endl; |
61 | | - return stream; |
62 | | -} |
63 | | - |
64 | | -struct WasmValidator : public PostWalker<WasmValidator> { |
65 | | - bool valid = true; |
66 | | - |
67 | | - // what to validate, see comment up top |
68 | | - bool validateWeb = false; |
69 | | - bool validateGlobally = true; |
70 | | - |
71 | | - bool quiet = false; // whether to log errors verbosely |
72 | | - |
73 | | - struct BreakInfo { |
74 | | - WasmType type; |
75 | | - Index arity; |
76 | | - BreakInfo() {} |
77 | | - BreakInfo(WasmType type, Index arity) : type(type), arity(arity) {} |
78 | | - }; |
79 | | - |
80 | | - std::map<Name, Expression*> breakTargets; |
81 | | - std::map<Expression*, BreakInfo> breakInfos; |
82 | | - |
83 | | - WasmType returnType = unreachable; // type used in returns |
84 | | - |
85 | | - std::set<Name> labelNames; // Binaryen IR requires that label names must be unique - IR generators must ensure that |
86 | | - |
87 | | - std::unordered_set<Expression*> seenExpressions; // expressions must not appear twice |
88 | | - |
89 | | - void noteLabelName(Name name); |
90 | | - |
91 | | -public: |
92 | | - // TODO: If we want the validator to be part of libwasm rather than libpasses, then |
93 | | - // Using PassRunner::getPassDebug causes a circular dependence. We should fix that, |
94 | | - // perhaps by moving some of the pass infrastructure into libsupport. |
95 | | - bool validate(Module& module, bool validateWeb_ = false, bool validateGlobally_ = true) { |
96 | | - validateWeb = validateWeb_; |
97 | | - validateGlobally = validateGlobally_; |
98 | | - // wasm logic validation |
99 | | - walkModule(&module); |
100 | | - // validate additional internal IR details when in pass-debug mode |
101 | | - if (PassRunner::getPassDebug()) { |
102 | | - validateBinaryenIR(module); |
103 | | - } |
104 | | - // print if an error occurred |
105 | | - if (!valid && !quiet) { |
106 | | - WasmPrinter::printModule(&module, std::cerr); |
107 | | - } |
108 | | - return valid; |
109 | | - } |
110 | | - |
111 | | - // visitors |
112 | | - |
113 | | - static void visitPreBlock(WasmValidator* self, Expression** currp) { |
114 | | - auto* curr = (*currp)->cast<Block>(); |
115 | | - if (curr->name.is()) self->breakTargets[curr->name] = curr; |
116 | | - } |
117 | | - |
118 | | - void visitBlock(Block *curr); |
119 | | - |
120 | | - static void visitPreLoop(WasmValidator* self, Expression** currp) { |
121 | | - auto* curr = (*currp)->cast<Loop>(); |
122 | | - if (curr->name.is()) self->breakTargets[curr->name] = curr; |
123 | | - } |
124 | | - |
125 | | - void visitLoop(Loop *curr); |
126 | | - void visitIf(If *curr); |
127 | | - |
128 | | - // override scan to add a pre and a post check task to all nodes |
129 | | - static void scan(WasmValidator* self, Expression** currp) { |
130 | | - PostWalker<WasmValidator>::scan(self, currp); |
131 | | - |
132 | | - auto* curr = *currp; |
133 | | - if (curr->is<Block>()) self->pushTask(visitPreBlock, currp); |
134 | | - if (curr->is<Loop>()) self->pushTask(visitPreLoop, currp); |
135 | | - } |
136 | | - |
137 | | - void noteBreak(Name name, Expression* value, Expression* curr); |
138 | | - void visitBreak(Break *curr); |
139 | | - void visitSwitch(Switch *curr); |
140 | | - void visitCall(Call *curr); |
141 | | - void visitCallImport(CallImport *curr); |
142 | | - void visitCallIndirect(CallIndirect *curr); |
143 | | - void visitGetLocal(GetLocal* curr); |
144 | | - void visitSetLocal(SetLocal *curr); |
145 | | - void visitLoad(Load *curr); |
146 | | - void visitStore(Store *curr); |
147 | | - void visitAtomicRMW(AtomicRMW *curr); |
148 | | - void visitAtomicCmpxchg(AtomicCmpxchg *curr); |
149 | | - void visitAtomicWait(AtomicWait *curr); |
150 | | - void visitAtomicWake(AtomicWake *curr); |
151 | | - void visitBinary(Binary *curr); |
152 | | - void visitUnary(Unary *curr); |
153 | | - void visitSelect(Select* curr); |
154 | | - void visitDrop(Drop* curr); |
155 | | - void visitReturn(Return* curr); |
156 | | - void visitHost(Host* curr); |
157 | | - void visitImport(Import* curr); |
158 | | - void visitExport(Export* curr); |
159 | | - void visitGlobal(Global* curr); |
160 | | - void visitFunction(Function *curr); |
161 | | - |
162 | | - void visitMemory(Memory *curr); |
163 | | - void visitTable(Table* curr); |
164 | | - void visitModule(Module *curr); |
165 | | - |
166 | | - void doWalkFunction(Function* func) { |
167 | | - PostWalker<WasmValidator>::doWalkFunction(func); |
168 | | - } |
169 | | - |
170 | | - // helpers |
171 | | - private: |
172 | | - template <typename T, typename S> |
173 | | - std::ostream& fail(S text, T curr); |
174 | | - std::ostream& printFailureHeader(); |
175 | | - |
176 | | - template<typename T> |
177 | | - bool shouldBeTrue(bool result, T curr, const char* text) { |
178 | | - if (!result) { |
179 | | - fail("unexpected false: " + std::string(text), curr); |
180 | | - return false; |
181 | | - } |
182 | | - return result; |
183 | | - } |
184 | | - template<typename T> |
185 | | - bool shouldBeFalse(bool result, T curr, const char* text) { |
186 | | - if (result) { |
187 | | - fail("unexpected true: " + std::string(text), curr); |
188 | | - return false; |
189 | | - } |
190 | | - return result; |
191 | | - } |
192 | | - |
193 | | - template<typename T, typename S> |
194 | | - bool shouldBeEqual(S left, S right, T curr, const char* text) { |
195 | | - if (left != right) { |
196 | | - std::ostringstream ss; |
197 | | - ss << left << " != " << right << ": " << text; |
198 | | - fail(ss.str(), curr); |
199 | | - return false; |
200 | | - } |
201 | | - return true; |
202 | | - } |
203 | | - |
204 | | - template<typename T, typename S> |
205 | | - bool shouldBeEqualOrFirstIsUnreachable(S left, S right, T curr, const char* text) { |
206 | | - if (left != unreachable && left != right) { |
207 | | - std::ostringstream ss; |
208 | | - ss << left << " != " << right << ": " << text; |
209 | | - fail(ss.str(), curr); |
210 | | - return false; |
211 | | - } |
212 | | - return true; |
213 | | - } |
214 | | - |
215 | | - template<typename T, typename S> |
216 | | - bool shouldBeUnequal(S left, S right, T curr, const char* text) { |
217 | | - if (left == right) { |
218 | | - std::ostringstream ss; |
219 | | - ss << left << " == " << right << ": " << text; |
220 | | - fail(ss.str(), curr); |
221 | | - return false; |
222 | | - } |
223 | | - return true; |
224 | | - } |
225 | | - |
226 | | - void shouldBeIntOrUnreachable(WasmType ty, Expression* curr, const char* text); |
227 | | - void validateAlignment(size_t align, WasmType type, Index bytes, bool isAtomic, |
228 | | - Expression* curr); |
229 | | - void validateMemBytes(uint8_t bytes, WasmType type, Expression* curr); |
230 | | - void validateBinaryenIR(Module& wasm); |
| 51 | +struct WasmValidator { |
| 52 | + bool validate(Module& module, bool validateWeb = false, bool validateGlobally = true, bool quiet = false); |
231 | 53 | }; |
232 | 54 |
|
233 | 55 | } // namespace wasm |
|
0 commit comments