From 0b7919ae9eccfd0fff88e2ebfffbbc04c5a12da4 Mon Sep 17 00:00:00 2001 From: Abel Stuker Date: Fri, 14 Nov 2025 02:31:49 +0100 Subject: [PATCH 1/6] feat: multivalue --- src/Interpreter/instructions.cpp | 2 +- src/Interpreter/interpreter.cpp | 14 +++--- src/WARDuino.h | 2 +- src/WARDuino/WARDuino.cpp | 80 +++++++++++++++++++++++--------- 4 files changed, 67 insertions(+), 31 deletions(-) diff --git a/src/Interpreter/instructions.cpp b/src/Interpreter/instructions.cpp index fd666702..36731100 100644 --- a/src/Interpreter/instructions.cpp +++ b/src/Interpreter/instructions.cpp @@ -130,9 +130,9 @@ bool i_instr_if(Module *m, uint8_t *block_ptr) { sprintf(exception, "call stack exhausted"); return false; } + uint32_t cond = m->stack[m->sp--].value.uint32; m->warduino->interpreter->push_block(m, block, m->sp); - uint32_t cond = m->stack[m->sp--].value.uint32; if (cond == 0) { // if false (I32) // branch to else block or after end of if if (block->else_ptr == nullptr) { diff --git a/src/Interpreter/interpreter.cpp b/src/Interpreter/interpreter.cpp index ee021c58..98838da9 100644 --- a/src/Interpreter/interpreter.cpp +++ b/src/Interpreter/interpreter.cpp @@ -45,20 +45,18 @@ Block *Interpreter::pop_block(Module *m) { m->fp = frame->fp; // Restore frame pointer // Validate the return value - if (t->result_count == 1) { - if (m->stack[m->sp].value_type != t->results[0]) { + for (uint32_t i = 0; i < t->result_count; i++) { + if (m->stack[m->sp - (t->result_count - 1 - i)].value_type != t->results[i]) { sprintf(exception, "call type mismatch"); return nullptr; } } - // Restore stack pointer - if (t->result_count == 1) { - // Save top value as result - if (frame->sp < m->sp) { - m->stack[frame->sp + 1] = m->stack[m->sp]; - m->sp = frame->sp + 1; + if (t->result_count > 0) { + for (uint32_t i = 0; i < t->result_count; i++) { + m->stack[frame->sp + 1 + i] = m->stack[m->sp - (t->result_count - 1) + i]; } + m->sp = frame->sp + t->result_count; } else { if (frame->sp < m->sp) { m->sp = frame->sp; diff --git a/src/WARDuino.h b/src/WARDuino.h index 2069eda6..0e0541f8 100644 --- a/src/WARDuino.h +++ b/src/WARDuino.h @@ -108,7 +108,7 @@ class WARDuino { void update_module(Module *old_module, uint8_t *wasm, uint32_t wasm_len); bool invoke(Module *m, uint32_t fidx, uint32_t arity = 0, - StackValue *args = nullptr); + StackValue *args = nullptr, uint32_t max_results = 0, StackValue *out_results = nullptr, uint32_t *out_result_count = nullptr); uint32_t get_export_fidx(Module *m, const char *name); diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index ae41c1fe..6e216a13 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -74,24 +74,37 @@ void initTypes() { block_types[4].results = block_type_results[3]; } -Type *get_block_type(uint8_t value_type) { - switch (value_type) { - case 0x40: - return &block_types[0]; - case I32: - return &block_types[1]; - case I64: - return &block_types[2]; - case F32: - return &block_types[3]; - case F64: - return &block_types[4]; - default: - FATAL("invalid block_type value_type: %d\n", value_type); +Type *get_block_type(Module *m, uint8_t type) { + uint8_t *pos = &type; + int64_t type_s = read_LEB_signed(&pos, 33); + + if (type_s < 0) { + switch (type) { + case 0x40: + return &block_types[0]; // empty + case I32: + return &block_types[1]; + case I64: + return &block_types[2]; + case F32: + return &block_types[3]; + case F64: + return &block_types[4]; + default: + FATAL("invalid block_type value_type: %d\n", type); + return nullptr; + } + } else { + if ((uint32_t)type_s >= m->type_count) { + FATAL("block_type index out of bounds: %lld >= %u\n", + type_s, m->type_count); return nullptr; + } + return &m->types[type_s]; } } + // TODO: calculate this while parsing types uint64_t get_type_mask(Type *type) { uint64_t mask = 0x80; @@ -215,7 +228,7 @@ void find_blocks(Module *m) { case 0x04: // if block = (Block *)acalloc(1, sizeof(Block), "Block"); block->block_type = opcode; - block->type = get_block_type(*(pos + 1)); + block->type = get_block_type(m, *(pos + 1)); block->start_ptr = pos; blockstack[++top] = block; m->block_lookup[pos] = block; @@ -261,7 +274,7 @@ void WARDuino::run_init_expr(Module *m, uint8_t type, uint8_t **pc) { WARDuino::instance()->program_state = WARDUINOinit; Block block; block.block_type = 0x01; - block.type = get_block_type(type); + block.type = get_block_type(m, type); block.start_ptr = *pc; m->pc_ptr = *pc; @@ -899,8 +912,7 @@ WARDuino::WARDuino() { } // Return value of false means exception occurred -bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, - StackValue *args) { +bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, StackValue *args, uint32_t max_results, StackValue *out_results, uint32_t *out_result_count) { bool result; m->sp = -1; m->fp = -1; @@ -918,9 +930,29 @@ bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, result = interpreter->interpret(m); dbg_trace("Interpretation ended\n"); dbg_dump_stack(m); - return result; + + if (!result) { + if (out_result_count) *out_result_count = 0; + return false; + } + + uint32_t rescount = 0; + Type *ftype = m->functions[fidx].type; + rescount = ftype->result_count; + + if (out_result_count) + { + *out_result_count = rescount > max_results ? max_results : rescount; + + for (uint32_t i = 0; i < *out_result_count; ++i) { + out_results[i] = m->stack[m->sp - (rescount - 1) + i]; + } + } + + return true; } + void WARDuino::setInterpreter(Interpreter *interpreter) { this->interpreter = interpreter; } @@ -930,9 +962,15 @@ int WARDuino::run_module(Module *m) { // execute main if (fidx != UNDEF) { - this->invoke(m, fidx); - return m->stack->value.uint32; + StackValue outputs[8]; + uint32_t out_count = 0; + bool ok = this->invoke(m, fidx, 0, nullptr, 8, outputs, &out_count); + if (!ok) { + return 0; + } + return (int)outputs[0].value.uint32; } + fflush(stdout); // wait m->warduino->debugger->pauseRuntime(m); From dc66782bedb23edc6dd1db64a83f8902449edf8b Mon Sep 17 00:00:00 2001 From: Abel Stuker Date: Mon, 17 Nov 2025 00:39:59 +0100 Subject: [PATCH 2/6] Clang format --- src/Interpreter/interpreter.cpp | 6 ++++-- src/WARDuino.h | 4 +++- src/WARDuino/WARDuino.cpp | 17 ++++++++--------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Interpreter/interpreter.cpp b/src/Interpreter/interpreter.cpp index 98838da9..1292858b 100644 --- a/src/Interpreter/interpreter.cpp +++ b/src/Interpreter/interpreter.cpp @@ -46,7 +46,8 @@ Block *Interpreter::pop_block(Module *m) { // Validate the return value for (uint32_t i = 0; i < t->result_count; i++) { - if (m->stack[m->sp - (t->result_count - 1 - i)].value_type != t->results[i]) { + if (m->stack[m->sp - (t->result_count - 1 - i)].value_type != + t->results[i]) { sprintf(exception, "call type mismatch"); return nullptr; } @@ -54,7 +55,8 @@ Block *Interpreter::pop_block(Module *m) { // Restore stack pointer if (t->result_count > 0) { for (uint32_t i = 0; i < t->result_count; i++) { - m->stack[frame->sp + 1 + i] = m->stack[m->sp - (t->result_count - 1) + i]; + m->stack[frame->sp + 1 + i] = + m->stack[m->sp - (t->result_count - 1) + i]; } m->sp = frame->sp + t->result_count; } else { diff --git a/src/WARDuino.h b/src/WARDuino.h index 0e0541f8..1f933adb 100644 --- a/src/WARDuino.h +++ b/src/WARDuino.h @@ -108,7 +108,9 @@ class WARDuino { void update_module(Module *old_module, uint8_t *wasm, uint32_t wasm_len); bool invoke(Module *m, uint32_t fidx, uint32_t arity = 0, - StackValue *args = nullptr, uint32_t max_results = 0, StackValue *out_results = nullptr, uint32_t *out_result_count = nullptr); + StackValue *args = nullptr, uint32_t max_results = 0, + StackValue *out_results = nullptr, + uint32_t *out_result_count = nullptr); uint32_t get_export_fidx(Module *m, const char *name); diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index 6e216a13..374ce09e 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -77,7 +77,7 @@ void initTypes() { Type *get_block_type(Module *m, uint8_t type) { uint8_t *pos = &type; int64_t type_s = read_LEB_signed(&pos, 33); - + if (type_s < 0) { switch (type) { case 0x40: @@ -96,15 +96,14 @@ Type *get_block_type(Module *m, uint8_t type) { } } else { if ((uint32_t)type_s >= m->type_count) { - FATAL("block_type index out of bounds: %lld >= %u\n", - type_s, m->type_count); + FATAL("block_type index out of bounds: %lld >= %u\n", type_s, + m->type_count); return nullptr; } return &m->types[type_s]; } } - // TODO: calculate this while parsing types uint64_t get_type_mask(Type *type) { uint64_t mask = 0x80; @@ -912,7 +911,9 @@ WARDuino::WARDuino() { } // Return value of false means exception occurred -bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, StackValue *args, uint32_t max_results, StackValue *out_results, uint32_t *out_result_count) { +bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, + StackValue *args, uint32_t max_results, + StackValue *out_results, uint32_t *out_result_count) { bool result; m->sp = -1; m->fp = -1; @@ -940,10 +941,9 @@ bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, StackValue *args Type *ftype = m->functions[fidx].type; rescount = ftype->result_count; - if (out_result_count) - { + if (out_result_count) { *out_result_count = rescount > max_results ? max_results : rescount; - + for (uint32_t i = 0; i < *out_result_count; ++i) { out_results[i] = m->stack[m->sp - (rescount - 1) + i]; } @@ -952,7 +952,6 @@ bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, StackValue *args return true; } - void WARDuino::setInterpreter(Interpreter *interpreter) { this->interpreter = interpreter; } From 83f95ba9690b0a365ad613bcaa5d1840c1b51ce0 Mon Sep 17 00:00:00 2001 From: Abel Stuker Date: Sat, 22 Nov 2025 12:33:03 +0100 Subject: [PATCH 3/6] Fix printf type for int64_t in block_type fatal --- src/WARDuino/WARDuino.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index 374ce09e..7b700bf3 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -96,8 +96,8 @@ Type *get_block_type(Module *m, uint8_t type) { } } else { if ((uint32_t)type_s >= m->type_count) { - FATAL("block_type index out of bounds: %lld >= %u\n", type_s, - m->type_count); + FATAL("block_type index out of bounds: %lld >= %u\n", + (long long)type_s, m->type_count); return nullptr; } return &m->types[type_s]; From 23a5d4fc892787f025d4c213778f01cdc8a795f1 Mon Sep 17 00:00:00 2001 From: Abel Stuker Date: Sat, 22 Nov 2025 12:42:25 +0100 Subject: [PATCH 4/6] Fix printf type for int64_t in block_type fatal --- src/WARDuino/WARDuino.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index 7b700bf3..403b4939 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -97,7 +97,7 @@ Type *get_block_type(Module *m, uint8_t type) { } else { if ((uint32_t)type_s >= m->type_count) { FATAL("block_type index out of bounds: %lld >= %u\n", - (long long)type_s, m->type_count); + (long long)type_s, (unsigned long)m->type_count); return nullptr; } return &m->types[type_s]; From 5511a36c1664b38934b13b7aa76e9999d1571c5f Mon Sep 17 00:00:00 2001 From: Abel Stuker Date: Sat, 22 Nov 2025 13:16:03 +0100 Subject: [PATCH 5/6] Fix printf type for uint32_t in block_type fatal --- src/WARDuino/WARDuino.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index 403b4939..1896e5b1 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -97,7 +97,7 @@ Type *get_block_type(Module *m, uint8_t type) { } else { if ((uint32_t)type_s >= m->type_count) { FATAL("block_type index out of bounds: %lld >= %u\n", - (long long)type_s, (unsigned long)m->type_count); + (long long)type_s, (unsigned int)m->type_count); return nullptr; } return &m->types[type_s]; From 3456b6ffeeb2640c8767e9b571c13c45ea5e3d6a Mon Sep 17 00:00:00 2001 From: Abel Stuker Date: Thu, 4 Dec 2025 00:46:26 +0100 Subject: [PATCH 6/6] Add function spectests --- tests/latch/core/func_0.asserts.wast | 108 ++++++++++++ tests/latch/core/func_0.wast | 237 +++++++++++++++++++++++++++ 2 files changed, 345 insertions(+) create mode 100644 tests/latch/core/func_0.asserts.wast create mode 100644 tests/latch/core/func_0.wast diff --git a/tests/latch/core/func_0.asserts.wast b/tests/latch/core/func_0.asserts.wast new file mode 100644 index 00000000..c0533803 --- /dev/null +++ b/tests/latch/core/func_0.asserts.wast @@ -0,0 +1,108 @@ +;; https://github.com/WebAssembly/testsuite/blob/1144e51585d4571a443dbcbb3300d8d20d9b6d4d/func.wast + +(assert_return (invoke "type-use-1")) +(assert_return (invoke "type-use-2") (i32.const 0)) +(assert_return (invoke "type-use-3" (i32.const 1))) +(assert_return + (invoke "type-use-4" (i32.const 1) (f64.const 1) (i32.const 1)) + (i32.const 0) +) +(assert_return (invoke "type-use-5") (i32.const 0)) +(assert_return (invoke "type-use-6" (i32.const 1))) +(assert_return + (invoke "type-use-7" (i32.const 1) (f64.const 1) (i32.const 1)) + (i32.const 0) +) + +(assert_return (invoke "local-first-i32") (i32.const 0)) +;; (assert_return (invoke "local-first-i64") (i64.const 0)) +(assert_return (invoke "local-first-f32") (f32.const 0)) +;; (assert_return (invoke "local-first-f64") (f64.const 0)) +(assert_return (invoke "local-second-i32") (i32.const 0)) +;; (assert_return (invoke "local-second-i64") (i64.const 0)) +(assert_return (invoke "local-second-f32") (f32.const 0)) +;; (assert_return (invoke "local-second-f64") (f64.const 0)) +;; (assert_return (invoke "local-mixed") (f64.const 0)) + +(assert_return (invoke "param-first-i32" (i32.const 2) (i32.const 3)) (i32.const 2)) +(assert_return (invoke "param-first-i64" (i64.const 2) (i64.const 3)) (i64.const 2)) +(assert_return (invoke "param-first-f32" (f32.const 2) (f32.const 3)) (f32.const 2)) +(assert_return (invoke "param-first-f64" (f64.const 2) (f64.const 3)) (f64.const 2)) +(assert_return (invoke "param-second-i32" (i32.const 2) (i32.const 3)) (i32.const 3)) +(assert_return (invoke "param-second-i64" (i64.const 2) (i64.const 3)) (i64.const 3)) +(assert_return (invoke "param-second-f32" (f32.const 2) (f32.const 3)) (f32.const 3)) +(assert_return (invoke "param-second-f64" (f64.const 2) (f64.const 3)) (f64.const 3)) + +(assert_return (invoke "param-mixed" (f32.const 1) (i32.const 2) (i64.const 3) (i32.const 4) (f64.const 5.5) (i32.const 6)) (f64.const 5.5)) + +(assert_return (invoke "empty")) +(assert_return (invoke "value-void")) +(assert_return (invoke "value-i32") (i32.const 77)) +(assert_return (invoke "value-i64") (i64.const 7777)) +;; (assert_return (invoke "value-f32") (f32.const 77.7)) +(assert_return (invoke "value-f64") (f64.const 77.77)) +(assert_return (invoke "value-i32-f64") (i32.const 77) (f64.const 7)) +(assert_return (invoke "value-i32-i32-i32") (i32.const 1) (i32.const 2) (i32.const 3)) +(assert_return (invoke "value-block-void")) +(assert_return (invoke "value-block-i32") (i32.const 77)) +(assert_return (invoke "value-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "return-empty")) +(assert_return (invoke "return-i32") (i32.const 78)) +(assert_return (invoke "return-i64") (i64.const 7878)) +;; (assert_return (invoke "return-f32") (f32.const 78.7)) +(assert_return (invoke "return-f64") (f64.const 78.78)) +(assert_return (invoke "return-i32-f64") (i32.const 78) (f64.const 78.78)) +(assert_return (invoke "return-i32-i32-i32") (i32.const 1) (i32.const 2) (i32.const 3)) +(assert_return (invoke "return-block-i32") (i32.const 77)) +(assert_return (invoke "return-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "break-empty")) +(assert_return (invoke "break-i32") (i32.const 79)) +(assert_return (invoke "break-i64") (i64.const 7979)) +;; (assert_return (invoke "break-f32") (f32.const 79.9)) +(assert_return (invoke "break-f64") (f64.const 79.79)) +(assert_return (invoke "break-i32-f64") (i32.const 79) (f64.const 79.79)) +(assert_return (invoke "break-i32-i32-i32") (i32.const 1) (i32.const 2) (i32.const 3)) +(assert_return (invoke "break-block-i32") (i32.const 77)) +(assert_return (invoke "break-block-i32-i64") (i32.const 1) (i64.const 2)) + +(assert_return (invoke "break-br_if-empty" (i32.const 0))) +(assert_return (invoke "break-br_if-empty" (i32.const 2))) +(assert_return (invoke "break-br_if-num" (i32.const 0)) (i32.const 51)) +(assert_return (invoke "break-br_if-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_if-num-num" (i32.const 0)) (i32.const 51) (i64.const 52)) +(assert_return (invoke "break-br_if-num-num" (i32.const 1)) (i32.const 50) (i64.const 51)) + +(assert_return (invoke "break-br_table-empty" (i32.const 0))) +(assert_return (invoke "break-br_table-empty" (i32.const 1))) +(assert_return (invoke "break-br_table-empty" (i32.const 5))) +(assert_return (invoke "break-br_table-empty" (i32.const -1))) +(assert_return (invoke "break-br_table-num" (i32.const 0)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const 10)) (i32.const 50)) +(assert_return (invoke "break-br_table-num" (i32.const -100)) (i32.const 50)) +(assert_return (invoke "break-br_table-num-num" (i32.const 0)) (f32.const 50) (i64.const 51)) +(assert_return (invoke "break-br_table-num-num" (i32.const 1)) (f32.const 50) (i64.const 51)) +(assert_return (invoke "break-br_table-num-num" (i32.const 10)) (f32.const 50) (i64.const 51)) +(assert_return (invoke "break-br_table-num-num" (i32.const -100)) (f32.const 50) (i64.const 51)) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 0))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 1))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const 3))) +(assert_return (invoke "break-br_table-nested-empty" (i32.const -2))) +(assert_return (invoke "break-br_table-nested-num" (i32.const 0)) (i32.const 52)) +(assert_return (invoke "break-br_table-nested-num" (i32.const 1)) (i32.const 50)) +(assert_return (invoke "break-br_table-nested-num" (i32.const 2)) (i32.const 52)) +(assert_return (invoke "break-br_table-nested-num" (i32.const -3)) (i32.const 52)) +(assert_return (invoke "break-br_table-nested-num-num" (i32.const 0)) (i32.const 101) (i32.const 52)) +(assert_return (invoke "break-br_table-nested-num-num" (i32.const 1)) (i32.const 50) (i32.const 51)) +(assert_return (invoke "break-br_table-nested-num-num" (i32.const 2)) (i32.const 101) (i32.const 52)) +(assert_return (invoke "break-br_table-nested-num-num" (i32.const -3)) (i32.const 101) (i32.const 52)) + +(assert_return (invoke "large-sig" (i32.const 0) (i64.const 1) (f32.const 2) (f32.const 3) (i32.const 4) (f64.const 5) (f32.const 6) (i32.const 7) (i32.const 8) (i32.const 9) (f32.const 10) (f64.const 11) (f64.const 12) (f64.const 13) (i32.const 14) (i32.const 15) (f32.const 16)) (f64.const 5) (f32.const 2) (i32.const 0) (i32.const 8) (i32.const 7) (i64.const 1) (f32.const 3) (i32.const 9) + (i32.const 4) (f32.const 6) (f64.const 13) (f64.const 11) (i32.const 15) (f32.const 16) (i32.const 14) (f64.const 12)) + +(assert_return (invoke "init-local-i32") (i32.const 0)) +;; (assert_return (invoke "init-local-i64") (i64.const 0)) +(assert_return (invoke "init-local-f32") (f32.const 0)) +;; (assert_return (invoke "init-local-f64") (f64.const 0)) \ No newline at end of file diff --git a/tests/latch/core/func_0.wast b/tests/latch/core/func_0.wast new file mode 100644 index 00000000..92e12f1d --- /dev/null +++ b/tests/latch/core/func_0.wast @@ -0,0 +1,237 @@ +(module + ;; Auxiliary definition + (type $sig (func)) + (func $dummy) + + ;; Syntax + + (func) + (func (export "f")) + (func $f) + (func $h (export "g")) + + (func (local)) + (func (local) (local)) + (func (local i32)) + (func (local $x i32)) + (func (local i32 f64 i64)) + (func (local i32) (local f64)) + (func (local i32 f32) (local $x i64) (local) (local i32 f64)) + + (func (param)) + (func (param) (param)) + (func (param i32)) + (func (param $x i32)) + (func (param i32 f64 i64)) + (func (param i32) (param f64)) + (func (param i32 f32) (param $x i64) (param) (param i32 f64)) + + (func (result)) + (func (result) (result)) + (func (result i32) (unreachable)) + (func (result i32 f64 f32) (unreachable)) + (func (result i32) (result f64) (unreachable)) + (func (result i32 f32) (result i64) (result) (result i32 f64) (unreachable)) + + (type $sig-1 (func)) + (type $sig-2 (func (result i32))) + (type $sig-3 (func (param $x i32))) + (type $sig-4 (func (param i32 f64 i32) (result i32))) + + (func (export "type-use-1") (type $sig-1)) + (func (export "type-use-2") (type $sig-2) (i32.const 0)) + (func (export "type-use-3") (type $sig-3)) + (func (export "type-use-4") (type $sig-4) (i32.const 0)) + (func (export "type-use-5") (type $sig-2) (result i32) (i32.const 0)) + (func (export "type-use-6") (type $sig-3) (param i32)) + (func (export "type-use-7") + (type $sig-4) (param i32) (param f64 i32) (result i32) (i32.const 0) + ) + + (func (type $sig)) + (func (type $forward)) ;; forward reference + + (func $complex + (param i32 f32) (param $x i64) (param) (param i32) + (result) (result i32) (result) (result i64 i32) + (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) + (unreachable) (unreachable) + ) + (func $complex-sig + (type $sig) + (local f32) (local $y i32) (local i64 i32) (local) (local f64 i32) + (unreachable) (unreachable) + ) + + (type $forward (func)) + + ;; Typing of locals + + (func (export "local-first-i32") (result i32) (local i32 i32) (local.get 0)) + (func (export "local-first-i64") (result i64) (local i64 i64) (local.get 0)) + (func (export "local-first-f32") (result f32) (local f32 f32) (local.get 0)) + (func (export "local-first-f64") (result f64) (local f64 f64) (local.get 0)) + (func (export "local-second-i32") (result i32) (local i32 i32) (local.get 1)) + (func (export "local-second-i64") (result i64) (local i64 i64) (local.get 1)) + (func (export "local-second-f32") (result f32) (local f32 f32) (local.get 1)) + (func (export "local-second-f64") (result f64) (local f64 f64) (local.get 1)) + (func (export "local-mixed") (result f64) + (local f32) (local $x i32) (local i64 i32) (local) (local f64 i32) + (drop (f32.neg (local.get 0))) + (drop (i32.eqz (local.get 1))) + (drop (i64.eqz (local.get 2))) + (drop (i32.eqz (local.get 3))) + (drop (f64.neg (local.get 4))) + (drop (i32.eqz (local.get 5))) + (local.get 4) + ) + + ;; Typing of parameters + + (func (export "param-first-i32") (param i32 i32) (result i32) (local.get 0)) + (func (export "param-first-i64") (param i64 i64) (result i64) (local.get 0)) + (func (export "param-first-f32") (param f32 f32) (result f32) (local.get 0)) + (func (export "param-first-f64") (param f64 f64) (result f64) (local.get 0)) + (func (export "param-second-i32") (param i32 i32) (result i32) (local.get 1)) + (func (export "param-second-i64") (param i64 i64) (result i64) (local.get 1)) + (func (export "param-second-f32") (param f32 f32) (result f32) (local.get 1)) + (func (export "param-second-f64") (param f64 f64) (result f64) (local.get 1)) + (func (export "param-mixed") (param f32 i32) (param) (param $x i64) (param i32 f64 i32) + (result f64) + (drop (f32.neg (local.get 0))) + (drop (i32.eqz (local.get 1))) + (drop (i64.eqz (local.get 2))) + (drop (i32.eqz (local.get 3))) + (drop (f64.neg (local.get 4))) + (drop (i32.eqz (local.get 5))) + (local.get 4) + ) + + ;; Typing of results + + (func (export "empty")) + (func (export "value-void") (call $dummy)) + (func (export "value-i32") (result i32) (i32.const 77)) + (func (export "value-i64") (result i64) (i64.const 7777)) + (func (export "value-f32") (result f32) (f32.const 77.7)) + (func (export "value-f64") (result f64) (f64.const 77.77)) + (func (export "value-i32-f64") (result i32 f64) (i32.const 77) (f64.const 7)) + (func (export "value-i32-i32-i32") (result i32 i32 i32) + (i32.const 1) (i32.const 2) (i32.const 3) + ) + (func (export "value-block-void") (block (call $dummy) (call $dummy))) + (func (export "value-block-i32") (result i32) + (block (result i32) (call $dummy) (i32.const 77)) + ) + (func (export "value-block-i32-i64") (result i32 i64) + (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2)) + ) + + (func (export "return-empty") (return)) + (func (export "return-i32") (result i32) (return (i32.const 78))) + (func (export "return-i64") (result i64) (return (i64.const 7878))) + (func (export "return-f32") (result f32) (return (f32.const 78.7))) + (func (export "return-f64") (result f64) (return (f64.const 78.78))) + (func (export "return-i32-f64") (result i32 f64) + (return (i32.const 78) (f64.const 78.78)) + ) + (func (export "return-i32-i32-i32") (result i32 i32 i32) + (return (i32.const 1) (i32.const 2) (i32.const 3)) + ) + (func (export "return-block-i32") (result i32) + (return (block (result i32) (call $dummy) (i32.const 77))) + ) + (func (export "return-block-i32-i64") (result i32 i64) + (return (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) + + (func (export "break-empty") (br 0)) + (func (export "break-i32") (result i32) (br 0 (i32.const 79))) + (func (export "break-i64") (result i64) (br 0 (i64.const 7979))) + (func (export "break-f32") (result f32) (br 0 (f32.const 79.9))) + (func (export "break-f64") (result f64) (br 0 (f64.const 79.79))) + (func (export "break-i32-f64") (result i32 f64) + (br 0 (i32.const 79) (f64.const 79.79)) + ) + (func (export "break-i32-i32-i32") (result i32 i32 i32) + (br 0 (i32.const 1) (i32.const 2) (i32.const 3)) + ) + (func (export "break-block-i32") (result i32) + (br 0 (block (result i32) (call $dummy) (i32.const 77))) + ) + (func (export "break-block-i32-i64") (result i32 i64) + (br 0 (block (result i32 i64) (call $dummy) (i32.const 1) (i64.const 2))) + ) + + (func (export "break-br_if-empty") (param i32) + (br_if 0 (local.get 0)) + ) + (func (export "break-br_if-num") (param i32) (result i32) + (drop (br_if 0 (i32.const 50) (local.get 0))) (i32.const 51) + ) + (func (export "break-br_if-num-num") (param i32) (result i32 i64) + (drop (drop (br_if 0 (i32.const 50) (i64.const 51) (local.get 0)))) + (i32.const 51) (i64.const 52) + ) + + (func (export "break-br_table-empty") (param i32) + (br_table 0 0 0 (local.get 0)) + ) + (func (export "break-br_table-num") (param i32) (result i32) + (br_table 0 0 (i32.const 50) (local.get 0)) (i32.const 51) + ) + (func (export "break-br_table-num-num") (param i32) (result f32 i64) + (br_table 0 0 (f32.const 50) (i64.const 51) (local.get 0)) + (f32.const 51) (i64.const 52) + ) + (func (export "break-br_table-nested-empty") (param i32) + (block (br_table 0 1 0 (local.get 0))) + ) + (func (export "break-br_table-nested-num") (param i32) (result i32) + (i32.add + (block (result i32) + (br_table 0 1 0 (i32.const 50) (local.get 0)) (i32.const 51) + ) + (i32.const 2) + ) + ) + (func (export "break-br_table-nested-num-num") (param i32) (result i32 i32) + (i32.add + (block (result i32 i32) + (br_table 0 1 0 (i32.const 50) (i32.const 51) (local.get 0)) + (i32.const 51) (i32.const -3) + ) + ) + (i32.const 52) + ) + + ;; Large signatures + + (func (export "large-sig") + (param i32 i64 f32 f32 i32 f64 f32 i32 i32 i32 f32 f64 f64 f64 i32 i32 f32) + (result f64 f32 i32 i32 i32 i64 f32 i32 i32 f32 f64 f64 i32 f32 i32 f64) + (local.get 5) + (local.get 2) + (local.get 0) + (local.get 8) + (local.get 7) + (local.get 1) + (local.get 3) + (local.get 9) + (local.get 4) + (local.get 6) + (local.get 13) + (local.get 11) + (local.get 15) + (local.get 16) + (local.get 14) + (local.get 12) + ) + + ;; Default initialization of locals + + (func (export "init-local-i32") (result i32) (local i32) (local.get 0)) + (func (export "init-local-i64") (result i64) (local i64) (local.get 0)) + (func (export "init-local-f32") (result f32) (local f32) (local.get 0)) + (func (export "init-local-f64") (result f64) (local f64) (local.get 0)) +)