diff options
author | Thomas Lively <tlively@google.com> | 2024-06-11 17:18:50 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-12 00:18:50 +0000 |
commit | 475841dd5f0c50d7072f6dfc26dffd66e02abc10 (patch) | |
tree | 62d0b25a347b7dfdcf1225e0ed3a5d8a2e8d3dd8 /src/parser | |
parent | 425ecc65dea1a26c5b3667a46926a9834834b5cc (diff) | |
download | binaryen-475841dd5f0c50d7072f6dfc26dffd66e02abc10.tar.gz binaryen-475841dd5f0c50d7072f6dfc26dffd66e02abc10.tar.bz2 binaryen-475841dd5f0c50d7072f6dfc26dffd66e02abc10.zip |
[Parser][NFC] Split parser into multiple compilation units (#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.
Diffstat (limited to 'src/parser')
-rw-r--r-- | src/parser/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/parser/parse-1-decls.cpp | 23 | ||||
-rw-r--r-- | src/parser/parse-2-typedefs.cpp | 55 | ||||
-rw-r--r-- | src/parser/parse-3-implicit-types.cpp | 35 | ||||
-rw-r--r-- | src/parser/parse-4-module-types.cpp | 41 | ||||
-rw-r--r-- | src/parser/parse-5-defs.cpp | 95 | ||||
-rw-r--r-- | src/parser/parsers.h | 31 | ||||
-rw-r--r-- | src/parser/wat-parser-internal.h | 97 | ||||
-rw-r--r-- | src/parser/wat-parser.cpp | 154 |
9 files changed, 369 insertions, 167 deletions
diff --git a/src/parser/CMakeLists.txt b/src/parser/CMakeLists.txt index 9c54646e7..045948ba1 100644 --- a/src/parser/CMakeLists.txt +++ b/src/parser/CMakeLists.txt @@ -3,6 +3,11 @@ set(parser_SOURCES context-decls.cpp context-defs.cpp lexer.cpp + parse-1-decls.cpp + parse-2-typedefs.cpp + parse-3-implicit-types.cpp + parse-4-module-types.cpp + parse-5-defs.cpp wast-parser.cpp wat-parser.cpp ${parser_HEADERS} diff --git a/src/parser/parse-1-decls.cpp b/src/parser/parse-1-decls.cpp new file mode 100644 index 000000000..1c753cc61 --- /dev/null +++ b/src/parser/parse-1-decls.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2024 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wat-parser-internal.h" + +namespace wasm::WATParser { + +Result<> parseDecls(ParseDeclsCtx& decls) { return module(decls); } + +} // namespace wasm::WATParser diff --git a/src/parser/parse-2-typedefs.cpp b/src/parser/parse-2-typedefs.cpp new file mode 100644 index 000000000..90a84941d --- /dev/null +++ b/src/parser/parse-2-typedefs.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2024 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wat-parser-internal.h" + +namespace wasm::WATParser { + +Result<> parseTypeDefs( + ParseDeclsCtx& decls, + Lexer& input, + IndexMap& typeIndices, + std::vector<HeapType>& types, + std::unordered_map<HeapType, std::unordered_map<Name, Index>>& typeNames) { + TypeBuilder builder(decls.subtypeDefs.size()); + ParseTypeDefsCtx ctx(input, builder, typeIndices); + for (auto& typeDef : decls.typeDefs) { + WithPosition with(ctx, typeDef.pos); + CHECK_ERR(deftype(ctx)); + } + auto built = builder.build(); + if (auto* err = built.getError()) { + std::stringstream msg; + msg << "invalid type: " << err->reason; + return ctx.in.err(decls.typeDefs[err->index].pos, msg.str()); + } + types = *built; + // Record type names on the module and in typeNames. + for (size_t i = 0; i < types.size(); ++i) { + auto& names = ctx.names[i]; + auto& fieldNames = names.fieldNames; + if (names.name.is() || fieldNames.size()) { + decls.wasm.typeNames.insert({types[i], names}); + auto& fieldIdxMap = typeNames[types[i]]; + for (auto [idx, name] : fieldNames) { + fieldIdxMap.insert({name, idx}); + } + } + } + return Ok{}; +} + +} // namespace wasm::WATParser diff --git a/src/parser/parse-3-implicit-types.cpp b/src/parser/parse-3-implicit-types.cpp new file mode 100644 index 000000000..3a3a867e1 --- /dev/null +++ b/src/parser/parse-3-implicit-types.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2024 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wat-parser-internal.h" + +namespace wasm::WATParser { + +Result<> +parseImplicitTypeDefs(ParseDeclsCtx& decls, + Lexer& input, + IndexMap& typeIndices, + std::vector<HeapType>& types, + std::unordered_map<Index, HeapType>& implicitTypes) { + ParseImplicitTypeDefsCtx ctx(input, types, implicitTypes, typeIndices); + for (Index pos : decls.implicitTypeDefs) { + WithPosition with(ctx, pos); + CHECK_ERR(typeuse(ctx)); + } + return Ok{}; +} + +} // namespace wasm::WATParser diff --git a/src/parser/parse-4-module-types.cpp b/src/parser/parse-4-module-types.cpp new file mode 100644 index 000000000..04d8292d0 --- /dev/null +++ b/src/parser/parse-4-module-types.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2024 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wat-parser-internal.h" + +namespace wasm::WATParser { + +Result<> parseModuleTypes(ParseDeclsCtx& decls, + Lexer& input, + IndexMap& typeIndices, + std::vector<HeapType>& types, + std::unordered_map<Index, HeapType>& implicitTypes) { + ParseModuleTypesCtx ctx(input, + decls.wasm, + types, + implicitTypes, + decls.implicitElemIndices, + typeIndices); + CHECK_ERR(parseDefs(ctx, decls.funcDefs, func)); + CHECK_ERR(parseDefs(ctx, decls.tableDefs, table)); + CHECK_ERR(parseDefs(ctx, decls.memoryDefs, memory)); + CHECK_ERR(parseDefs(ctx, decls.globalDefs, global)); + CHECK_ERR(parseDefs(ctx, decls.elemDefs, elem)); + CHECK_ERR(parseDefs(ctx, decls.tagDefs, tag)); + return Ok{}; +} + +} // namespace wasm::WATParser diff --git a/src/parser/parse-5-defs.cpp b/src/parser/parse-5-defs.cpp new file mode 100644 index 000000000..acc81bb75 --- /dev/null +++ b/src/parser/parse-5-defs.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2024 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "wat-parser-internal.h" + +namespace wasm::WATParser { + +Result<> parseDefinitions( + ParseDeclsCtx& decls, + Lexer& input, + IndexMap& typeIndices, + std::vector<HeapType>& types, + std::unordered_map<Index, HeapType>& implicitTypes, + std::unordered_map<HeapType, std::unordered_map<Name, Index>>& typeNames) { + // Parse definitions. + // TODO: Parallelize this. + ParseDefsCtx ctx(input, + decls.wasm, + types, + implicitTypes, + typeNames, + decls.implicitElemIndices, + typeIndices); + CHECK_ERR(parseDefs(ctx, decls.tableDefs, table)); + CHECK_ERR(parseDefs(ctx, decls.globalDefs, global)); + CHECK_ERR(parseDefs(ctx, decls.startDefs, start)); + CHECK_ERR(parseDefs(ctx, decls.elemDefs, elem)); + CHECK_ERR(parseDefs(ctx, decls.dataDefs, data)); + + for (Index i = 0; i < decls.funcDefs.size(); ++i) { + ctx.index = i; + auto* f = decls.wasm.functions[i].get(); + WithPosition with(ctx, decls.funcDefs[i].pos); + ctx.setSrcLoc(decls.funcDefs[i].annotations); + if (!f->imported()) { + CHECK_ERR(ctx.visitFunctionStart(f)); + } + if (auto parsed = func(ctx)) { + CHECK_ERR(parsed); + } else { + auto im = import_(ctx); + assert(im); + CHECK_ERR(im); + } + if (!f->imported()) { + auto end = ctx.irBuilder.visitEnd(); + if (auto* err = end.getErr()) { + return ctx.in.err(decls.funcDefs[i].pos, err->msg); + } + } + } + + // Parse exports. + // TODO: It would be more technically correct to interleave these properly + // with the implicit inline exports in other module field definitions. + for (auto pos : decls.exportDefs) { + WithPosition with(ctx, pos); + auto parsed = export_(ctx); + CHECK_ERR(parsed); + assert(parsed); + } + return Ok{}; +} + +Result<Literal> parseConst(Lexer& lexer) { + Module wasm; + ParseDefsCtx ctx(lexer, wasm, {}, {}, {}, {}, {}); + auto inst = foldedinstr(ctx); + CHECK_ERR(inst); + auto expr = ctx.irBuilder.build(); + if (auto* err = expr.getErr()) { + return lexer.err(err->msg); + } + auto* e = *expr; + if (!e->is<Const>() && !e->is<RefNull>() && !e->is<RefI31>()) { + return lexer.err("expected constant"); + } + lexer = ctx.in; + return getLiteralFromConstExpression(e); +} + +} // namespace wasm::WATParser diff --git a/src/parser/parsers.h b/src/parser/parsers.h index ab990e7b0..c4eaf5fc6 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -20,6 +20,7 @@ #include "common.h" #include "contexts.h" #include "lexer.h" +#include "wat-parser-internal.h" namespace wasm::WATParser { @@ -350,32 +351,6 @@ template<typename Ctx> MaybeResult<> tag(Ctx&); template<typename Ctx> MaybeResult<> modulefield(Ctx&); template<typename Ctx> Result<> module(Ctx&); -// ========= -// Utilities -// ========= - -// RAII utility for temporarily changing the parsing position of a parsing -// context. -template<typename Ctx> struct WithPosition { - Ctx& ctx; - Index original; - std::vector<Annotation> annotations; - - WithPosition(Ctx& ctx, Index pos) - : ctx(ctx), original(ctx.in.getPos()), - annotations(ctx.in.takeAnnotations()) { - ctx.in.setPos(pos); - } - - ~WithPosition() { - ctx.in.setPos(original); - ctx.in.setAnnotations(std::move(annotations)); - } -}; - -// Deduction guide to satisfy -Wctad-maybe-unsupported. -template<typename Ctx> WithPosition(Ctx& ctx, Index) -> WithPosition<Ctx>; - // ===== // Types // ===== @@ -2699,7 +2674,7 @@ Result<typename Ctx::TypeUseT> typeuse(Ctx& ctx, bool allowNames) { } // ('(' 'import' mod:name nm:name ')')? -MaybeResult<ImportNames> inlineImport(Lexer& in) { +inline MaybeResult<ImportNames> inlineImport(Lexer& in) { if (!in.takeSExprStart("import"sv)) { return {}; } @@ -2719,7 +2694,7 @@ MaybeResult<ImportNames> inlineImport(Lexer& in) { } // ('(' 'export' name ')')* -Result<std::vector<Name>> inlineExports(Lexer& in) { +inline Result<std::vector<Name>> inlineExports(Lexer& in) { std::vector<Name> exports; while (in.takeSExprStart("export"sv)) { auto name = in.takeName(); diff --git a/src/parser/wat-parser-internal.h b/src/parser/wat-parser-internal.h new file mode 100644 index 000000000..00c96abd4 --- /dev/null +++ b/src/parser/wat-parser-internal.h @@ -0,0 +1,97 @@ +/* + * Copyright 2024 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "contexts.h" +#include "parsers.h" + +#ifndef parser_wat_parser_internal_h +#define parser_wat_parser_internal_h + +namespace wasm::WATParser { + +Result<> parseDecls(ParseDeclsCtx& decls); + +Result<> parseTypeDefs( + ParseDeclsCtx& decls, + Lexer& input, + IndexMap& typeIndices, + std::vector<HeapType>& types, + std::unordered_map<HeapType, std::unordered_map<Name, Index>>& typeNames); + +Result<> +parseImplicitTypeDefs(ParseDeclsCtx& decls, + Lexer& input, + IndexMap& typeIndices, + std::vector<HeapType>& types, + std::unordered_map<Index, HeapType>& implicitTypes); + +Result<> parseModuleTypes(ParseDeclsCtx& decls, + Lexer& input, + IndexMap& typeIndices, + std::vector<HeapType>& types, + std::unordered_map<Index, HeapType>& implicitTypes); + +Result<> parseDefinitions( + ParseDeclsCtx& decls, + Lexer& input, + IndexMap& typeIndices, + std::vector<HeapType>& types, + std::unordered_map<Index, HeapType>& implicitTypes, + std::unordered_map<HeapType, std::unordered_map<Name, Index>>& typeNames); + +// RAII utility for temporarily changing the parsing position of a parsing +// context. +template<typename Ctx> struct WithPosition { + Ctx& ctx; + Index original; + std::vector<Annotation> annotations; + + WithPosition(Ctx& ctx, Index pos) + : ctx(ctx), original(ctx.in.getPos()), + annotations(ctx.in.takeAnnotations()) { + ctx.in.setPos(pos); + } + + ~WithPosition() { + ctx.in.setPos(original); + ctx.in.setAnnotations(std::move(annotations)); + } +}; + +template<typename Ctx> +Result<> parseDefs(Ctx& ctx, + const std::vector<DefPos>& defs, + MaybeResult<> (*parser)(Ctx&)) { + for (auto& def : defs) { + ctx.index = def.index; + WithPosition with(ctx, def.pos); + if (auto parsed = parser(ctx)) { + CHECK_ERR(parsed); + } else { + auto im = import_(ctx); + assert(im); + CHECK_ERR(im); + } + } + return Ok{}; +} + +// Deduction guide to satisfy -Wctad-maybe-unsupported. +template<typename Ctx> WithPosition(Ctx& ctx, Index) -> WithPosition<Ctx>; + +} // namespace wasm::WATParser + +#endif // parser_wat_parser_internal_h diff --git a/src/parser/wat-parser.cpp b/src/parser/wat-parser.cpp index 4763c69ec..975cbd3c3 100644 --- a/src/parser/wat-parser.cpp +++ b/src/parser/wat-parser.cpp @@ -18,10 +18,10 @@ #include "contexts.h" #include "ir/names.h" #include "lexer.h" -#include "parsers.h" #include "pass.h" #include "wasm-type.h" #include "wasm.h" +#include "wat-parser-internal.h" // The WebAssembly text format is recursive in the sense that elements may be // referred to before they are declared. Furthermore, elements may be referred @@ -73,24 +73,6 @@ Result<IndexMap> createIndexMap(Lexer& in, const std::vector<DefPos>& defs) { return indices; } -template<typename Ctx> -Result<> parseDefs(Ctx& ctx, - const std::vector<DefPos>& defs, - MaybeResult<> (*parser)(Ctx&)) { - for (auto& def : defs) { - ctx.index = def.index; - WithPosition with(ctx, def.pos); - if (auto parsed = parser(ctx)) { - CHECK_ERR(parsed); - } else { - auto im = import_(ctx); - assert(im); - CHECK_ERR(im); - } - } - return Ok{}; -} - void propagateDebugLocations(Module& wasm) { // Copy debug locations from parents or previous siblings to expressions that // do not already have their own debug locations. @@ -101,10 +83,16 @@ void propagateDebugLocations(Module& wasm) { runner.run(); } +// Parse module-level declarations. + +// Parse type definitions. + +// Parse implicit type definitions and map typeuses without explicit types to +// the correct types. + Result<> doParseModule(Module& wasm, Lexer& input, bool allowExtra) { - // Parse module-level declarations. ParseDeclsCtx decls(input, wasm); - CHECK_ERR(module(decls)); + CHECK_ERR(parseDecls(decls)); if (!allowExtra && !decls.in.empty()) { return decls.in.err("Unexpected tokens after module"); } @@ -112,113 +100,18 @@ Result<> doParseModule(Module& wasm, Lexer& input, bool allowExtra) { auto typeIndices = createIndexMap(decls.in, decls.subtypeDefs); CHECK_ERR(typeIndices); - // Parse type definitions. std::vector<HeapType> types; std::unordered_map<HeapType, std::unordered_map<Name, Index>> typeNames; - { - TypeBuilder builder(decls.subtypeDefs.size()); - ParseTypeDefsCtx ctx(input, builder, *typeIndices); - for (auto& typeDef : decls.typeDefs) { - WithPosition with(ctx, typeDef.pos); - CHECK_ERR(deftype(ctx)); - } - auto built = builder.build(); - if (auto* err = built.getError()) { - std::stringstream msg; - msg << "invalid type: " << err->reason; - return ctx.in.err(decls.typeDefs[err->index].pos, msg.str()); - } - types = *built; - // Record type names on the module and in typeNames. - for (size_t i = 0; i < types.size(); ++i) { - auto& names = ctx.names[i]; - auto& fieldNames = names.fieldNames; - if (names.name.is() || fieldNames.size()) { - wasm.typeNames.insert({types[i], names}); - auto& fieldIdxMap = typeNames[types[i]]; - for (auto [idx, name] : fieldNames) { - fieldIdxMap.insert({name, idx}); - } - } - } - } + CHECK_ERR(parseTypeDefs(decls, input, *typeIndices, types, typeNames)); - // Parse implicit type definitions and map typeuses without explicit types to - // the correct types. std::unordered_map<Index, HeapType> implicitTypes; + CHECK_ERR( + parseImplicitTypeDefs(decls, input, *typeIndices, types, implicitTypes)); - { - ParseImplicitTypeDefsCtx ctx(input, types, implicitTypes, *typeIndices); - for (Index pos : decls.implicitTypeDefs) { - WithPosition with(ctx, pos); - CHECK_ERR(typeuse(ctx)); - } - } - - { - // Parse module-level types. - ParseModuleTypesCtx ctx(input, - wasm, - types, - implicitTypes, - decls.implicitElemIndices, - *typeIndices); - CHECK_ERR(parseDefs(ctx, decls.funcDefs, func)); - CHECK_ERR(parseDefs(ctx, decls.tableDefs, table)); - CHECK_ERR(parseDefs(ctx, decls.memoryDefs, memory)); - CHECK_ERR(parseDefs(ctx, decls.globalDefs, global)); - CHECK_ERR(parseDefs(ctx, decls.elemDefs, elem)); - CHECK_ERR(parseDefs(ctx, decls.tagDefs, tag)); - } - { - // Parse definitions. - // TODO: Parallelize this. - ParseDefsCtx ctx(input, - wasm, - types, - implicitTypes, - typeNames, - decls.implicitElemIndices, - *typeIndices); - CHECK_ERR(parseDefs(ctx, decls.tableDefs, table)); - CHECK_ERR(parseDefs(ctx, decls.globalDefs, global)); - CHECK_ERR(parseDefs(ctx, decls.startDefs, start)); - CHECK_ERR(parseDefs(ctx, decls.elemDefs, elem)); - CHECK_ERR(parseDefs(ctx, decls.dataDefs, data)); - - for (Index i = 0; i < decls.funcDefs.size(); ++i) { - ctx.index = i; - auto* f = wasm.functions[i].get(); - WithPosition with(ctx, decls.funcDefs[i].pos); - ctx.setSrcLoc(decls.funcDefs[i].annotations); - if (!f->imported()) { - CHECK_ERR(ctx.visitFunctionStart(f)); - } - if (auto parsed = func(ctx)) { - CHECK_ERR(parsed); - } else { - auto im = import_(ctx); - assert(im); - CHECK_ERR(im); - } - if (!f->imported()) { - auto end = ctx.irBuilder.visitEnd(); - if (auto* err = end.getErr()) { - return ctx.in.err(decls.funcDefs[i].pos, err->msg); - } - } - } + CHECK_ERR(parseModuleTypes(decls, input, *typeIndices, types, implicitTypes)); - // Parse exports. - // TODO: It would be more technically correct to interleave these properly - // with the implicit inline exports in other module field definitions. - for (auto pos : decls.exportDefs) { - WithPosition with(ctx, pos); - auto parsed = export_(ctx); - CHECK_ERR(parsed); - assert(parsed); - } - } + CHECK_ERR(parseDefinitions( + decls, input, *typeIndices, types, implicitTypes, typeNames)); propagateDebugLocations(wasm); input = decls.in; @@ -237,21 +130,4 @@ Result<> parseModule(Module& wasm, Lexer& lexer) { return doParseModule(wasm, lexer, true); } -Result<Literal> parseConst(Lexer& lexer) { - Module wasm; - ParseDefsCtx ctx(lexer, wasm, {}, {}, {}, {}, {}); - auto inst = foldedinstr(ctx); - CHECK_ERR(inst); - auto expr = ctx.irBuilder.build(); - if (auto* err = expr.getErr()) { - return lexer.err(err->msg); - } - auto* e = *expr; - if (!e->is<Const>() && !e->is<RefNull>() && !e->is<RefI31>()) { - return lexer.err("expected constant"); - } - lexer = ctx.in; - return getLiteralFromConstExpression(e); -} - } // namespace wasm::WATParser |