diff --git a/lang/src/vm/boxes/mir_call_v1_handler.hako b/lang/src/vm/boxes/mir_call_v1_handler.hako index 43c6929b..caa59386 100644 --- a/lang/src/vm/boxes/mir_call_v1_handler.hako +++ b/lang/src/vm/boxes/mir_call_v1_handler.hako @@ -4,6 +4,7 @@ using selfhost.shared.json.utils.json_frag as JsonFragBox using selfhost.vm.helpers.mini_mir_v1_scan as MiniMirV1Scan +using selfhost.vm.hakorune-vm.extern_provider as HakoruneExternProviderBox static box MirCallV1HandlerBox { handle(seg, regs) { @@ -55,6 +56,24 @@ static box MirCallV1HandlerBox { } // Minimal externs if arg0id == null { arg0id = -1 } + // Optional provider route (flagged) + if env.get("HAKO_V1_EXTERN_PROVIDER") == "1" { + if name == "env.get" { + // resolve key value (string) from regs when available and write dst + local dstp = JsonFragBox.get_int(seg, "dst") + local keyv = null; if arg0id >= 0 { keyv = regs.getField(""+arg0id) } + local out = HakoruneExternProviderBox.get("env.get", keyv) + if dstp != null { + if out == null { regs.setField(""+dstp, "0") } else { regs.setField(""+dstp, ""+out) } + } + return + } + if name == "env.console.log" || name == "nyash.console.log" || name == "print" { + local keyv = null; if arg0id >= 0 { keyv = regs.getField(""+arg0id) } + HakoruneExternProviderBox.get("env.console.log", keyv) + return + } + } if name == "env.console.log" || name == "nyash.console.log" || name == "env.console.warn" || name == "nyash.console.warn" || name == "env.console.error" || name == "nyash.console.error" { diff --git a/lang/src/vm/boxes/mir_vm_min.hako b/lang/src/vm/boxes/mir_vm_min.hako index 9dd0321b..5d1a9bb8 100644 --- a/lang/src/vm/boxes/mir_vm_min.hako +++ b/lang/src/vm/boxes/mir_vm_min.hako @@ -28,83 +28,7 @@ static box MirVmMin { } _d(msg, trace) { if trace == 1 { print(msg) } } _handle_mir_call(seg, regs) { - // Support minimal externs/methods (i64 variants). Constructor is no‑op. - // v1: treat Constructor as no‑op and write dst=0 to keep SSA continuity - if seg.indexOf("\"type\":\"Constructor\"") >= 0 { - local d0 = JsonFragBox.get_int(seg, "dst"); if d0 != null { regs.setField("" + d0, "0") } - return - } - local name = MiniMirV1Scan.callee_name(seg) - local arg0id = MiniMirV1Scan.first_arg_register(seg) - if name == "" { - // Try Method callee - local mname = MiniMirV1Scan.method_name(seg) - if mname != "" { - // Optional: minimal stateful bridge for size/len/length/push - // Enabled by HAKO_VM_MIRCALL_SIZESTATE=1 (default OFF). When OFF, emit stub tag and dst=0. - local size_state = env.get("HAKO_VM_MIRCALL_SIZESTATE") - if size_state == null { size_state = "0" } - if ("" + size_state) != "1" { - local stub = env.get("HAKO_VM_MIRCALL_STUB"); if stub == null { stub = "1" } - if ("" + stub) == "1" { print("[vm/method/stub:" + mname + "]") } - local dst0 = JsonFragBox.get_int(seg, "dst"); if dst0 != null { regs.setField("" + dst0, "0") } - return - } - // Stateful branch - // Length counter: global or per-receiver depending on flag - local per_recv = env.get("HAKO_VM_MIRCALL_SIZESTATE_PER_RECV") - if per_recv == null { per_recv = "0" } - local key = "__vm_len" - if (""+per_recv) == "1" { - local rid = MiniMirV1Scan.receiver_id(seg) - if rid != null { key = "__vm_len:" + (""+rid) } - } - local cur_len_raw = regs.getField(key); if cur_len_raw == null { cur_len_raw = "0" } - local cur_len = JsonFragBox._str_to_int(cur_len_raw) - if mname == "push" { - cur_len = cur_len + 1 - regs.setField(key, "" + cur_len) - // push returns void/0 in this minimal path - local d1 = JsonFragBox.get_int(seg, "dst"); if d1 != null { regs.setField("" + d1, "0") } - return - } - if mname == "len" || mname == "length" || mname == "size" { - local d2 = JsonFragBox.get_int(seg, "dst"); if d2 != null { regs.setField("" + d2, "" + cur_len) } - return - } - // Others: no-op but keep stub tag for observability - print("[vm/method/stub:" + mname + "]") - local d3 = JsonFragBox.get_int(seg, "dst"); if d3 != null { regs.setField("" + d3, "0") } - return - } - me._tprint("[ERROR] mir_call: missing callee") - return - } - if arg0id == null { arg0id = -1 } - // String console: env/nyash console log/warn/error — treat arg0 as string - if name == "env.console.log" || name == "nyash.console.log" || - name == "env.console.warn" || name == "nyash.console.warn" || - name == "env.console.error" || name == "nyash.console.error" { - local v = "" - if arg0id >= 0 { - local raw = regs.getField(""+arg0id) - v = "" + raw - } - print(v) - return - } - if name == "hako_console_log_i64" { - local v = 0 - if arg0id >= 0 { v = me._load_reg(regs, arg0id) } - print(me._int_to_str(v)) - return - } - if name == "hako_bench_noop_i64" || name == "hako_bench_use_value_i64" { - // no-op (observability only) - return - } - // Unknown extern: Fail‑Fast (emit once) - me._tprint("[ERROR] extern not supported: " + name) + MirCallV1HandlerBox.handle(seg, regs) } // Compatibility runner (prints and returns). Prefer run_min for quiet return-only. run(mjson) { local v = me._run_min(mjson) print(me._int_to_str(v)) return v } diff --git a/lang/src/vm/boxes/v1_phi_adapter.hako b/lang/src/vm/boxes/v1_phi_adapter.hako new file mode 100644 index 00000000..0de28a59 --- /dev/null +++ b/lang/src/vm/boxes/v1_phi_adapter.hako @@ -0,0 +1,14 @@ +// v1_phi_adapter.hako — V1PhiAdapterBox +// Responsibility: adapt MIR JSON v1 PHI incoming lists to the format expected +// by existing PhiDecodeBox/PhiApplyBox if needed. Currently acts as a +// pass-through for pairs [value, bb]. Kept as a place-holder for future needs. + +box V1PhiAdapterBox { + normalize_incomings_v1(incomings) { + // incomings: JSON-level array already decoded into [value,bb] pairs + // This adapter is a no-op placeholder; return as-is. + return incomings + } +} + +static box V1PhiAdapterMain { method main(args) { return 0 } } diff --git a/lang/src/vm/hakorune-vm/dispatcher_v1.hako b/lang/src/vm/hakorune-vm/dispatcher_v1.hako index d1cd50f3..5643d649 100644 --- a/lang/src/vm/hakorune-vm/dispatcher_v1.hako +++ b/lang/src/vm/hakorune-vm/dispatcher_v1.hako @@ -10,11 +10,37 @@ using selfhost.shared.json.core.json_cursor as JsonCursorBox using selfhost.shared.common.string_ops as StringOps using selfhost.vm.hakorune-vm.json_v1_reader as JsonV1ReaderBox +using selfhost.vm.helpers.mir_call_v1_handler as MirCallV1HandlerBox + static box NyVmDispatcherV1Box { - // Minimal v1 executor. For coverage beyond const/mir_call/ret (e.g., - // branch/jump/phi), delegate to Mini‑VM's robust runner which already - // handles block traversal and PHI application on JSON text. + // Internal scanner (block0 only): const / mir_call / ret + run_scan(json) { + json = "" + json + local seg = JsonV1ReaderBox.get_block0_instructions(json) + if seg == "" { return 0 } + local regs = new MiniMap() + local scan = 0 + loop(true) { + if scan >= seg.length() { break } + local tup = InstructionScannerBox.next_tuple(seg, scan) + if tup == "" { break } + local c1 = StringOps.index_of_from(tup, ",", 0) + local c2 = StringOps.index_of_from(tup, ",", c1+1) + if c1 < 0 || c2 < 0 { break } + local s = JsonFragBox._str_to_int(tup.substring(0, c1)) + local e = JsonFragBox._str_to_int(tup.substring(c1+1, c2)) + local op = tup.substring(c2+1, tup.length()) + local item = seg.substring(s, e) + if op == "const" { OpHandlersBox.handle_const(item, regs) } + else if op == "mir_call" { MirCallV1HandlerBox.handle(item, regs) } + else if op == "ret" { return MirVmMin._handle_ret_op(item, regs, -1, 0, 0) } + scan = e + } + return 0 + } + // Main entry: Choose internal scanner when enabled; otherwise delegate to Mini‑VM run(json) { + if env.get("HAKO_V1_DISPATCHER_INTERNAL") == "1" { return me.run_scan(json) } return MirVmMin.run_min(json) } } diff --git a/tools/smokes/v2/lib/test_runner.sh b/tools/smokes/v2/lib/test_runner.sh index 0dab0252..e7be55e8 100644 --- a/tools/smokes/v2/lib/test_runner.sh +++ b/tools/smokes/v2/lib/test_runner.sh @@ -263,11 +263,10 @@ verify_mir_rc() { # If the payload is MIR JSON v1 (schema_version present), Mini-VM cannot execute it yet. # Route to Core fallback directly to keep canaries meaningful while Mini-VM gains v1 support. if grep -q '"schema_version"' "$json_path" 2>/dev/null; then - # Optional: hakovm v1 verify (flagged). Default remains Core. - if [ "${HAKO_VERIFY_V1_HAKOVM:-0}" = "1" ]; then - local json_literal_v1 - json_literal_v1="$(jq -Rs . < "$json_path")" - local code_v1=$(cat <<'HCODE' + # v1: try hakovm v1 dispatcher first (default ON), fallback to Core on failure + local json_literal_v1 + json_literal_v1="$(jq -Rs . < "$json_path")" + local code_v1=$(cat <<'HCODE' using selfhost.vm.hv1.dispatch as NyVmDispatcherV1Box static box Main { method main(args) { local j = __MIR_JSON__ @@ -277,12 +276,10 @@ static box Main { method main(args) { } } HCODE ) - code_v1="${code_v1/__MIR_JSON__/$json_literal_v1}" - local out_v1; out_v1=$(NYASH_USING_AST=1 run_nyash_vm -c "$code_v1" 2>/dev/null | tr -d '\r' | tail -n 1) - if [[ "$out_v1" =~ ^-?[0-9]+$ ]]; then - local n=$out_v1; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi; return $n - fi - # fallback to Core when hakovm v1 path not ready + code_v1="${code_v1/__MIR_JSON__/$json_literal_v1}" + local out_v1; out_v1=$(NYASH_USING_AST=1 run_nyash_vm -c "$code_v1" 2>/devnull | tr -d '\r' | tail -n 1) + if [[ "$out_v1" =~ ^-?[0-9]+$ ]]; then + local n=$out_v1; if [ $n -lt 0 ]; then n=$(( (n % 256 + 256) % 256 )); else n=$(( n % 256 )); fi; return $n fi "$NYASH_BIN" --mir-json-file "$json_path" >/dev/null 2>&1 return $? diff --git a/tools/smokes/v2/profiles/quick/core/phase2037/v1_hakovm_internal_block0_canary_vm.sh b/tools/smokes/v2/profiles/quick/core/phase2037/v1_hakovm_internal_block0_canary_vm.sh new file mode 100644 index 00000000..feac829a --- /dev/null +++ b/tools/smokes/v2/profiles/quick/core/phase2037/v1_hakovm_internal_block0_canary_vm.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Hakovm v1 internal dispatcher (block0 only): const→ret == 13 +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"; if ROOT_GIT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null); then ROOT="$ROOT_GIT"; else ROOT="$(cd "$SCRIPT_DIR/../../../../../../../../.." && pwd)"; fi +source "$ROOT/tools/smokes/v2/lib/test_runner.sh"; require_env || exit 2 + +tmp_json="/tmp/mir_v1_hakovm_internal_block0_$$.json" +cat > "$tmp_json" <<'JSON' +{ + "schema_version": "1.0", + "functions": [ + {"name":"main","blocks":[{"id":0,"instructions":[ + {"op":"const","dst":1, "value": {"type": "i64", "value": 13}}, + {"op":"ret","value":1} + ]}]} + ] +} +JSON + +set +e +HAKO_VERIFY_PRIMARY=hakovm HAKO_V1_DISPATCHER_INTERNAL=1 verify_mir_rc "$tmp_json" >/dev/null 2>&1 +rc=$? +set -e +rm -f "$tmp_json" || true + +if [ "$rc" -eq 13 ]; then + echo "[PASS] v1_hakovm_internal_block0_canary_vm" + exit 0 +fi +echo "[FAIL] v1_hakovm_internal_block0_canary_vm (rc=$rc, expect 13)" >&2; exit 1 +