Skip to content

Commit 1f8d8a5

Browse files
authored
Fast validation (#1204)
This makes wasm validation parallel (the function part). This makes loading+validating tanks (a 12MB wasm file) 2.3x faster on a 4-core machine (from 3.5 to 1.5 seconds). It's a big speedup because most of loading+validating was actually validating. It's also noticeable during compilation, since we validate by default at the end. 8% faster on -O2 and 23% on -O0. So actually fairly significant on -O0 builds. As a bonus, this PR also moves the code from being 99% in the header to be 1% in the header.
1 parent a9f91b9 commit 1f8d8a5

File tree

4 files changed

+482
-378
lines changed

4 files changed

+482
-378
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ SET(wasm_as_SOURCES
270270
)
271271
ADD_EXECUTABLE(wasm-as
272272
${wasm_as_SOURCES})
273-
TARGET_LINK_LIBRARIES(wasm-as passes wasm asmjs ast cfg support)
273+
TARGET_LINK_LIBRARIES(wasm-as wasm asmjs passes ast cfg support)
274274
SET_PROPERTY(TARGET wasm-as PROPERTY CXX_STANDARD 11)
275275
SET_PROPERTY(TARGET wasm-as PROPERTY CXX_STANDARD_REQUIRED ON)
276276
INSTALL(TARGETS wasm-as DESTINATION ${CMAKE_INSTALL_BINDIR})

src/tools/wasm-reduce.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -414,9 +414,7 @@ struct Reducer : public WalkerPass<PostWalker<Reducer, UnifiedExpressionVisitor<
414414
}
415415
for (auto& func : functions) {
416416
curr->removeFunction(func.name);
417-
WasmValidator validator;
418-
validator.quiet = true;
419-
if (validator.validate(*curr) && writeAndTestReduction()) {
417+
if (WasmValidator().validate(*curr, false, true, true /* override quiet */) && writeAndTestReduction()) {
420418
std::cerr << "| removed function " << func.name << '\n';
421419
noteReduction();
422420
} else {

src/wasm-validator.h

Lines changed: 4 additions & 182 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
// about function B not existing yet, but we would care
3434
// if e.g. inside function A an i32.add receives an i64).
3535
//
36+
// * quiet: Whether to log errors verbosely.
37+
//
3638

3739
#ifndef wasm_wasm_validator_h
3840
#define wasm_wasm_validator_h
@@ -46,188 +48,8 @@
4648

4749
namespace wasm {
4850

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);
23153
};
23254

23355
} // namespace wasm

0 commit comments

Comments
 (0)