Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/parser/parse-5-defs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
136 changes: 94 additions & 42 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ template<typename Ctx> Result<> subtype(Ctx&);
template<typename Ctx> MaybeResult<> typedef_(Ctx&);
template<typename Ctx> MaybeResult<> rectype(Ctx&);
template<typename Ctx> MaybeResult<typename Ctx::LocalsT> locals(Ctx&);
template<typename Ctx>
Result<> importdesc(Ctx&, Name, Name, std::optional<Name>);
template<typename Ctx> MaybeResult<> import_(Ctx&);
template<typename Ctx> MaybeResult<> func(Ctx&);
template<typename Ctx> MaybeResult<> table(Ctx&);
Expand Down Expand Up @@ -3436,37 +3438,30 @@ template<typename Ctx> MaybeResult<typename Ctx::LocalsT> 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<typename Ctx> MaybeResult<> import_(Ctx& ctx) {
template<typename Ctx>
Result<> importdesc(Ctx& ctx, Name mod, Name nm, std::optional<Name> 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,
Expand All @@ -3476,46 +3471,103 @@ template<typename Ctx> 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");
}

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<typename Ctx> 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<CompactItem> items;
std::optional<bool> 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");
}
Expand Down
3 changes: 1 addition & 2 deletions src/parser/wat-parser-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
77 changes: 77 additions & 0 deletions test/lit/basic/compact-imports.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • How about adding a case that doesn't have an ID, because the code covers it?
  • When do we print the items in the compact way?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, will add a case with no ID. So far we never print compact imports.


;; 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))
)
)
Loading