Skip to content

Commit 475841d

Browse files
authored
[Parser][NFC] Split parser into multiple compilation units (WebAssembly#6653)
Because the parser has five stages, it requires instantiating all of the templates in parsers.h with up to five different contexts. Instantiating all those templates in a single compilation unit takes a long time. On my machine, a release build of wat-parser.cpp.o took 32 seconds. To reduce the time of incremental rebuilds on machines with many cores, split the code across several compilation units so that the templates need to be instantiated for just a single context in each unit. On my machine the longest compilation time after this splitting is 17 seconds. The time for a full release build also drops from 42 seconds to 33 seconds. On machines with fewer cores, the benefit may be smaller or even negative, though.
1 parent 425ecc6 commit 475841d

9 files changed

Lines changed: 369 additions & 167 deletions

src/parser/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ set(parser_SOURCES
33
context-decls.cpp
44
context-defs.cpp
55
lexer.cpp
6+
parse-1-decls.cpp
7+
parse-2-typedefs.cpp
8+
parse-3-implicit-types.cpp
9+
parse-4-module-types.cpp
10+
parse-5-defs.cpp
611
wast-parser.cpp
712
wat-parser.cpp
813
${parser_HEADERS}

src/parser/parse-1-decls.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2024 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 "wat-parser-internal.h"
18+
19+
namespace wasm::WATParser {
20+
21+
Result<> parseDecls(ParseDeclsCtx& decls) { return module(decls); }
22+
23+
} // namespace wasm::WATParser

src/parser/parse-2-typedefs.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2024 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 "wat-parser-internal.h"
18+
19+
namespace wasm::WATParser {
20+
21+
Result<> parseTypeDefs(
22+
ParseDeclsCtx& decls,
23+
Lexer& input,
24+
IndexMap& typeIndices,
25+
std::vector<HeapType>& types,
26+
std::unordered_map<HeapType, std::unordered_map<Name, Index>>& typeNames) {
27+
TypeBuilder builder(decls.subtypeDefs.size());
28+
ParseTypeDefsCtx ctx(input, builder, typeIndices);
29+
for (auto& typeDef : decls.typeDefs) {
30+
WithPosition with(ctx, typeDef.pos);
31+
CHECK_ERR(deftype(ctx));
32+
}
33+
auto built = builder.build();
34+
if (auto* err = built.getError()) {
35+
std::stringstream msg;
36+
msg << "invalid type: " << err->reason;
37+
return ctx.in.err(decls.typeDefs[err->index].pos, msg.str());
38+
}
39+
types = *built;
40+
// Record type names on the module and in typeNames.
41+
for (size_t i = 0; i < types.size(); ++i) {
42+
auto& names = ctx.names[i];
43+
auto& fieldNames = names.fieldNames;
44+
if (names.name.is() || fieldNames.size()) {
45+
decls.wasm.typeNames.insert({types[i], names});
46+
auto& fieldIdxMap = typeNames[types[i]];
47+
for (auto [idx, name] : fieldNames) {
48+
fieldIdxMap.insert({name, idx});
49+
}
50+
}
51+
}
52+
return Ok{};
53+
}
54+
55+
} // namespace wasm::WATParser
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2024 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 "wat-parser-internal.h"
18+
19+
namespace wasm::WATParser {
20+
21+
Result<>
22+
parseImplicitTypeDefs(ParseDeclsCtx& decls,
23+
Lexer& input,
24+
IndexMap& typeIndices,
25+
std::vector<HeapType>& types,
26+
std::unordered_map<Index, HeapType>& implicitTypes) {
27+
ParseImplicitTypeDefsCtx ctx(input, types, implicitTypes, typeIndices);
28+
for (Index pos : decls.implicitTypeDefs) {
29+
WithPosition with(ctx, pos);
30+
CHECK_ERR(typeuse(ctx));
31+
}
32+
return Ok{};
33+
}
34+
35+
} // namespace wasm::WATParser
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2024 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 "wat-parser-internal.h"
18+
19+
namespace wasm::WATParser {
20+
21+
Result<> parseModuleTypes(ParseDeclsCtx& decls,
22+
Lexer& input,
23+
IndexMap& typeIndices,
24+
std::vector<HeapType>& types,
25+
std::unordered_map<Index, HeapType>& implicitTypes) {
26+
ParseModuleTypesCtx ctx(input,
27+
decls.wasm,
28+
types,
29+
implicitTypes,
30+
decls.implicitElemIndices,
31+
typeIndices);
32+
CHECK_ERR(parseDefs(ctx, decls.funcDefs, func));
33+
CHECK_ERR(parseDefs(ctx, decls.tableDefs, table));
34+
CHECK_ERR(parseDefs(ctx, decls.memoryDefs, memory));
35+
CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
36+
CHECK_ERR(parseDefs(ctx, decls.elemDefs, elem));
37+
CHECK_ERR(parseDefs(ctx, decls.tagDefs, tag));
38+
return Ok{};
39+
}
40+
41+
} // namespace wasm::WATParser

src/parser/parse-5-defs.cpp

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2024 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 "wat-parser-internal.h"
18+
19+
namespace wasm::WATParser {
20+
21+
Result<> parseDefinitions(
22+
ParseDeclsCtx& decls,
23+
Lexer& input,
24+
IndexMap& typeIndices,
25+
std::vector<HeapType>& types,
26+
std::unordered_map<Index, HeapType>& implicitTypes,
27+
std::unordered_map<HeapType, std::unordered_map<Name, Index>>& typeNames) {
28+
// Parse definitions.
29+
// TODO: Parallelize this.
30+
ParseDefsCtx ctx(input,
31+
decls.wasm,
32+
types,
33+
implicitTypes,
34+
typeNames,
35+
decls.implicitElemIndices,
36+
typeIndices);
37+
CHECK_ERR(parseDefs(ctx, decls.tableDefs, table));
38+
CHECK_ERR(parseDefs(ctx, decls.globalDefs, global));
39+
CHECK_ERR(parseDefs(ctx, decls.startDefs, start));
40+
CHECK_ERR(parseDefs(ctx, decls.elemDefs, elem));
41+
CHECK_ERR(parseDefs(ctx, decls.dataDefs, data));
42+
43+
for (Index i = 0; i < decls.funcDefs.size(); ++i) {
44+
ctx.index = i;
45+
auto* f = decls.wasm.functions[i].get();
46+
WithPosition with(ctx, decls.funcDefs[i].pos);
47+
ctx.setSrcLoc(decls.funcDefs[i].annotations);
48+
if (!f->imported()) {
49+
CHECK_ERR(ctx.visitFunctionStart(f));
50+
}
51+
if (auto parsed = func(ctx)) {
52+
CHECK_ERR(parsed);
53+
} else {
54+
auto im = import_(ctx);
55+
assert(im);
56+
CHECK_ERR(im);
57+
}
58+
if (!f->imported()) {
59+
auto end = ctx.irBuilder.visitEnd();
60+
if (auto* err = end.getErr()) {
61+
return ctx.in.err(decls.funcDefs[i].pos, err->msg);
62+
}
63+
}
64+
}
65+
66+
// Parse exports.
67+
// TODO: It would be more technically correct to interleave these properly
68+
// with the implicit inline exports in other module field definitions.
69+
for (auto pos : decls.exportDefs) {
70+
WithPosition with(ctx, pos);
71+
auto parsed = export_(ctx);
72+
CHECK_ERR(parsed);
73+
assert(parsed);
74+
}
75+
return Ok{};
76+
}
77+
78+
Result<Literal> parseConst(Lexer& lexer) {
79+
Module wasm;
80+
ParseDefsCtx ctx(lexer, wasm, {}, {}, {}, {}, {});
81+
auto inst = foldedinstr(ctx);
82+
CHECK_ERR(inst);
83+
auto expr = ctx.irBuilder.build();
84+
if (auto* err = expr.getErr()) {
85+
return lexer.err(err->msg);
86+
}
87+
auto* e = *expr;
88+
if (!e->is<Const>() && !e->is<RefNull>() && !e->is<RefI31>()) {
89+
return lexer.err("expected constant");
90+
}
91+
lexer = ctx.in;
92+
return getLiteralFromConstExpression(e);
93+
}
94+
95+
} // namespace wasm::WATParser

src/parser/parsers.h

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "common.h"
2121
#include "contexts.h"
2222
#include "lexer.h"
23+
#include "wat-parser-internal.h"
2324

2425
namespace wasm::WATParser {
2526

@@ -350,32 +351,6 @@ template<typename Ctx> MaybeResult<> tag(Ctx&);
350351
template<typename Ctx> MaybeResult<> modulefield(Ctx&);
351352
template<typename Ctx> Result<> module(Ctx&);
352353

353-
// =========
354-
// Utilities
355-
// =========
356-
357-
// RAII utility for temporarily changing the parsing position of a parsing
358-
// context.
359-
template<typename Ctx> struct WithPosition {
360-
Ctx& ctx;
361-
Index original;
362-
std::vector<Annotation> annotations;
363-
364-
WithPosition(Ctx& ctx, Index pos)
365-
: ctx(ctx), original(ctx.in.getPos()),
366-
annotations(ctx.in.takeAnnotations()) {
367-
ctx.in.setPos(pos);
368-
}
369-
370-
~WithPosition() {
371-
ctx.in.setPos(original);
372-
ctx.in.setAnnotations(std::move(annotations));
373-
}
374-
};
375-
376-
// Deduction guide to satisfy -Wctad-maybe-unsupported.
377-
template<typename Ctx> WithPosition(Ctx& ctx, Index) -> WithPosition<Ctx>;
378-
379354
// =====
380355
// Types
381356
// =====
@@ -2699,7 +2674,7 @@ Result<typename Ctx::TypeUseT> typeuse(Ctx& ctx, bool allowNames) {
26992674
}
27002675

27012676
// ('(' 'import' mod:name nm:name ')')?
2702-
MaybeResult<ImportNames> inlineImport(Lexer& in) {
2677+
inline MaybeResult<ImportNames> inlineImport(Lexer& in) {
27032678
if (!in.takeSExprStart("import"sv)) {
27042679
return {};
27052680
}
@@ -2719,7 +2694,7 @@ MaybeResult<ImportNames> inlineImport(Lexer& in) {
27192694
}
27202695

27212696
// ('(' 'export' name ')')*
2722-
Result<std::vector<Name>> inlineExports(Lexer& in) {
2697+
inline Result<std::vector<Name>> inlineExports(Lexer& in) {
27232698
std::vector<Name> exports;
27242699
while (in.takeSExprStart("export"sv)) {
27252700
auto name = in.takeName();

0 commit comments

Comments
 (0)