diff --git a/src/Interpreter/instructions.cpp b/src/Interpreter/instructions.cpp index 5ebaefac2..da6520511 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 d30598822..adbca8f68 100644 --- a/src/Interpreter/interpreter.cpp +++ b/src/Interpreter/interpreter.cpp @@ -45,20 +45,20 @@ 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 08852a950..c1e5aa073 100644 --- a/src/WARDuino.h +++ b/src/WARDuino.h @@ -59,8 +59,8 @@ 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); + std::vector invoke(Module *m, uint32_t fidx, uint32_t arity = 0, + StackValue *args = nullptr); uint32_t get_export_fidx(Module *m, const char *name); diff --git a/src/WARDuino/WARDuino.cpp b/src/WARDuino/WARDuino.cpp index 38865e83e..b08de2430 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -72,21 +72,33 @@ 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", + (long long)type_s, (unsigned int)m->type_count); return nullptr; + } + return &m->types[type_s]; } } @@ -213,7 +225,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; @@ -259,7 +271,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; @@ -908,8 +920,8 @@ WARDuino::WARDuino() { } // Return value of false means exception occurred -bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, - StackValue *args) { +std::vector WARDuino::invoke(Module *m, uint32_t fidx, + uint32_t arity, StackValue *args) { bool result; m->sp = -1; m->fp = -1; @@ -927,7 +939,18 @@ 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) { + return {}; + } + + uint32_t rescount = 0; + Type *ftype = m->functions[fidx].type; + rescount = ftype->result_count; + std::vector out(rescount); + for (uint32_t i = 0; i < rescount; ++i) + out[i] = m->stack[m->sp - (rescount - 1) + i]; + return out; } void WARDuino::setInterpreter(Interpreter *interpreter) { @@ -939,9 +962,11 @@ int WARDuino::run_module(Module *m) { // execute main if (fidx != UNDEF) { - this->invoke(m, fidx); - return m->stack->value.uint32; + auto results = this->invoke(m, fidx); + if (results.empty()) return 0; + return (int)results[0].value.uint32; } + fflush(stdout); // wait m->warduino->debugger->pauseRuntime(m); diff --git a/tests/latch/core/func_0.asserts.wast b/tests/latch/core/func_0.asserts.wast new file mode 100644 index 000000000..1a53d0b9e --- /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 000000000..92e12f1d2 --- /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)) +) diff --git a/tests/latch/core/func_ptrs_0.asserts.wast b/tests/latch/core/func_ptrs_0.asserts.wast new file mode 100644 index 000000000..700a4f4de --- /dev/null +++ b/tests/latch/core/func_ptrs_0.asserts.wast @@ -0,0 +1,4 @@ +(assert_return (invoke "one") (i32.const 13)) +(assert_return (invoke "two" (i32.const 13)) (i32.const 14)) +(assert_return (invoke "three" (i32.const 13)) (i32.const 11)) +;; (assert_return (invoke "four" (i32.const 83))) \ No newline at end of file diff --git a/tests/latch/core/func_ptrs_0.wast b/tests/latch/core/func_ptrs_0.wast new file mode 100644 index 000000000..962b7d3e4 --- /dev/null +++ b/tests/latch/core/func_ptrs_0.wast @@ -0,0 +1,25 @@ +(module + (type (func)) ;; 0: void -> void + (type $S (func)) ;; 1: void -> void + (type (func (param))) ;; 2: void -> void + (type (func (result i32))) ;; 3: void -> i32 + (type (func (param) (result i32))) ;; 4: void -> i32 + (type $T (func (param i32) (result i32))) ;; 5: i32 -> i32 + (type $U (func (param i32))) ;; 6: i32 -> void + + ;; (func $print (import "spectest" "print_i32") (type 6)) + + (func (type 0)) + (func (type $S)) + + (func (export "one") (type 4) (i32.const 13)) + (func (export "two") (type $T) (i32.add (local.get 0) (i32.const 1))) + + ;; Both signature and parameters are allowed (and required to match) + ;; since this allows the naming of parameters. + (func (export "three") (type $T) (param $a i32) (result i32) + (i32.sub (local.get 0) (i32.const 2)) + ) + + ;; (func (export "four") (type $U) (call $print (local.get 0))) +) \ No newline at end of file diff --git a/tutorials/wat/main/multi_value.wat b/tutorials/wat/main/multi_value.wat new file mode 100644 index 000000000..f599e15d7 --- /dev/null +++ b/tutorials/wat/main/multi_value.wat @@ -0,0 +1,287 @@ +(module + (import "env" "print_string" (func $print.string (param i32 i32))) + (import "env" "print_int" (func $print.int (param i32))) + + (memory $mem 1) + (export "memory" (memory $mem)) + + (data (i32.const 0) "Test 1: Basic swap\n") + (data (i32.const 20) "Test 2: Triple return\n") + (data (i32.const 43) "Test 3: Nested calls\n") + (data (i32.const 65) "Test 4: Block multi-value\n") + (data (i32.const 92) "Test 5: Loop multi-value\n") + (data (i32.const 118) "Test 6: If multi-value\n") + (data (i32.const 142) "Test 7: Branch with values\n") + (data (i32.const 170) "Result: ") + (data (i32.const 179) "Test 8: br_table multi-value\n") + (data (i32.const 208) "Test 9: Complex composition\n") + + ;; Test 1: two return values (swap) + (func $swap (param i32 i32) (result i32 i32) + (local.get 1) + (local.get 0) + ) + + (func $test_swap (export "test_swap") + (call $print.string (i32.const 0) (i32.const 19)) + + (i32.const 5) + (i32.const 10) + (call $swap) + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + ) + + ;; Test 2: three return values + (func $triple (param i32) (result i32 i32 i32) + ;; returns (x, x+10, x+20) + (local.get 0) + (local.get 0) + (i32.const 10) + (i32.add) + (local.get 0) + (i32.const 20) + (i32.add) + ) + + (func $test_triple (export "test_triple") + (call $print.string (i32.const 20) (i32.const 22)) + + (i32.const 5) + (call $triple) + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + ) + + ;; Test 3: nested multi-value calls + (func $add_pair (param i32 i32) (result i32) + (local.get 0) + (local.get 1) + (i32.add) + ) + + (func $test_nested (export "test_nested") + (call $print.string (i32.const 43) (i32.const 21)) + + (i32.const 3) + (i32.const 7) + (call $swap) + (call $add_pair) + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + ) + + ;; Test 4: block with multi-value type + (func $test_block (export "test_block") + (call $print.string (i32.const 65) (i32.const 26)) + + (block (result i32 i32) + (i32.const 42) + (i32.const 99) + ) + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + ) + + ;; Test 5: loop with multi-value + (func $test_loop (export "test_loop") + (local $counter i32) + (local $val1 i32) + (local $val2 i32) + (call $print.string (i32.const 92) (i32.const 25)) + + (i32.const 0) + (local.set $counter) + + (i32.const 10) + (local.set $val1) + (i32.const 20) + (local.set $val2) + + ;; loop that accumulates + (block $exit + (loop $continue + + (local.get $counter) + (i32.const 3) + (i32.ge_u) + (br_if $exit) + + ;; incr both values + (local.get $val1) + (i32.const 1) + (i32.add) + (local.set $val1) + + (local.get $val2) + (i32.const 2) + (i32.add) + (local.set $val2) + + ;; incr counter + (local.get $counter) + (i32.const 1) + (i32.add) + (local.set $counter) + + (br $continue) + ) + ) + + ;; get results from loop + (block (result i32 i32) + (local.get $val1) + (local.get $val2) + ) + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + ) + + ;; Test 6: if/else with multi-value result + (func $test_if (export "test_if") (param i32) + (call $print.string (i32.const 118) (i32.const 23)) + + (local.get 0) + (if (result i32 i32) + (then + (i32.const 100) + (i32.const 200) + ) + (else + (i32.const 300) + (i32.const 400) + ) + ) + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + ) + + ;; Test 7: branch with multi-value preversation + (func $test_branch (export "test_branch") (param i32) + (call $print.string (i32.const 142) (i32.const 27)) + + (block (result i32 i32) + (i32.const 10) + (i32.const 20) + + (local.get 0) + (br_if 0) + + ;; if we don't branch, we replace the values + (drop) + (drop) + (i32.const 30) + (i32.const 40) + ) + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + ) + + ;; Test 8: br_table with multi-value + (func $test_br_table (export "test_br_table") (param i32) + (local $result1 i32) + (local $result2 i32) + (call $print.string (i32.const 179) (i32.const 29)) + + (block (result i32 i32) + (block (result i32 i32) + (block (result i32 i32) + (i32.const 1) + (i32.const 2) + + (local.get 0) + (br_table 0 1 2 2) ;; this will branch to case 0, 1, or 2 based on input (with default to 2) + ) + ;; case 0 + (i32.const 10) + (i32.add) + (i32.const 10) + (i32.add) + (br 1) + ) + ;; case 1 + (i32.const 20) + (i32.add) + (i32.const 20) + (i32.add) + ) + ;; case 2 (and default) + (local.set $result2) + (local.set $result1) + + (call $print.string (i32.const 170) (i32.const 8)) + (local.get $result1) + (call $print.int) + (call $print.string (i32.const 170) (i32.const 8)) + (local.get $result2) + (call $print.int) + ) + + ;; Test 9: complex composition + (func $complex (param i32 i32) (result i32 i32 i32) + ;; returns (a+b, a-b, a*2) + (local.get 0) + (local.get 1) + (i32.add) + + (local.get 0) + (local.get 1) + (i32.sub) + + (local.get 0) + (i32.const 2) + (i32.mul) + ) + + (func $test_complex (export "test_complex") + (call $print.string (i32.const 208) (i32.const 28)) + (i32.const 10) + (i32.const 3) + (call $complex) + + (i32.add) ;; 20 + 7 = 27 + (i32.mul) ;; 27 * 13 = 351 + + (call $print.string (i32.const 170) (i32.const 8)) + (call $print.int) + ) + + + (func $main (export "main") + (call $test_swap) + (call $test_triple) + (call $test_nested) + (call $test_block) + (call $test_loop) + (call $test_if (i32.const 1)) + (call $test_if (i32.const 0)) + (call $test_branch (i32.const 1)) + (call $test_branch (i32.const 0)) + (call $test_br_table (i32.const 0)) + (call $test_br_table (i32.const 1)) + (call $test_br_table (i32.const 2)) + (call $test_complex) + ) +) \ No newline at end of file