diff --git a/src/parser/parse-5-defs.cpp b/src/parser/parse-5-defs.cpp index 14ad9ffcdd5..31048176c42 100644 --- a/src/parser/parse-5-defs.cpp +++ b/src/parser/parse-5-defs.cpp @@ -50,8 +50,8 @@ Result<> parseDefinitions( CHECK_ERR(ctx.visitFunctionStart(f)); } if (decls.funcDefs[i].kind == DefKind::ImportDesc) { - auto im = import_(ctx); - assert(im); + auto im = importdesc(ctx, Name{}, Name{}, std::nullopt); + assert(!im.getErr()); CHECK_ERR(im); } else { auto parsed = func(ctx); diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 07c7cc8102b..861d2c5cf46 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -393,6 +393,8 @@ template Result<> subtype(Ctx&); template MaybeResult<> typedef_(Ctx&); template MaybeResult<> rectype(Ctx&); template MaybeResult locals(Ctx&); +template +Result<> importdesc(Ctx&, Name, Name, std::optional); template MaybeResult<> import_(Ctx&); template MaybeResult<> func(Ctx&); template MaybeResult<> table(Ctx&); @@ -3436,37 +3438,30 @@ template MaybeResult locals(Ctx& ctx) { return {}; } -// import ::= '(' 'import' mod:name nm:name importdesc ')' // importdesc ::= '(' 'func' id? exacttypeuse ')' // | '(' 'table' id? tabletype ')' // | '(' 'memory' id? memtype ')' // | '(' 'global' id? globaltype ')' // | '(' 'tag' id? typeuse ')' -template MaybeResult<> import_(Ctx& ctx) { +template +Result<> importdesc(Ctx& ctx, Name mod, Name nm, std::optional id) { auto pos = ctx.in.getPos(); - - if (!ctx.in.takeSExprStart("import"sv)) { - return {}; - } - - auto mod = ctx.in.takeName(); - if (!mod) { - return ctx.in.err("expected import module name"); - } - - auto nm = ctx.in.takeName(); - if (!nm) { - return ctx.in.err("expected import name"); - } - ImportNames names{*mod, *nm}; - + // We only try to parse an ID if `id` is nullopt. + auto getID = [&]() { + if (id) { + return *id; + } + auto parsed = ctx.in.takeID(); + return parsed ? *parsed : Name{}; + }; + ImportNames names{mod, nm}; if (ctx.in.takeSExprStart("func"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto use = exacttypeuse(ctx); CHECK_ERR(use); auto [type, exact] = *use; // TODO: function import annotations - CHECK_ERR(ctx.addFunc(name ? *name : Name{}, + CHECK_ERR(ctx.addFunc(name, {}, &names, type, @@ -3476,39 +3471,27 @@ template MaybeResult<> import_(Ctx& ctx) { pos, DefKind::ImportDesc)); } else if (ctx.in.takeSExprStart("table"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto type = tabletype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addTable(name ? *name : Name{}, - {}, - &names, - *type, - std::nullopt, - pos, - DefKind::ImportDesc)); + CHECK_ERR(ctx.addTable( + name, {}, &names, *type, std::nullopt, pos, DefKind::ImportDesc)); } else if (ctx.in.takeSExprStart("memory"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto type = memtype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addMemory( - name ? *name : Name{}, {}, &names, *type, pos, DefKind::ImportDesc)); + CHECK_ERR(ctx.addMemory(name, {}, &names, *type, pos, DefKind::ImportDesc)); } else if (ctx.in.takeSExprStart("global"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto type = globaltype(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addGlobal(name ? *name : Name{}, - {}, - &names, - *type, - std::nullopt, - pos, - DefKind::ImportDesc)); + CHECK_ERR(ctx.addGlobal( + name, {}, &names, *type, std::nullopt, pos, DefKind::ImportDesc)); } else if (ctx.in.takeSExprStart("tag"sv)) { - auto name = ctx.in.takeID(); + auto name = getID(); auto type = typeuse(ctx); CHECK_ERR(type); - CHECK_ERR(ctx.addTag( - name ? *name : Name{}, {}, &names, *type, pos, DefKind::ImportDesc)); + CHECK_ERR(ctx.addTag(name, {}, &names, *type, pos, DefKind::ImportDesc)); } else { return ctx.in.err("expected import description"); } @@ -3516,6 +3499,75 @@ template MaybeResult<> import_(Ctx& ctx) { if (!ctx.in.takeRParen()) { return ctx.in.err("expected end of import description"); } + return Ok{}; +} + +// import ::= '(' 'import' mod:name nm:name importdesc ')' +// | '(' 'import' mod:name (item id? nm:name importdesc)+ ')' +// | '(' 'import' mod:name (item id? nm:name)+ importdesc ')' +template MaybeResult<> import_(Ctx& ctx) { + if (!ctx.in.takeSExprStart("import"sv)) { + return {}; + } + + auto mod = ctx.in.takeName(); + if (!mod) { + return ctx.in.err("expected import module name"); + } + + if (auto nm = ctx.in.takeName()) { + CHECK_ERR(importdesc(ctx, *mod, *nm, std::nullopt)); + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of import"); + } + return Ok{}; + } + + struct CompactItem { + Name id; + Name name; + }; + std::vector items; + std::optional hasSharedImportDesc; + while (ctx.in.takeSExprStart("item"sv)) { + auto id = ctx.in.takeID(); + if (!id) { + // Do not allow parsing an id in the importdesc. + id = Name{}; + } + + auto nm = ctx.in.takeName(); + if (!nm) { + return ctx.in.err("expected import name"); + } + + if (!hasSharedImportDesc.has_value()) { + hasSharedImportDesc = ctx.in.peekRParen(); + } + + if (*hasSharedImportDesc) { + items.emplace_back(*id, *nm); + } else { + CHECK_ERR(importdesc(ctx, *mod, *nm, id)) + } + + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of import item"); + } + } + if (!hasSharedImportDesc.has_value()) { + // There were no items. + return ctx.in.err("expected import name or item"); + } + + if (*hasSharedImportDesc) { + auto pos = ctx.in.getPos(); + for (auto item : items) { + ctx.in.setPos(pos); + CHECK_ERR(importdesc(ctx, *mod, item.name, item.id)); + } + } + if (!ctx.in.takeRParen()) { return ctx.in.err("expected end of import"); } diff --git a/src/parser/wat-parser-internal.h b/src/parser/wat-parser-internal.h index 9219b62012c..33528701be5 100644 --- a/src/parser/wat-parser-internal.h +++ b/src/parser/wat-parser-internal.h @@ -82,8 +82,7 @@ Result<> parseDefs(Ctx& ctx, WithPosition with(ctx, def.pos); ctx.in.setAnnotations(def.annotations); if (def.kind == DefKind::ImportDesc) { - auto im = import_(ctx); - assert(im); + auto im = importdesc(ctx, Name{}, Name{}, std::nullopt); CHECK_ERR(im); } else { auto parsed = parser(ctx); diff --git a/test/lit/basic/compact-imports.wast b/test/lit/basic/compact-imports.wast new file mode 100644 index 00000000000..89fd1003068 --- /dev/null +++ b/test/lit/basic/compact-imports.wast @@ -0,0 +1,77 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; RUN: wasm-opt %s -all -S -o - | filecheck %s + +(module + ;; CHECK: (type $sig1 (func (param i32) (result i32))) + (type $sig1 (func (param i32) (result i32))) + + ;; Compact Encoding 1: per-item import descriptions + (import "env" + (item $f1 "f1" (func (type $sig1))) + (item $f2 "f2" (func (exact (param i32) (result i32)))) + (item $g1 "g1" (global i32)) + (item $t1 "t1" (table 1 10 funcref)) + (item $m1 "m1" (memory 1 2)) + ) + + ;; Compact Encoding 2: shared import description + (import "math" + (item $sin "sin") + (item $cos "cos") + (item $tan "tan") + (func (param i32) (result i32)) + ) + + ;; Same, but with an exact function type. + (import "math" + (item $sinh "sinh") + (item $cosh "cosh") + (item $tanh "tanh") + (func (exact (param i32) (result i32))) + ) + + ;; Compact Encoding 2: shared global import description + (import "constants" + (item $pi "pi") + (item $e "e") + (global f64) + ) + + ;; CHECK: (type $1 (func (result i32))) + + ;; CHECK: (import "env" "m1" (memory $m1 1 2)) + + ;; CHECK: (import "env" "t1" (table $t1 1 10 funcref)) + + ;; CHECK: (import "env" "g1" (global $g1 i32)) + + ;; CHECK: (import "constants" "pi" (global $pi f64)) + + ;; CHECK: (import "constants" "e" (global $e f64)) + + ;; CHECK: (import "env" "f1" (func $f1 (type $sig1) (param i32) (result i32))) + + ;; CHECK: (import "env" "f2" (func $f2 (exact (type $sig1) (param i32) (result i32)))) + + ;; CHECK: (import "math" "sin" (func $sin (type $sig1) (param i32) (result i32))) + + ;; CHECK: (import "math" "cos" (func $cos (type $sig1) (param i32) (result i32))) + + ;; CHECK: (import "math" "tan" (func $tan (type $sig1) (param i32) (result i32))) + + ;; CHECK: (import "math" "sinh" (func $sinh (exact (type $sig1) (param i32) (result i32)))) + + ;; CHECK: (import "math" "cosh" (func $cosh (exact (type $sig1) (param i32) (result i32)))) + + ;; CHECK: (import "math" "tanh" (func $tanh (exact (type $sig1) (param i32) (result i32)))) + + ;; CHECK: (func $main (type $1) (result i32) + ;; CHECK-NEXT: (call $f1 + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $main (result i32) + (call $f1 (i32.const 1)) + ) +)