diff --git a/src/Interpreter/instructions.cpp b/src/Interpreter/instructions.cpp index 2eba848d..00652852 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 834c4f4f..63062375 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 57e18a25..501eef95 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); + 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 5d4ddc39..b462faa9 100644 --- a/src/WARDuino/WARDuino.cpp +++ b/src/WARDuino/WARDuino.cpp @@ -73,21 +73,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]; } } @@ -214,7 +226,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; @@ -260,7 +272,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,7 +920,8 @@ WARDuino::WARDuino() { // Return value of false means exception occurred bool WARDuino::invoke(Module *m, uint32_t fidx, uint32_t arity, - StackValue *args) { + StackValue *args, uint32_t max_results, + StackValue *out_results, uint32_t *out_result_count) { bool result; m->sp = -1; m->fp = -1; @@ -926,7 +939,25 @@ 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) { @@ -938,9 +969,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); 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)) +)