Skip to content

Commit 95d8876

Browse files
authored
[NFC] Skip parsing instructions in first parser pass (#8601)
The first parser pass is responsible for two things: finding the locations of definitions of top-level module items like globals and functions and finding the locations of implicit function type definitions. It previously accomplished the latter by fully parsing every instruction in each function. But the IR is not constructed in this phase of parsing, so fully parsing every instruction was largely wasted work. Optimize the parser by parsing only the instructions that might have implicit type definitions and otherwise just blindly match parentheses to skip the function body. Combined with #8597, this speeds up parsing by 30-40%.
1 parent 2fa35d6 commit 95d8876

File tree

4 files changed

+48
-11
lines changed

4 files changed

+48
-11
lines changed

src/parser/context-decls.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
#include "contexts.h"
18+
#include "parsers.h"
1819

1920
namespace wasm::WATParser {
2021

@@ -302,4 +303,36 @@ Result<> ParseDeclsCtx::addTag(Name name,
302303
return Ok{};
303304
}
304305

306+
bool ParseDeclsCtx::skipFunctionBody() {
307+
using namespace std::string_view_literals;
308+
size_t depth = 1;
309+
while (depth > 0 && !in.empty()) {
310+
if (in.takeLParen()) {
311+
++depth;
312+
continue;
313+
}
314+
if (in.takeRParen()) {
315+
--depth;
316+
continue;
317+
}
318+
if (auto kw = in.takeKeyword()) {
319+
if (*kw == "block"sv || *kw == "loop"sv || *kw == "if"sv ||
320+
*kw == "try"sv || *kw == "try_table"sv) {
321+
in.takeID();
322+
(void)typeuse(*this);
323+
continue;
324+
}
325+
if (*kw == "call_indirect"sv || *kw == "return_call_indirect"sv) {
326+
(void)maybeTableidx(*this);
327+
(void)typeuse(*this, false);
328+
continue;
329+
}
330+
continue;
331+
}
332+
in.take(1);
333+
in.advance();
334+
}
335+
return true;
336+
}
337+
305338
} // namespace wasm::WATParser

src/parser/contexts.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,8 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
10741074
recTypeDefs.push_back({{}, pos, Index(recTypeDefs.size()), {}});
10751075
}
10761076

1077+
bool skipFunctionBody();
1078+
10771079
Limits makeLimits(uint64_t n, std::optional<uint64_t> m) {
10781080
return Limits{n, m};
10791081
}

src/parser/lexer.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ struct Lexer {
8282
advance();
8383
}
8484

85+
// Consume the next `n` characters.
86+
void take(size_t n) { pos += n; }
87+
void takeAll() { pos = buffer.size(); }
88+
89+
// Whether the unlexed input starts with prefix `sv`.
90+
size_t startsWith(std::string_view sv) const {
91+
return next().starts_with(sv);
92+
}
93+
8594
std::optional<char> peekChar() const;
8695

8796
bool peekLParen() { return !empty() && peek() == '('; }
@@ -155,15 +164,6 @@ struct Lexer {
155164
}
156165

157166
private:
158-
// Whether the unlexed input starts with prefix `sv`.
159-
size_t startsWith(std::string_view sv) const {
160-
return next().starts_with(sv);
161-
}
162-
163-
// Consume the next `n` characters.
164-
void take(size_t n) { pos += n; }
165-
void takeAll() { pos = buffer.size(); }
166-
167167
std::optional<int> getDigit(char c);
168168

169169
std::optional<int> getHexDigit(char c);

src/parser/parsers.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,6 +3491,7 @@ template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
34913491
typename Ctx::TypeUseT type;
34923492
Exactness exact = Exact;
34933493
std::optional<typename Ctx::LocalsT> localVars;
3494+
bool skipped = false;
34943495

34953496
if (import) {
34963497
auto use = exacttypeuse(ctx);
@@ -3505,13 +3506,14 @@ template<typename Ctx> MaybeResult<> func(Ctx& ctx) {
35053506
CHECK_ERR(l);
35063507
localVars = *l;
35073508
}
3508-
if (!ctx.skipFunctionBody()) {
3509+
skipped = ctx.skipFunctionBody();
3510+
if (!skipped) {
35093511
CHECK_ERR(instrs(ctx));
35103512
ctx.setSrcLoc(ctx.in.takeAnnotations());
35113513
}
35123514
}
35133515

3514-
if (!ctx.skipFunctionBody() && !ctx.in.takeRParen()) {
3516+
if ((import || !skipped) && !ctx.in.takeRParen()) {
35153517
return ctx.in.err("expected end of function");
35163518
}
35173519

0 commit comments

Comments
 (0)