summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/wasm/wat-parser.cpp414
-rw-r--r--src/wat-lexer.h3
-rw-r--r--test/lit/wat-kitchen-sink.wast108
3 files changed, 468 insertions, 57 deletions
diff --git a/src/wasm/wat-parser.cpp b/src/wasm/wat-parser.cpp
index 5455c0438..843e26d08 100644
--- a/src/wasm/wat-parser.cpp
+++ b/src/wasm/wat-parser.cpp
@@ -38,15 +38,14 @@
// other module elements that can be referred to by name before their
// definitions have been parsed.
//
-// The third phase, not yet implemented, further parses and constructs types
-// implicitly defined by type uses in functions, blocks, and call_indirect
-// instructions. These implicitly defined types may be referred to by index
-// elsewhere.
+// The third phase further parses and constructs types implicitly defined by
+// type uses in functions, blocks, and call_indirect instructions. These
+// implicitly defined types may be referred to by index elsewhere.
//
-// The fourth phase, not yet implemented, parses and sets the types of globals,
-// functions, and other top-level module elements. These types need to be set
-// before we parse instructions because they determine the types of instructions
-// such as global.get and ref.func.
+// The fourth phase parses and sets the types of globals, functions, and other
+// top-level module elements. These types need to be set before we parse
+// instructions because they determine the types of instructions such as
+// global.get and ref.func.
//
// The fifth and final phase parses the remaining contents of all module
// elements, including instructions.
@@ -282,6 +281,13 @@ struct GlobalType {
Type type;
};
+// A signature type and parameter names (possibly empty), used for parsing
+// function types.
+struct TypeUse {
+ HeapType type;
+ std::vector<Name> names;
+};
+
struct ImportNames {
Name mod;
Name nm;
@@ -366,6 +372,8 @@ struct NullTypeParserCtx {
using StructT = Ok;
using ArrayT = Ok;
using GlobalTypeT = Ok;
+ using TypeUseT = Ok;
+ using LocalsT = Ok;
HeapTypeT makeFunc() { return Ok{}; }
HeapTypeT makeAny() { return Ok{}; }
@@ -405,6 +413,9 @@ struct NullTypeParserCtx {
GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; }
+ LocalsT makeLocals() { return Ok{}; }
+ void appendLocal(LocalsT&, Name, TypeT) {}
+
Result<Index> getTypeIndex(Name, ParseInput&) { return 1; }
Result<HeapTypeT> getHeapTypeFromIdx(Index, ParseInput&) { return Ok{}; }
};
@@ -421,6 +432,7 @@ template<typename Ctx> struct TypeParserCtx {
using FieldsT = std::pair<std::vector<Name>, std::vector<Field>>;
using StructT = std::pair<std::vector<Name>, Struct>;
using ArrayT = Array;
+ using LocalsT = std::vector<NameType>;
// Map heap type names to their indices.
const IndexMap& typeIndices;
@@ -492,6 +504,11 @@ template<typename Ctx> struct TypeParserCtx {
return {};
}
+ LocalsT makeLocals() { return {}; }
+ void appendLocal(LocalsT& locals, Name id, TypeT type) {
+ locals.push_back({id, type});
+ }
+
Result<Index> getTypeIndex(Name id, ParseInput& in) {
auto it = typeIndices.find(id);
if (it == typeIndices.end()) {
@@ -573,10 +590,15 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
// The module element definitions we are parsing in this phase.
std::vector<DefPos> typeDefs;
std::vector<DefPos> subtypeDefs;
+ std::vector<DefPos> funcDefs;
std::vector<DefPos> globalDefs;
+ // Positions of typeuses that might implicitly define new types.
+ std::vector<Index> implicitTypeDefs;
+
// Counters used for generating names for module elements.
int globalCounter = 0;
+ int funcCounter = 0;
// Used to verify that all imports come before all non-imports.
bool hasNonImport = false;
@@ -594,6 +616,57 @@ struct ParseDeclsCtx : NullTypeParserCtx, NullInstrParserCtx {
void addRecGroup(Index, size_t) {}
void finishDeftype(Index pos) { typeDefs.push_back({{}, pos}); }
+ Result<TypeUseT> makeTypeUse(Index pos,
+ std::optional<HeapTypeT> type,
+ ParamsT*,
+ ResultsT*,
+ ParseInput&) {
+ if (!type) {
+ implicitTypeDefs.push_back(pos);
+ }
+ return Ok{};
+ }
+
+ Result<Function*> addFuncDecl(ParseInput& in,
+ Name name,
+ std::optional<ImportNames> importNames) {
+ auto f = std::make_unique<Function>();
+ if (name.is()) {
+ if (wasm.getFunctionOrNull(name)) {
+ // TDOO: if the existing function is not explicitly named, fix its name
+ // and continue.
+ // TODO: Fix error location to point to name.
+ return in.err("repeated function name");
+ }
+ f->setExplicitName(name);
+ } else {
+ name = (importNames ? "fimport$" : "") + std::to_string(funcCounter++);
+ name = Names::getValidFunctionName(wasm, name);
+ f->name = name;
+ }
+ applyImportNames(*f, importNames);
+ return wasm.addFunction(std::move(f));
+ }
+
+ Result<> addFunc(Name name,
+ const std::vector<Name>& exports,
+ ImportNames* import,
+ TypeUseT type,
+ std::optional<LocalsT>,
+ std::optional<InstrsT>,
+ Index pos,
+ ParseInput& in) {
+ if (import && hasNonImport) {
+ return in.err("import after non-import");
+ }
+ auto imp = import ? std::make_optional(*import) : std::nullopt;
+ auto f = addFuncDecl(in, name, imp);
+ CHECK_ERR(f);
+ CHECK_ERR(addExports(in, wasm, *f, exports, ExternalKind::Function));
+ funcDefs.push_back({name, pos});
+ return Ok{};
+ }
+
Result<Global*> addGlobalDecl(ParseInput& in,
Name name,
std::optional<ImportNames> importNames) {
@@ -697,7 +770,65 @@ struct ParseTypeDefsCtx : TypeParserCtx<ParseTypeDefsCtx> {
void finishDeftype(Index) {}
};
-// TODO: Phase 3: ParseImplicitTypeDefsCtx
+// Phase 3: Parse type uses to find implicitly defined types.
+struct ParseImplicitTypeDefsCtx : TypeParserCtx<ParseImplicitTypeDefsCtx> {
+ using TypeUseT = Ok;
+
+ // Types parsed so far.
+ std::vector<HeapType>& types;
+
+ // Map typeuse positions without an explicit type to the correct type.
+ std::unordered_map<Index, HeapType>& implicitTypes;
+
+ // Map signatures to the first defined heap type they match.
+ std::unordered_map<Signature, HeapType> sigTypes;
+
+ ParseImplicitTypeDefsCtx(std::vector<HeapType>& types,
+ std::unordered_map<Index, HeapType>& implicitTypes,
+ const IndexMap& typeIndices)
+ : TypeParserCtx<ParseImplicitTypeDefsCtx>(typeIndices), types(types),
+ implicitTypes(implicitTypes) {
+ for (auto type : types) {
+ if (type.isSignature() && type.getRecGroup().size() == 1) {
+ sigTypes.insert({type.getSignature(), type});
+ }
+ }
+ }
+
+ Result<HeapTypeT> getHeapTypeFromIdx(Index idx, ParseInput& in) {
+ if (idx >= types.size()) {
+ return in.err("type index out of bounds");
+ }
+ return types[idx];
+ }
+
+ Result<TypeUseT> makeTypeUse(Index pos,
+ std::optional<HeapTypeT>,
+ ParamsT* params,
+ ResultsT* results,
+ ParseInput& in) {
+ std::vector<Type> paramTypes;
+ if (params) {
+ paramTypes = getUnnamedTypes(*params);
+ }
+
+ std::vector<Type> resultTypes;
+ if (results) {
+ resultTypes = *results;
+ }
+
+ auto sig = Signature(Type(paramTypes), Type(resultTypes));
+ auto [it, inserted] = sigTypes.insert({sig, HeapType::func});
+ if (inserted) {
+ auto type = HeapType(sig);
+ it->second = type;
+ types.push_back(type);
+ }
+ implicitTypes.insert({pos, it->second});
+
+ return Ok{};
+ }
+};
// Phase 4: Parse and set the types of module elements.
struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
@@ -706,19 +837,22 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
// validate them when they are used.
using GlobalTypeT = GlobalType;
+ using TypeUseT = TypeUse;
Module& wasm;
const std::vector<HeapType>& types;
+ const std::unordered_map<Index, HeapType>& implicitTypes;
// The index of the current type.
Index index = 0;
ParseModuleTypesCtx(Module& wasm,
const std::vector<HeapType>& types,
+ const std::unordered_map<Index, HeapType>& implicitTypes,
const IndexMap& typeIndices)
- : TypeParserCtx<ParseModuleTypesCtx>(typeIndices), wasm(wasm),
- types(types) {}
+ : TypeParserCtx<ParseModuleTypesCtx>(typeIndices), wasm(wasm), types(types),
+ implicitTypes(implicitTypes) {}
Result<HeapTypeT> getHeapTypeFromIdx(Index idx, ParseInput& in) {
if (idx >= types.size()) {
@@ -727,10 +861,57 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
return types[idx];
}
+ Result<TypeUseT> makeTypeUse(Index pos,
+ std::optional<HeapTypeT> type,
+ ParamsT* params,
+ ResultsT* results,
+ ParseInput& in) {
+ std::vector<Name> ids;
+ if (params) {
+ ids.reserve(params->size());
+ for (auto& p : *params) {
+ ids.push_back(p.name);
+ }
+ }
+
+ if (type) {
+ return TypeUse{*type, ids};
+ }
+
+ auto it = implicitTypes.find(pos);
+ assert(it != implicitTypes.end());
+
+ return TypeUse{it->second, ids};
+ }
+
GlobalTypeT makeGlobalType(Mutability mutability, TypeT type) {
return {mutability, type};
}
+ Result<> addFunc(Name name,
+ const std::vector<Name>&,
+ ImportNames*,
+ TypeUseT type,
+ std::optional<LocalsT> locals,
+ std::optional<InstrsT>,
+ Index,
+ ParseInput&) {
+ auto& f = wasm.functions[index];
+ f->type = type.type;
+ for (Index i = 0; i < type.names.size(); ++i) {
+ if (type.names[i].is()) {
+ f->setLocalName(i, type.names[i]);
+ }
+ }
+ if (locals) {
+ for (auto& l : *locals) {
+ Builder::addVar(f.get(), l.name, l.type);
+ }
+ }
+ // TODO: local types and names.
+ return Ok{};
+ }
+
Result<> addGlobal(Name,
const std::vector<Name>&,
ImportNames*,
@@ -748,6 +929,7 @@ struct ParseModuleTypesCtx : TypeParserCtx<ParseModuleTypesCtx>,
// Phase 5: Parse module element definitions, including instructions.
struct ParseDefsCtx : InstrParserCtx<ParseDefsCtx> {
using GlobalTypeT = Ok;
+ using TypeUseT = HeapType;
using InstrT = Expression*;
using InstrsT = std::vector<Expression*>;
@@ -759,15 +941,17 @@ struct ParseDefsCtx : InstrParserCtx<ParseDefsCtx> {
std::vector<std::vector<Expression*>> instrStack;
const std::vector<HeapType>& types;
+ const std::unordered_map<Index, HeapType>& implicitTypes;
// The index of the current module element.
Index index = 0;
ParseDefsCtx(Module& wasm,
const std::vector<HeapType>& types,
+ const std::unordered_map<Index, HeapType>& implicitTypes,
const IndexMap& typeIndices)
- : InstrParserCtx<ParseDefsCtx>(wasm, typeIndices), wasm(wasm),
- types(types) {}
+ : InstrParserCtx<ParseDefsCtx>(wasm, typeIndices), wasm(wasm), types(types),
+ implicitTypes(implicitTypes) {}
GlobalTypeT makeGlobalType(Mutability, TypeT) { return Ok{}; }
@@ -778,6 +962,66 @@ struct ParseDefsCtx : InstrParserCtx<ParseDefsCtx> {
return types[idx];
}
+ Result<TypeUseT> makeTypeUse(Index pos,
+ std::optional<HeapTypeT> type,
+ ParamsT* params,
+ ResultsT* results,
+ ParseInput& in) {
+ if (type && (params || results)) {
+ std::vector<Type> paramTypes;
+ if (params) {
+ paramTypes = getUnnamedTypes(*params);
+ }
+
+ std::vector<Type> resultTypes;
+ if (results) {
+ resultTypes = *results;
+ }
+
+ auto sig = Signature(Type(paramTypes), Type(resultTypes));
+
+ if (!type->isSignature() || type->getSignature() != sig) {
+ return in.err("type does not match provided signature");
+ }
+ }
+
+ if (type) {
+ return *type;
+ }
+
+ auto it = implicitTypes.find(pos);
+ assert(it != implicitTypes.end());
+ return it->second;
+ }
+
+ Result<> addFunc(Name,
+ const std::vector<Name>&,
+ ImportNames*,
+ TypeUseT,
+ std::optional<LocalsT>,
+ std::optional<InstrsT> insts,
+ Index,
+ ParseInput&) {
+ Expression* body;
+ if (insts) {
+ switch (insts->size()) {
+ case 0:
+ body = builder.makeNop();
+ break;
+ case 1:
+ body = insts->back();
+ break;
+ default:
+ body = builder.makeBlock(*insts, wasm.functions[index]->getResults());
+ break;
+ }
+ } else {
+ body = builder.makeNop();
+ }
+ wasm.functions[index]->body = body;
+ return Ok{};
+ }
+
Result<> addGlobal(Name,
const std::vector<Name>&,
ImportNames*,
@@ -1026,12 +1270,17 @@ template<typename Ctx>
MaybeResult<Index> maybeTypeidx(Ctx& ctx, ParseInput& in);
template<typename Ctx>
Result<typename Ctx::HeapTypeT> typeidx(Ctx&, ParseInput&);
+template<typename Ctx>
+Result<typename Ctx::TypeUseT> typeuse(Ctx&, ParseInput&);
MaybeResult<ImportNames> inlineImport(ParseInput&);
Result<std::vector<Name>> inlineExports(ParseInput&);
template<typename Ctx> Result<> strtype(Ctx&, ParseInput&);
template<typename Ctx>
MaybeResult<typename Ctx::ModuleNameT> subtype(Ctx&, ParseInput&);
template<typename Ctx> MaybeResult<> deftype(Ctx&, ParseInput&);
+template<typename Ctx>
+MaybeResult<typename Ctx::LocalsT> locals(Ctx&, ParseInput&);
+template<typename Ctx> MaybeResult<> func(Ctx&, ParseInput&);
template<typename Ctx> MaybeResult<> global(Ctx&, ParseInput&);
MaybeResult<> modulefield(ParseDeclsCtx&, ParseInput&);
Result<> module(ParseDeclsCtx&, ParseInput&);
@@ -1920,6 +2169,38 @@ Result<typename Ctx::HeapTypeT> typeidx(Ctx& ctx, ParseInput& in) {
return in.err("expected type index or identifier");
}
+// typeuse ::= '(' 'type' x:typeidx ')' => x, []
+// (if typedefs[x] = [t1*] -> [t2*]
+// | '(' 'type' x:typeidx ')' ((t1,IDs):param)* (t2:result)* => x, IDs
+// (if typedefs[x] = [t1*] -> [t2*])
+// | ((t1,IDs):param)* (t2:result)* => x, IDs
+// (if x is minimum s.t. typedefs[x] = [t1*] -> [t2*])
+template<typename Ctx>
+Result<typename Ctx::TypeUseT> typeuse(Ctx& ctx, ParseInput& in) {
+ auto pos = in.getPos();
+ std::optional<typename Ctx::HeapTypeT> type;
+ if (in.takeSExprStart("type"sv)) {
+ auto x = typeidx(ctx, in);
+ CHECK_ERR(x);
+
+ if (!in.takeRParen()) {
+ return in.err("expected end of type use");
+ }
+
+ type = *x;
+ }
+
+ auto namedParams = params(ctx, in);
+ CHECK_ERR(namedParams);
+
+ auto resultTypes = results(ctx, in);
+ CHECK_ERR(resultTypes);
+
+ // TODO: Use `pos` for error reporting rather than `in`.
+ return ctx.makeTypeUse(
+ pos, type, namedParams.getPtr(), resultTypes.getPtr(), in);
+}
+
// ('(' 'import' mod:name nm:name ')')?
MaybeResult<ImportNames> inlineImport(ParseInput& in) {
if (!in.takeSExprStart("import"sv)) {
@@ -2041,14 +2322,96 @@ template<typename Ctx> MaybeResult<> deftype(Ctx& ctx, ParseInput& in) {
return Ok{};
}
+// local ::= '(' 'local id? t:valtype ')' => [t]
+// | '(' 'local t*:valtype* ')' => [t*]
+// locals ::= local*
+template<typename Ctx>
+MaybeResult<typename Ctx::LocalsT> locals(Ctx& ctx, ParseInput& in) {
+ bool hasAny = false;
+ auto res = ctx.makeLocals();
+ while (in.takeSExprStart("local"sv)) {
+ hasAny = true;
+ if (auto id = in.takeID()) {
+ // Single named local
+ auto type = valtype(ctx, in);
+ CHECK_ERR(type);
+ if (!in.takeRParen()) {
+ return in.err("expected end of local");
+ }
+ ctx.appendLocal(res, *id, *type);
+ } else {
+ // Repeated unnamed locals
+ while (!in.takeRParen()) {
+ auto type = valtype(ctx, in);
+ CHECK_ERR(type);
+ ctx.appendLocal(res, {}, *type);
+ }
+ }
+ }
+ if (hasAny) {
+ return res;
+ }
+ return {};
+}
+
+// func ::= '(' 'func' id? ('(' 'export' name ')')*
+// x,I:typeuse t*:vec(local) (in:instr)* ')'
+// | '(' 'func' id? ('(' 'export' name ')')*
+// '(' 'import' mode:name nm:name ')' typeuse ')'
+template<typename Ctx> MaybeResult<> func(Ctx& ctx, ParseInput& in) {
+ auto pos = in.getPos();
+ if (!in.takeSExprStart("func"sv)) {
+ return {};
+ }
+
+ Name name;
+ if (auto id = in.takeID()) {
+ name = *id;
+ }
+
+ auto exports = inlineExports(in);
+ CHECK_ERR(exports);
+
+ auto import = inlineImport(in);
+ CHECK_ERR(import);
+
+ auto type = typeuse(ctx, in);
+ CHECK_ERR(type);
+
+ std::optional<typename Ctx::LocalsT> localVars;
+ if (!import) {
+ if (auto l = locals(ctx, in)) {
+ CHECK_ERR(l);
+ localVars = *l;
+ }
+ }
+
+ std::optional<typename Ctx::InstrsT> insts;
+ if (!import) {
+ auto i = instrs(ctx, in);
+ CHECK_ERR(i);
+ insts = *i;
+ }
+
+ if (!in.takeRParen()) {
+ return in.err("expected end of function");
+ }
+
+ // TODO: Use `pos` instead of `in` for error position.
+ CHECK_ERR(ctx.addFunc(
+ name, *exports, import.getPtr(), *type, localVars, insts, pos, in));
+ return Ok{};
+}
+
// global ::= '(' 'global' id? ('(' 'export' name ')')* gt:globaltype e:expr ')'
-// | '(' 'global' id? '(' 'import' mod:name nm:name ')'
-// gt:globaltype ')'
+// | '(' 'global' id? ('(' 'export' name ')')*
+// '(' 'import' mod:name nm:name ')' gt:globaltype ')'
template<typename Ctx> MaybeResult<> global(Ctx& ctx, ParseInput& in) {
auto pos = in.getPos();
if (!in.takeSExprStart("global"sv)) {
return {};
}
+
Name name;
if (auto id = in.takeID()) {
name = *id;
@@ -2098,6 +2461,10 @@ MaybeResult<> modulefield(ParseDeclsCtx& ctx, ParseInput& in) {
CHECK_ERR(res);
return Ok{};
}
+ if (auto res = func(ctx, in)) {
+ CHECK_ERR(res);
+ return Ok{};
+ }
if (auto res = global(ctx, in)) {
CHECK_ERR(res);
return Ok{};
@@ -2168,19 +2535,30 @@ Result<> parseModule(Module& wasm, std::string_view input) {
}
}
- // TODO: Parse implicit type definitions.
+ // Parse implicit type definitions and map typeuses without explicit types to
+ // the correct types.
+ std::unordered_map<Index, HeapType> implicitTypes;
+ {
+ ParseImplicitTypeDefsCtx ctx(types, implicitTypes, *typeIndices);
+ for (Index pos : decls.implicitTypeDefs) {
+ ParseInput in(input, pos);
+ CHECK_ERR(typeuse(ctx, in));
+ }
+ }
{
// Parse module-level types.
- ParseModuleTypesCtx ctx(wasm, types, *typeIndices);
+ ParseModuleTypesCtx ctx(wasm, types, implicitTypes, *typeIndices);
CHECK_ERR(parseDefs(ctx, input, decls.globalDefs, global));
+ CHECK_ERR(parseDefs(ctx, input, decls.funcDefs, func));
// TODO: Parse types of other module elements.
}
{
// Parse definitions.
// TODO: Parallelize this.
- ParseDefsCtx ctx(wasm, types, *typeIndices);
+ ParseDefsCtx ctx(wasm, types, implicitTypes, *typeIndices);
CHECK_ERR(parseDefs(ctx, input, decls.globalDefs, global));
+ CHECK_ERR(parseDefs(ctx, input, decls.funcDefs, func));
}
return Ok{};
diff --git a/src/wat-lexer.h b/src/wat-lexer.h
index b41cbd8c1..d73c11faf 100644
--- a/src/wat-lexer.h
+++ b/src/wat-lexer.h
@@ -172,14 +172,13 @@ public:
index = i;
skipSpace();
lexToken();
- skipSpace();
}
std::string_view next() const { return buffer.substr(index); }
Lexer& operator++() {
// Preincrement
- lexToken();
skipSpace();
+ lexToken();
return *this;
}
diff --git a/test/lit/wat-kitchen-sink.wast b/test/lit/wat-kitchen-sink.wast
index ddb974ed6..09c983c5d 100644
--- a/test/lit/wat-kitchen-sink.wast
+++ b/test/lit/wat-kitchen-sink.wast
@@ -5,7 +5,11 @@
(module $parse
;; types
(rec
- ;; CHECK: (rec
+ ;; CHECK: (type $void (func_subtype func))
+
+ ;; CHECK: (type $i32_=>_none (func_subtype (param i32) func))
+
+ ;; CHECK: (rec
;; CHECK-NEXT: (type $s0 (struct_subtype data))
(type $s0 (sub (struct)))
;; CHECK: (type $s1 (struct_subtype data))
@@ -14,6 +18,10 @@
(rec)
+ ;; CHECK: (type $many (func_subtype (param i32 i64 f32 f64) (result anyref (ref func)) func))
+
+ ;; CHECK: (type $none_=>_i32 (func_subtype (result i32) func))
+
;; CHECK: (type $s2 (struct_subtype (field i32) data))
(type $s2 (struct i32))
;; CHECK: (type $s3 (struct_subtype (field i64) data))
@@ -39,14 +47,12 @@
(type $a3 (array (field $x (mut f64))))
(rec
- ;; CHECK: (type $void (func_subtype func))
(type $void (func))
)
;; CHECK: (type $subvoid (func_subtype $void))
(type $subvoid (sub $void (func)))
- ;; CHECK: (type $many (func_subtype (param i32 i64 f32 f64) (result anyref (ref func)) func))
(type $many (func (param $x i32) (param i64 f32) (param) (param $y f64)
(result anyref (ref func))))
@@ -60,6 +66,8 @@
(global (import "mod" "") (ref null $many))
(global i32 i32.const 0)
+ ;; CHECK: (type $ref|$s0|_ref|$s1|_ref|$s2|_ref|$s3|_ref|$s4|_ref|$s5|_ref|$s6|_ref|$s7|_ref|$s8|_ref|$a0|_ref|$a1|_ref|$a2|_ref|$a3|_ref|$subvoid|_ref|$submany|_=>_none (func_subtype (param (ref $s0) (ref $s1) (ref $s2) (ref $s3) (ref $s4) (ref $s5) (ref $s6) (ref $s7) (ref $s8) (ref $a0) (ref $a1) (ref $a2) (ref $a3) (ref $subvoid) (ref $submany)) func))
+
;; CHECK: (import "mod" "g1" (global $g1 i32))
;; CHECK: (import "mod" "g2" (global $g2 (mut i64)))
@@ -68,43 +76,69 @@
;; CHECK: (import "mod" "" (global $gimport$1 (ref null $many)))
+ ;; CHECK: (import "mod" "f5" (func $fimport$1))
+
;; CHECK: (global $2 i32 (i32.const 0))
;; CHECK: (global $i32 i32 (i32.const 42))
(global $i32 i32 i32.const 42)
- ;; uninteresting globals just to use the types
- ;; CHECK: (global $s0 (mut (ref null $s0)) (ref.null $s0))
- (global $s0 (mut (ref null $s0)) ref.null $s0)
- ;; CHECK: (global $s1 (mut (ref null $s1)) (ref.null $s1))
- (global $s1 (mut (ref null $s1)) ref.null $s1)
- ;; CHECK: (global $s2 (mut (ref null $s2)) (ref.null $s2))
- (global $s2 (mut (ref null $s2)) ref.null $s2)
- ;; CHECK: (global $s3 (mut (ref null $s3)) (ref.null $s3))
- (global $s3 (mut (ref null $s3)) ref.null $s3)
- ;; CHECK: (global $s4 (mut (ref null $s4)) (ref.null $s4))
- (global $s4 (mut (ref null $s4)) ref.null $s4)
- ;; CHECK: (global $s5 (mut (ref null $s5)) (ref.null $s5))
- (global $s5 (mut (ref null $s5)) ref.null $s5)
- ;; CHECK: (global $s6 (mut (ref null $s6)) (ref.null $s6))
- (global $s6 (mut (ref null $s6)) ref.null $s6)
- ;; CHECK: (global $s7 (mut (ref null $s7)) (ref.null $s7))
- (global $s7 (mut (ref null $s7)) ref.null $s7)
- ;; CHECK: (global $s8 (mut (ref null $s8)) (ref.null $s8))
- (global $s8 (mut (ref null $s8)) ref.null $s8)
- ;; CHECK: (global $a0 (mut (ref null $a0)) (ref.null $a0))
- (global $a0 (mut (ref null $a0)) ref.null $a0)
- ;; CHECK: (global $a1 (mut (ref null $a1)) (ref.null $a1))
- (global $a1 (mut (ref null $a1)) ref.null $a1)
- ;; CHECK: (global $a2 (mut (ref null $a2)) (ref.null $a2))
- (global $a2 (mut (ref null $a2)) ref.null $a2)
- ;; CHECK: (global $a3 (mut (ref null $a3)) (ref.null $a3))
- (global $a3 (mut (ref null $a3)) ref.null $a3)
- ;; CHECK: (global $sub0 (mut (ref null $subvoid)) (ref.null $subvoid))
- (global $sub0 (mut (ref null $subvoid)) ref.null $subvoid)
- ;; CHECK: (global $sub1 (mut (ref null $submany)) (ref.null $submany))
- (global $sub1 (mut (ref null $submany)) ref.null $submany)
-)
-;; CHECK: (export "g1" (global $g1))
+ ;; functions
+ (func)
-;; CHECK: (export "g1.1" (global $g1))
+ ;; CHECK: (export "g1" (global $g1))
+
+ ;; CHECK: (export "g1.1" (global $g1))
+
+ ;; CHECK: (export "f5.0" (func $fimport$1))
+
+ ;; CHECK: (export "f5.1" (func $fimport$1))
+
+ ;; CHECK: (func $0 (type $void)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+
+ ;; CHECK: (func $f1 (type $i32_=>_none) (param $0 i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $f1 (param i32))
+ ;; CHECK: (func $f2 (type $i32_=>_none) (param $x i32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $f2 (param $x i32))
+ ;; CHECK: (func $f3 (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ (func $f3 (result i32)
+ i32.const 0
+ )
+ ;; CHECK: (func $f4 (type $void)
+ ;; CHECK-NEXT: (local $0 i32)
+ ;; CHECK-NEXT: (local $1 i64)
+ ;; CHECK-NEXT: (local $l f32)
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $f4 (type 13) (local i32 i64) (local $l f32))
+ (func (export "f5.0") (export "f5.1") (import "mod" "f5"))
+
+ ;; CHECK: (func $use-types (type $ref|$s0|_ref|$s1|_ref|$s2|_ref|$s3|_ref|$s4|_ref|$s5|_ref|$s6|_ref|$s7|_ref|$s8|_ref|$a0|_ref|$a1|_ref|$a2|_ref|$a3|_ref|$subvoid|_ref|$submany|_=>_none) (param $0 (ref $s0)) (param $1 (ref $s1)) (param $2 (ref $s2)) (param $3 (ref $s3)) (param $4 (ref $s4)) (param $5 (ref $s5)) (param $6 (ref $s6)) (param $7 (ref $s7)) (param $8 (ref $s8)) (param $9 (ref $a0)) (param $10 (ref $a1)) (param $11 (ref $a2)) (param $12 (ref $a3)) (param $13 (ref $subvoid)) (param $14 (ref $submany))
+ ;; CHECK-NEXT: (nop)
+ ;; CHECK-NEXT: )
+ (func $use-types
+ (param (ref $s0))
+ (param (ref $s1))
+ (param (ref $s2))
+ (param (ref $s3))
+ (param (ref $s4))
+ (param (ref $s5))
+ (param (ref $s6))
+ (param (ref $s7))
+ (param (ref $s8))
+ (param (ref $a0))
+ (param (ref $a1))
+ (param (ref $a2))
+ (param (ref $a3))
+ (param (ref $subvoid))
+ (param (ref $submany))
+ )
+)