feat: comprehensive development progress
- Pattern matching implementation extended in match_expr.rs - CLI configuration structured with categorized groups (task recommendation completed) - Python LLVM builder split into function_lower.py (task recommendation completed) - parse_box_declaration massive function refactored (task recommendation completed) - Phase 16 Macro Revolution comprehensive planning and documentation - Archive legacy phase documentation for clean structure - HTTP message box improvements and performance optimizations - MIR builder enhancements and control flow improvements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
16
AGENTS.md
16
AGENTS.md
@ -214,6 +214,22 @@ Flags
|
|||||||
- Optional logs: enable `NYASH_CLI_VERBOSE=1` for detailed emit diagnostics.
|
- Optional logs: enable `NYASH_CLI_VERBOSE=1` for detailed emit diagnostics.
|
||||||
- LLVM harness safety valve (dev only): set `NYASH_LLVM_SANITIZE_EMPTY_PHI=1` to drop malformed empty PHI lines from IR before llvmlite parses it. Keep OFF for normal runs; use only to unblock bring-up when `finalize_phis` is being debugged.
|
- LLVM harness safety valve (dev only): set `NYASH_LLVM_SANITIZE_EMPTY_PHI=1` to drop malformed empty PHI lines from IR before llvmlite parses it. Keep OFF for normal runs; use only to unblock bring-up when `finalize_phis` is being debugged.
|
||||||
|
|
||||||
|
### LLVM Python Builder Layout (after split)
|
||||||
|
- Files (under `src/llvm_py/`):
|
||||||
|
- `llvm_builder.py`: top-level orchestration; delegates to builders.
|
||||||
|
- `builders/entry.py`: `ensure_ny_main(builder)` – create ny_main wrapper if needed.
|
||||||
|
- `builders/function_lower.py`: `lower_function(builder, func_json)` – per-function lowering (CFG, PHI metadata, loop prepass, finalize_phis).
|
||||||
|
- `builders/block_lower.py`: `lower_blocks(builder, func, block_by_id, order, loop_plan)` – block-local lowering and snapshots.
|
||||||
|
- `builders/instruction_lower.py`: `lower_instruction(owner, builder, inst, func)` – per-instruction dispatch.
|
||||||
|
- Dev toggles:
|
||||||
|
- `NYASH_LLVM_DUMP_IR=<path>` – dump IR text for inspection.
|
||||||
|
- `NYASH_LLVM_PREPASS_IFMERGE=1` – enable return-merge PHI predeclare metadata.
|
||||||
|
- `NYASH_LLVM_PREPASS_LOOP=1` – enable simple while prepass (loopform synthesis).
|
||||||
|
- `NYASH_CLI_VERBOSE=1` – extra trace from builder.
|
||||||
|
- Smokes:
|
||||||
|
- Empty PHI guard: `tools/test/smoke/llvm/ir_phi_empty_check.sh <file.nyash>`
|
||||||
|
- Batch run: `tools/test/smoke/llvm/ir_phi_empty_check_all.sh`
|
||||||
|
|
||||||
## Codex Async Workflow (Background Jobs)
|
## Codex Async Workflow (Background Jobs)
|
||||||
- Purpose: run Codex tasks in the background and notify a tmux session on completion.
|
- Purpose: run Codex tasks in the background and notify a tmux session on completion.
|
||||||
- Script: `tools/codex-async-notify.sh`
|
- Script: `tools/codex-async-notify.sh`
|
||||||
|
|||||||
@ -18,6 +18,17 @@ Delivered
|
|||||||
- Docs
|
- Docs
|
||||||
- AGENTS.md: Add LLVM/PHI invariants + debug flow; match guard policy; harness build/run steps.
|
- AGENTS.md: Add LLVM/PHI invariants + debug flow; match guard policy; harness build/run steps.
|
||||||
|
|
||||||
|
Minor Polish (2025-09-19 evening)
|
||||||
|
- Rust warnings cleanup
|
||||||
|
- Remove unused Effect/EffectMask import in `src/mir/optimizer.rs:11`.
|
||||||
|
- Silence dead_code on helper `collect_free_vars` and TLS helper `with_current_vm_mut` (kept for future use).
|
||||||
|
- Fix duplicate `groups` binding in `src/runner/mod.rs`.
|
||||||
|
- Drop unnecessary `mut` bindings in `src/boxes/http_message_box.rs` quick creators.
|
||||||
|
- Parser hygiene
|
||||||
|
- Make `MatchArm::Default` a unit variant (no unused payload); logic unchanged.
|
||||||
|
- Build check
|
||||||
|
- `cargo check` clean (no warnings) on default feature set; LLVM path unchanged.
|
||||||
|
|
||||||
Refactor Progress (2025-09-19, noon)
|
Refactor Progress (2025-09-19, noon)
|
||||||
- Parser/Box Definition
|
- Parser/Box Definition
|
||||||
- Extracted and integrated parse_unified_member_block_first (block-first unified members) from parse_box_declaration.
|
- Extracted and integrated parse_unified_member_block_first (block-first unified members) from parse_box_declaration.
|
||||||
@ -55,6 +66,15 @@ CLI Refactor — Phase A (non‑breaking)
|
|||||||
Python LLVM Builder Split — Phase A (incremental)
|
Python LLVM Builder Split — Phase A (incremental)
|
||||||
- Extracted entry wrapper creation to `src/llvm_py/builders/entry.py::ensure_ny_main(builder)` and delegated from `build_from_mir`.
|
- Extracted entry wrapper creation to `src/llvm_py/builders/entry.py::ensure_ny_main(builder)` and delegated from `build_from_mir`.
|
||||||
- Added scaffolding for function lowering split: `src/llvm_py/builders/function_lower.py` (not yet wired for all paths).
|
- Added scaffolding for function lowering split: `src/llvm_py/builders/function_lower.py` (not yet wired for all paths).
|
||||||
|
|
||||||
|
Python LLVM Builder Split — Phase B/C (complete delegation)
|
||||||
|
- Function-level delegation is now default:
|
||||||
|
- `llvm_builder.py.lower_function` delegates to `builders/function_lower.py` by default (fallback only on exception).
|
||||||
|
- Blocks are lowered via `builders/block_lower.py::lower_blocks`.
|
||||||
|
- Instructions are lowered via `builders/instruction_lower.py::lower_instruction`.
|
||||||
|
- Developer flow:
|
||||||
|
- Use `NYASH_LLVM_DUMP_IR` to inspect IR and `tools/test/smoke/llvm/ir_phi_empty_check.sh` to assert there are no empty PHIs.
|
||||||
|
- Optional: `NYASH_LLVM_PREPASS_IFMERGE=1` and `NYASH_LLVM_PREPASS_LOOP=1` to test prepasses.
|
||||||
Refactor Plan (next 1–2 weeks)
|
Refactor Plan (next 1–2 weeks)
|
||||||
1) Split parse_box_declaration (667 lines) in src/parser/declarations/box_definition.rs
|
1) Split parse_box_declaration (667 lines) in src/parser/declarations/box_definition.rs
|
||||||
- Targets (line ranges are indicative):
|
- Targets (line ranges are indicative):
|
||||||
|
|||||||
@ -351,7 +351,7 @@ impl HTTPResponseBox {
|
|||||||
|
|
||||||
/// Quick HTML response creation
|
/// Quick HTML response creation
|
||||||
pub fn create_html_response(content: Box<dyn NyashBox>) -> Self {
|
pub fn create_html_response(content: Box<dyn NyashBox>) -> Self {
|
||||||
let mut response = HTTPResponseBox::new();
|
let response = HTTPResponseBox::new();
|
||||||
*response.status_code.lock().unwrap() = 200;
|
*response.status_code.lock().unwrap() = 200;
|
||||||
*response.status_message.lock().unwrap() = "OK".to_string();
|
*response.status_message.lock().unwrap() = "OK".to_string();
|
||||||
response.headers.lock().unwrap().insert(
|
response.headers.lock().unwrap().insert(
|
||||||
@ -364,7 +364,7 @@ impl HTTPResponseBox {
|
|||||||
|
|
||||||
/// Quick JSON response creation
|
/// Quick JSON response creation
|
||||||
pub fn create_json_response(content: Box<dyn NyashBox>) -> Self {
|
pub fn create_json_response(content: Box<dyn NyashBox>) -> Self {
|
||||||
let mut response = HTTPResponseBox::new();
|
let response = HTTPResponseBox::new();
|
||||||
*response.status_code.lock().unwrap() = 200;
|
*response.status_code.lock().unwrap() = 200;
|
||||||
*response.status_message.lock().unwrap() = "OK".to_string();
|
*response.status_message.lock().unwrap() = "OK".to_string();
|
||||||
response.headers.lock().unwrap().insert(
|
response.headers.lock().unwrap().insert(
|
||||||
@ -377,7 +377,7 @@ impl HTTPResponseBox {
|
|||||||
|
|
||||||
/// Quick 404 response creation
|
/// Quick 404 response creation
|
||||||
pub fn create_404_response() -> Self {
|
pub fn create_404_response() -> Self {
|
||||||
let mut response = HTTPResponseBox::new();
|
let response = HTTPResponseBox::new();
|
||||||
*response.status_code.lock().unwrap() = 404;
|
*response.status_code.lock().unwrap() = 404;
|
||||||
*response.status_message.lock().unwrap() = "Not Found".to_string();
|
*response.status_message.lock().unwrap() = "Not Found".to_string();
|
||||||
response.headers.lock().unwrap().insert(
|
response.headers.lock().unwrap().insert(
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
use clap::{Arg, ArgMatches, Command};
|
use clap::{Arg, ArgMatches, Command};
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::fmt::Debug as _; // for derive Debug consistency
|
|
||||||
|
|
||||||
/// Command-line configuration structure
|
/// Command-line configuration structure
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@ -9,7 +9,9 @@ use std::sync::Arc;
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct JitEngine {
|
pub struct JitEngine {
|
||||||
// In the future: isa, module, context, fn table, etc.
|
// In the future: isa, module, context, fn table, etc.
|
||||||
|
#[allow(dead_code)]
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
|
#[allow(dead_code)]
|
||||||
next_handle: u64,
|
next_handle: u64,
|
||||||
/// Stub function table: handle -> callable closure
|
/// Stub function table: handle -> callable closure
|
||||||
fntab: HashMap<
|
fntab: HashMap<
|
||||||
|
|||||||
243
src/llvm_py/builders/block_lower.py
Normal file
243
src/llvm_py/builders/block_lower.py
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
from typing import Dict, Any, List
|
||||||
|
from llvmlite import ir
|
||||||
|
from trace import debug as trace_debug
|
||||||
|
|
||||||
|
|
||||||
|
def lower_blocks(builder, func: ir.Function, block_by_id: Dict[int, Dict[str, Any]], order: List[int], loop_plan: Dict[str, Any] | None):
|
||||||
|
skipped: set[int] = set()
|
||||||
|
if loop_plan is not None:
|
||||||
|
try:
|
||||||
|
for bskip in loop_plan.get('skip_blocks', []):
|
||||||
|
if bskip != loop_plan.get('header'):
|
||||||
|
skipped.add(int(bskip))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for bid in order:
|
||||||
|
block_data = block_by_id.get(bid)
|
||||||
|
if block_data is None:
|
||||||
|
continue
|
||||||
|
# If loop prepass applies, lower while once at header and skip loop-internal blocks
|
||||||
|
if loop_plan is not None and bid == loop_plan.get('header'):
|
||||||
|
bb = builder.bb_map[bid]
|
||||||
|
ib = ir.IRBuilder(bb)
|
||||||
|
try:
|
||||||
|
builder.resolver.builder = ib
|
||||||
|
builder.resolver.module = builder.module
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
builder.loop_count += 1
|
||||||
|
body_insts = loop_plan.get('body_insts', [])
|
||||||
|
cond_vid = loop_plan.get('cond')
|
||||||
|
from instructions.loopform import lower_while_loopform
|
||||||
|
ok = False
|
||||||
|
try:
|
||||||
|
builder._current_vmap = dict(builder.vmap)
|
||||||
|
ok = lower_while_loopform(
|
||||||
|
ib,
|
||||||
|
func,
|
||||||
|
cond_vid,
|
||||||
|
body_insts,
|
||||||
|
builder.loop_count,
|
||||||
|
builder.vmap,
|
||||||
|
builder.bb_map,
|
||||||
|
builder.resolver,
|
||||||
|
builder.preds,
|
||||||
|
builder.block_end_values,
|
||||||
|
getattr(builder, 'ctx', None),
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
ok = False
|
||||||
|
if not ok:
|
||||||
|
try:
|
||||||
|
builder.resolver._owner_lower_instruction = builder.lower_instruction
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
from instructions.controlflow.while_ import lower_while_regular
|
||||||
|
lower_while_regular(ib, func, cond_vid, body_insts,
|
||||||
|
builder.loop_count, builder.vmap, builder.bb_map,
|
||||||
|
builder.resolver, builder.preds, builder.block_end_values)
|
||||||
|
try:
|
||||||
|
delattr(builder, '_current_vmap')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for bskip in loop_plan.get('skip_blocks', []):
|
||||||
|
skipped.add(bskip)
|
||||||
|
# Ensure skipped original blocks have a valid terminator: branch to while exit
|
||||||
|
try:
|
||||||
|
exit_name = f"while{builder.loop_count}_exit"
|
||||||
|
exit_bb = None
|
||||||
|
for bbf in func.blocks:
|
||||||
|
try:
|
||||||
|
if str(bbf.name) == exit_name:
|
||||||
|
exit_bb = bbf
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if exit_bb is not None:
|
||||||
|
try:
|
||||||
|
orig_exit_bb = builder.bb_map.get(loop_plan.get('exit'))
|
||||||
|
if orig_exit_bb is not None and exit_bb.terminator is None:
|
||||||
|
ibx = ir.IRBuilder(exit_bb)
|
||||||
|
ibx.branch(orig_exit_bb)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for bskip in loop_plan.get('skip_blocks', []):
|
||||||
|
if bskip == loop_plan.get('header'):
|
||||||
|
continue
|
||||||
|
bb_skip = builder.bb_map.get(bskip)
|
||||||
|
if bb_skip is None:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
if bb_skip.terminator is None:
|
||||||
|
ib = ir.IRBuilder(bb_skip)
|
||||||
|
if orig_exit_bb is not None:
|
||||||
|
ib.branch(orig_exit_bb)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
continue
|
||||||
|
|
||||||
|
if bid in skipped:
|
||||||
|
continue
|
||||||
|
bb = builder.bb_map[bid]
|
||||||
|
ib = ir.IRBuilder(bb)
|
||||||
|
try:
|
||||||
|
builder.resolver.builder = ib
|
||||||
|
builder.resolver.module = builder.module
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
block_data = block_by_id.get(bid, {})
|
||||||
|
insts = block_data.get('instructions', []) or []
|
||||||
|
# Split into body and terminator ops
|
||||||
|
body_ops: List[Dict[str, Any]] = []
|
||||||
|
term_ops: List[Dict[str, Any]] = []
|
||||||
|
for inst in insts:
|
||||||
|
try:
|
||||||
|
opx = inst.get('op')
|
||||||
|
except Exception:
|
||||||
|
opx = None
|
||||||
|
if opx in ("ret","jump","branch"):
|
||||||
|
term_ops.append(inst)
|
||||||
|
elif opx == "phi":
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
body_ops.append(inst)
|
||||||
|
# Per-block SSA map
|
||||||
|
vmap_cur: Dict[int, ir.Value] = {}
|
||||||
|
try:
|
||||||
|
for _vid, _val in (builder.vmap or {}).items():
|
||||||
|
keep = True
|
||||||
|
try:
|
||||||
|
if hasattr(_val, 'add_incoming'):
|
||||||
|
bb_of = getattr(getattr(_val, 'basic_block', None), 'name', None)
|
||||||
|
keep = (bb_of == bb.name)
|
||||||
|
except Exception:
|
||||||
|
keep = False
|
||||||
|
if keep:
|
||||||
|
vmap_cur[_vid] = _val
|
||||||
|
except Exception:
|
||||||
|
vmap_cur = dict(builder.vmap)
|
||||||
|
builder._current_vmap = vmap_cur
|
||||||
|
created_ids: List[int] = []
|
||||||
|
defined_here_all: set = set()
|
||||||
|
for _inst in body_ops:
|
||||||
|
try:
|
||||||
|
d = _inst.get('dst')
|
||||||
|
if isinstance(d, int):
|
||||||
|
defined_here_all.add(d)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# Lower body ops
|
||||||
|
for i_idx, inst in enumerate(body_ops):
|
||||||
|
try:
|
||||||
|
trace_debug(f"[llvm-py] body op: {inst.get('op')} dst={inst.get('dst')} cond={inst.get('cond')}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if bb.terminator is not None:
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
ib.position_at_end(bb)
|
||||||
|
if inst.get('op') == 'copy':
|
||||||
|
src_i = inst.get('src')
|
||||||
|
skip_now = False
|
||||||
|
if isinstance(src_i, int):
|
||||||
|
try:
|
||||||
|
for _rest in body_ops[i_idx+1:]:
|
||||||
|
try:
|
||||||
|
if int(_rest.get('dst')) == int(src_i):
|
||||||
|
skip_now = True
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if skip_now:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
builder.lower_instruction(ib, inst, func)
|
||||||
|
else:
|
||||||
|
builder.lower_instruction(ib, inst, func)
|
||||||
|
try:
|
||||||
|
dst = inst.get("dst")
|
||||||
|
if isinstance(dst, int):
|
||||||
|
if dst in builder.vmap:
|
||||||
|
_gval = builder.vmap[dst]
|
||||||
|
try:
|
||||||
|
if hasattr(_gval, 'add_incoming'):
|
||||||
|
bb_of = getattr(getattr(_gval, 'basic_block', None), 'name', None)
|
||||||
|
if bb_of == bb.name:
|
||||||
|
vmap_cur[dst] = _gval
|
||||||
|
else:
|
||||||
|
vmap_cur[dst] = _gval
|
||||||
|
except Exception:
|
||||||
|
vmap_cur[dst] = _gval
|
||||||
|
if dst not in created_ids and dst in vmap_cur:
|
||||||
|
created_ids.append(dst)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# Lower terminators
|
||||||
|
for inst in term_ops:
|
||||||
|
try:
|
||||||
|
trace_debug(f"[llvm-py] term op: {inst.get('op')} dst={inst.get('dst')} cond={inst.get('cond')}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if bb.terminator is not None:
|
||||||
|
break
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
ib.position_at_end(bb)
|
||||||
|
builder.lower_instruction(ib, inst, func)
|
||||||
|
try:
|
||||||
|
for vid in created_ids:
|
||||||
|
val = vmap_cur.get(vid)
|
||||||
|
if val is not None and hasattr(val, 'add_incoming'):
|
||||||
|
try:
|
||||||
|
builder.vmap[vid] = val
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# End-of-block snapshot
|
||||||
|
snap = dict(vmap_cur)
|
||||||
|
try:
|
||||||
|
keys = sorted(list(snap.keys()))
|
||||||
|
from phi_wiring.common import trace as trace_phi_json
|
||||||
|
try:
|
||||||
|
trace_phi_json({"phi": "snapshot", "block": int(bid), "keys": [int(k) for k in keys[:20]]})
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
for vid in created_ids:
|
||||||
|
if vid in vmap_cur:
|
||||||
|
builder.def_blocks.setdefault(vid, set()).add(block_data.get("id", 0))
|
||||||
|
builder.block_end_values[bid] = snap
|
||||||
|
try:
|
||||||
|
delattr(builder, '_current_vmap')
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
@ -1,84 +1,257 @@
|
|||||||
from typing import Dict, Any
|
from typing import Dict, Any, List
|
||||||
|
|
||||||
|
from llvmlite import ir
|
||||||
|
from trace import debug as trace_debug
|
||||||
|
from prepass.if_merge import plan_ret_phi_predeclare
|
||||||
|
from prepass.loops import detect_simple_while
|
||||||
|
from phi_wiring import setup_phi_placeholders as _setup_phi_placeholders, finalize_phis as _finalize_phis
|
||||||
|
|
||||||
|
|
||||||
def lower_function(builder, func_data: Dict[str, Any]):
|
def lower_function(builder, func_data: Dict[str, Any]):
|
||||||
"""Lower a single MIR function to LLVM IR using the given builder context.
|
"""Lower a single MIR function to LLVM IR using the given builder context.
|
||||||
|
This is a faithful extraction of NyashLLVMBuilder.lower_function.
|
||||||
This helper is a thin wrapper that delegates to the NyashLLVMBuilder's
|
|
||||||
existing methods/attributes, enabling gradual file decomposition without
|
|
||||||
changing semantics.
|
|
||||||
"""
|
"""
|
||||||
|
import os, re
|
||||||
|
|
||||||
name = func_data.get("name", "unknown")
|
name = func_data.get("name", "unknown")
|
||||||
builder.current_function_name = name
|
builder.current_function_name = name
|
||||||
|
|
||||||
import re
|
|
||||||
params = func_data.get("params", [])
|
params = func_data.get("params", [])
|
||||||
blocks = func_data.get("blocks", [])
|
blocks = func_data.get("blocks", [])
|
||||||
|
|
||||||
# Determine function signature
|
# Determine function signature
|
||||||
if name == "ny_main":
|
if name == "ny_main":
|
||||||
func_ty = builder.i32.func_type([])
|
# Special case: ny_main returns i32
|
||||||
|
func_ty = ir.FunctionType(builder.i32, [])
|
||||||
else:
|
else:
|
||||||
|
# Default: i64(i64, ...) signature; derive arity from '/N' suffix when params missing
|
||||||
m = re.search(r"/(\d+)$", name)
|
m = re.search(r"/(\d+)$", name)
|
||||||
arity = int(m.group(1)) if m else len(params)
|
arity = int(m.group(1)) if m else len(params)
|
||||||
param_types = [builder.i64] * arity
|
param_types = [builder.i64] * arity
|
||||||
func_ty = builder.i64.func_type(param_types)
|
func_ty = ir.FunctionType(builder.i64, param_types)
|
||||||
|
|
||||||
|
# Reset per-function maps and resolver caches to avoid cross-function collisions
|
||||||
try:
|
try:
|
||||||
builder.vmap.clear()
|
builder.vmap.clear()
|
||||||
except Exception:
|
except Exception:
|
||||||
builder.vmap = {}
|
builder.vmap = {}
|
||||||
|
try:
|
||||||
|
builder.bb_map.clear()
|
||||||
|
except Exception:
|
||||||
builder.bb_map = {}
|
builder.bb_map = {}
|
||||||
builder.preds = {}
|
try:
|
||||||
builder.block_end_values = {}
|
# Reset resolver caches keyed by block names
|
||||||
builder.def_blocks = {}
|
builder.resolver.i64_cache.clear()
|
||||||
builder.predeclared_ret_phis = {}
|
builder.resolver.ptr_cache.clear()
|
||||||
|
builder.resolver.f64_cache.clear()
|
||||||
|
if hasattr(builder.resolver, '_end_i64_cache'):
|
||||||
|
builder.resolver._end_i64_cache.clear()
|
||||||
|
if hasattr(builder.resolver, 'string_ids'):
|
||||||
|
builder.resolver.string_ids.clear()
|
||||||
|
if hasattr(builder.resolver, 'string_literals'):
|
||||||
|
builder.resolver.string_literals.clear()
|
||||||
|
if hasattr(builder.resolver, 'string_ptrs'):
|
||||||
|
builder.resolver.string_ptrs.clear()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Ensure function exists or create one
|
# Create or reuse function
|
||||||
fn = None
|
func = None
|
||||||
for f in builder.module.functions:
|
for f in builder.module.functions:
|
||||||
if f.name == name:
|
if f.name == name:
|
||||||
fn = f
|
func = f
|
||||||
break
|
break
|
||||||
if fn is None:
|
if func is None:
|
||||||
from llvmlite import ir
|
func = ir.Function(builder.module, func_ty, name=name)
|
||||||
fn = ir.Function(builder.module, func_ty, name=name)
|
|
||||||
|
|
||||||
# Create all basic blocks first
|
# Map parameters to vmap (value_id: 0..arity-1)
|
||||||
from llvmlite import ir
|
try:
|
||||||
block_by_id = {}
|
arity = len(func.args)
|
||||||
for b in blocks:
|
for i in range(arity):
|
||||||
bbid = int(b.get("id", 0))
|
builder.vmap[i] = func.args[i]
|
||||||
bb = fn.append_basic_block(name=f"bb{bbid}")
|
except Exception:
|
||||||
block_by_id[bbid] = bb
|
pass
|
||||||
builder.bb_map[bbid] = bb
|
|
||||||
|
|
||||||
# Predeclare ret PHIs if needed (if-merge prepass)
|
# Build predecessor map from control-flow edges
|
||||||
from prepass.if_merge import plan_ret_phi_predeclare
|
builder.preds = {}
|
||||||
|
for block_data in blocks:
|
||||||
|
bid = block_data.get("id", 0)
|
||||||
|
builder.preds.setdefault(bid, [])
|
||||||
|
for block_data in blocks:
|
||||||
|
src = block_data.get("id", 0)
|
||||||
|
for inst in block_data.get("instructions", []):
|
||||||
|
op = inst.get("op")
|
||||||
|
if op == "jump":
|
||||||
|
t = inst.get("target")
|
||||||
|
if t is not None:
|
||||||
|
builder.preds.setdefault(t, []).append(src)
|
||||||
|
elif op == "branch":
|
||||||
|
th = inst.get("then")
|
||||||
|
el = inst.get("else")
|
||||||
|
if th is not None:
|
||||||
|
builder.preds.setdefault(th, []).append(src)
|
||||||
|
if el is not None:
|
||||||
|
builder.preds.setdefault(el, []).append(src)
|
||||||
|
|
||||||
|
# Create all blocks first
|
||||||
|
for block_data in blocks:
|
||||||
|
bid = block_data.get("id", 0)
|
||||||
|
block_name = f"bb{bid}"
|
||||||
|
bb = func.append_basic_block(block_name)
|
||||||
|
builder.bb_map[bid] = bb
|
||||||
|
|
||||||
|
# Build quick lookup for blocks by id
|
||||||
|
block_by_id: Dict[int, Dict[str, Any]] = {}
|
||||||
|
for block_data in blocks:
|
||||||
|
block_by_id[block_data.get("id", 0)] = block_data
|
||||||
|
|
||||||
|
# Determine entry block: first with no predecessors; fallback to first block
|
||||||
|
entry_bid = None
|
||||||
|
for bid, preds in builder.preds.items():
|
||||||
|
if len(preds) == 0:
|
||||||
|
entry_bid = bid
|
||||||
|
break
|
||||||
|
if entry_bid is None and blocks:
|
||||||
|
entry_bid = blocks[0].get("id", 0)
|
||||||
|
|
||||||
|
# Compute approx preds-first order
|
||||||
|
visited = set()
|
||||||
|
order: List[int] = []
|
||||||
|
|
||||||
|
def visit(bid: int):
|
||||||
|
if bid in visited:
|
||||||
|
return
|
||||||
|
visited.add(bid)
|
||||||
|
for p in builder.preds.get(bid, []):
|
||||||
|
visit(p)
|
||||||
|
order.append(bid)
|
||||||
|
|
||||||
|
if entry_bid is not None:
|
||||||
|
visit(entry_bid)
|
||||||
|
for bid in block_by_id.keys():
|
||||||
|
if bid not in visited:
|
||||||
|
visit(bid)
|
||||||
|
|
||||||
|
# Prepass: collect PHI metadata and placeholders
|
||||||
|
_setup_phi_placeholders(builder, blocks)
|
||||||
|
|
||||||
|
# Optional: if-merge prepass (gate NYASH_LLVM_PREPASS_IFMERGE)
|
||||||
|
try:
|
||||||
|
if os.environ.get('NYASH_LLVM_PREPASS_IFMERGE') == '1':
|
||||||
plan = plan_ret_phi_predeclare(block_by_id)
|
plan = plan_ret_phi_predeclare(block_by_id)
|
||||||
if plan:
|
if plan:
|
||||||
if not hasattr(builder, 'block_phi_incomings') or builder.block_phi_incomings is None:
|
if not hasattr(builder, 'block_phi_incomings') or builder.block_phi_incomings is None:
|
||||||
builder.block_phi_incomings = {}
|
builder.block_phi_incomings = {}
|
||||||
for (bbid, pairs) in plan.items():
|
for bbid, ret_vid in plan.items():
|
||||||
for (ret_vid, preds_list) in pairs.items():
|
try:
|
||||||
builder.block_phi_incomings.setdefault(int(bbid), {}).setdefault(int(ret_vid), [])
|
preds_raw = [p for p in builder.preds.get(bbid, []) if p != bbid]
|
||||||
builder.block_phi_incomings[int(bbid)][int(ret_vid)] = [(int(p), int(ret_vid)) for p in preds_list]
|
except Exception:
|
||||||
|
preds_raw = []
|
||||||
|
seen = set(); preds_list = []
|
||||||
|
for p in preds_raw:
|
||||||
|
if p not in seen:
|
||||||
|
preds_list.append(p); seen.add(p)
|
||||||
|
try:
|
||||||
|
builder.block_phi_incomings.setdefault(int(bbid), {})[int(ret_vid)] = [
|
||||||
|
(int(p), int(ret_vid)) for p in preds_list
|
||||||
|
]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
trace_debug(f"[prepass] if-merge: plan metadata at bb{bbid} for v{ret_vid} preds={preds_list}")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Lower instructions per block
|
# Predeclare PHIs for used-in-block values defined in predecessors (multi-pred only)
|
||||||
from llvmlite.ir import IRBuilder
|
try:
|
||||||
from instructions import dispatcher # if exists; else inline lowerers
|
from cfg.utils import build_preds_succs
|
||||||
for b in blocks:
|
local_preds, _ = build_preds_succs(block_by_id)
|
||||||
bbid = int(b.get("id", 0))
|
def _collect_defs(block):
|
||||||
bb = block_by_id[bbid]
|
defs = set()
|
||||||
builder_bb = IRBuilder(bb)
|
for ins in block.get('instructions') or []:
|
||||||
builder.resolver.attach_function_and_block(fn, bb)
|
try:
|
||||||
insts = b.get("insts", [])
|
dstv = ins.get('dst')
|
||||||
for inst in insts:
|
if isinstance(dstv, int):
|
||||||
op = inst.get("op")
|
defs.add(int(dstv))
|
||||||
# Delegate to existing NyashLLVMBuilder method for now
|
except Exception:
|
||||||
builder.lower_instruction(op, inst, builder_bb)
|
pass
|
||||||
|
return defs
|
||||||
|
def _collect_uses(block):
|
||||||
|
uses = set()
|
||||||
|
for ins in block.get('instructions') or []:
|
||||||
|
for k in ('lhs','rhs','value','cond','box_val'):
|
||||||
|
try:
|
||||||
|
v = ins.get(k)
|
||||||
|
if isinstance(v, int):
|
||||||
|
uses.add(int(v))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return uses
|
||||||
|
if not hasattr(builder, 'block_phi_incomings') or builder.block_phi_incomings is None:
|
||||||
|
builder.block_phi_incomings = {}
|
||||||
|
for bid, blk in block_by_id.items():
|
||||||
|
try:
|
||||||
|
preds_raw = [p for p in local_preds.get(int(bid), []) if p != int(bid)]
|
||||||
|
except Exception:
|
||||||
|
preds_raw = []
|
||||||
|
seen = set(); preds_list = []
|
||||||
|
for p in preds_raw:
|
||||||
|
if p not in seen:
|
||||||
|
preds_list.append(p); seen.add(p)
|
||||||
|
if len(preds_list) <= 1:
|
||||||
|
continue
|
||||||
|
defs = _collect_defs(blk)
|
||||||
|
uses = _collect_uses(blk)
|
||||||
|
need = [u for u in uses if u not in defs]
|
||||||
|
if not need:
|
||||||
|
continue
|
||||||
|
for vid in need:
|
||||||
|
try:
|
||||||
|
builder.block_phi_incomings.setdefault(int(bid), {}).setdefault(int(vid), [])
|
||||||
|
builder.block_phi_incomings[int(bid)][int(vid)] = [(int(p), int(vid)) for p in preds_list]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
builder.resolver.block_phi_incomings = builder.block_phi_incomings
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# Finalize PHIs after the function is fully lowered
|
# Optional: simple loop prepass
|
||||||
from phi_wiring import finalize_phis as _finalize_phis
|
loop_plan = None
|
||||||
|
try:
|
||||||
|
if os.environ.get('NYASH_LLVM_PREPASS_LOOP') == '1':
|
||||||
|
loop_plan = detect_simple_while(block_by_id)
|
||||||
|
if loop_plan is not None:
|
||||||
|
trace_debug(f"[prepass] detect loop header=bb{loop_plan['header']} then=bb{loop_plan['then']} latch=bb{loop_plan['latch']} exit=bb{loop_plan['exit']}")
|
||||||
|
except Exception:
|
||||||
|
loop_plan = None
|
||||||
|
|
||||||
|
from builders.block_lower import lower_blocks as _lower_blocks
|
||||||
|
_lower_blocks(builder, func, block_by_id, order, loop_plan)
|
||||||
|
|
||||||
|
# Optional: capture lowering ctx for downstream helpers
|
||||||
|
try:
|
||||||
|
builder.ctx = dict(
|
||||||
|
module=builder.module,
|
||||||
|
i64=builder.i64,
|
||||||
|
i32=builder.i32,
|
||||||
|
i8=builder.i8,
|
||||||
|
i1=builder.i1,
|
||||||
|
i8p=builder.i8p,
|
||||||
|
vmap=builder.vmap,
|
||||||
|
bb_map=builder.bb_map,
|
||||||
|
preds=builder.preds,
|
||||||
|
block_end_values=builder.block_end_values,
|
||||||
|
resolver=builder.resolver,
|
||||||
|
trace_phi=os.environ.get('NYASH_LLVM_TRACE_PHI') == '1',
|
||||||
|
verbose=os.environ.get('NYASH_CLI_VERBOSE') == '1',
|
||||||
|
)
|
||||||
|
builder.resolver.ctx = builder.ctx
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Finalize PHIs for this function
|
||||||
_finalize_phis(builder)
|
_finalize_phis(builder)
|
||||||
|
|
||||||
|
|||||||
179
src/llvm_py/builders/instruction_lower.py
Normal file
179
src/llvm_py/builders/instruction_lower.py
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
from typing import Dict, Any
|
||||||
|
from llvmlite import ir
|
||||||
|
from trace import debug as trace_debug
|
||||||
|
|
||||||
|
# Import instruction handlers
|
||||||
|
from instructions.const import lower_const
|
||||||
|
from instructions.binop import lower_binop
|
||||||
|
from instructions.compare import lower_compare
|
||||||
|
from instructions.controlflow.jump import lower_jump
|
||||||
|
from instructions.controlflow.branch import lower_branch
|
||||||
|
from instructions.ret import lower_return
|
||||||
|
from instructions.copy import lower_copy
|
||||||
|
from instructions.call import lower_call
|
||||||
|
from instructions.boxcall import lower_boxcall
|
||||||
|
from instructions.externcall import lower_externcall
|
||||||
|
from instructions.typeop import lower_typeop
|
||||||
|
from instructions.newbox import lower_newbox
|
||||||
|
from instructions.safepoint import lower_safepoint
|
||||||
|
from instructions.barrier import lower_barrier
|
||||||
|
from instructions.loopform import lower_while_loopform
|
||||||
|
from instructions.controlflow.while_ import lower_while_regular
|
||||||
|
|
||||||
|
|
||||||
|
def lower_instruction(owner, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
|
||||||
|
"""Dispatch a single MIR instruction to appropriate lowering helper.
|
||||||
|
|
||||||
|
owner is the NyashLLVMBuilder instance to access module, resolver, maps, and ctx.
|
||||||
|
"""
|
||||||
|
op = inst.get("op")
|
||||||
|
# Pick current vmap context (per-block context during lowering)
|
||||||
|
vmap_ctx = getattr(owner, '_current_vmap', owner.vmap)
|
||||||
|
|
||||||
|
if op == "const":
|
||||||
|
dst = inst.get("dst")
|
||||||
|
value = inst.get("value")
|
||||||
|
lower_const(builder, owner.module, dst, value, vmap_ctx, owner.resolver)
|
||||||
|
|
||||||
|
elif op == "binop":
|
||||||
|
operation = inst.get("operation")
|
||||||
|
lhs = inst.get("lhs")
|
||||||
|
rhs = inst.get("rhs")
|
||||||
|
dst = inst.get("dst")
|
||||||
|
dst_type = inst.get("dst_type")
|
||||||
|
lower_binop(builder, owner.resolver, operation, lhs, rhs, dst,
|
||||||
|
vmap_ctx, builder.block, owner.preds, owner.block_end_values, owner.bb_map,
|
||||||
|
dst_type=dst_type)
|
||||||
|
|
||||||
|
elif op == "jump":
|
||||||
|
target = inst.get("target")
|
||||||
|
lower_jump(builder, target, owner.bb_map)
|
||||||
|
|
||||||
|
elif op == "copy":
|
||||||
|
dst = inst.get("dst")
|
||||||
|
src = inst.get("src")
|
||||||
|
lower_copy(builder, dst, src, vmap_ctx, owner.resolver, builder.block, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "branch":
|
||||||
|
cond = inst.get("cond")
|
||||||
|
then_bid = inst.get("then")
|
||||||
|
else_bid = inst.get("else")
|
||||||
|
lower_branch(builder, cond, then_bid, else_bid, vmap_ctx, owner.bb_map, owner.resolver, owner.preds, owner.block_end_values)
|
||||||
|
|
||||||
|
elif op == "ret":
|
||||||
|
value = inst.get("value")
|
||||||
|
lower_return(builder, value, vmap_ctx, func.function_type.return_type,
|
||||||
|
owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "phi":
|
||||||
|
# No-op here: PHIはメタのみ(resolverがon‑demand生成)
|
||||||
|
return
|
||||||
|
|
||||||
|
elif op == "compare":
|
||||||
|
# Dedicated compare op
|
||||||
|
operation = inst.get("operation") or inst.get("op")
|
||||||
|
lhs = inst.get("lhs")
|
||||||
|
rhs = inst.get("rhs")
|
||||||
|
dst = inst.get("dst")
|
||||||
|
cmp_kind = inst.get("cmp_kind")
|
||||||
|
lower_compare(builder, operation, lhs, rhs, dst, vmap_ctx,
|
||||||
|
owner.resolver, builder.block, owner.preds, owner.block_end_values, owner.bb_map,
|
||||||
|
meta={"cmp_kind": cmp_kind} if cmp_kind else None,
|
||||||
|
ctx=getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "call":
|
||||||
|
func_name = inst.get("func")
|
||||||
|
args = inst.get("args", [])
|
||||||
|
dst = inst.get("dst")
|
||||||
|
lower_call(builder, owner.module, func_name, args, dst, vmap_ctx, owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "boxcall":
|
||||||
|
box_vid = inst.get("box")
|
||||||
|
method = inst.get("method")
|
||||||
|
args = inst.get("args", [])
|
||||||
|
dst = inst.get("dst")
|
||||||
|
lower_boxcall(builder, owner.module, box_vid, method, args, dst,
|
||||||
|
vmap_ctx, owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
|
||||||
|
# Optional: honor explicit dst_type for tagging (string handle)
|
||||||
|
try:
|
||||||
|
dst_type = inst.get("dst_type")
|
||||||
|
if dst is not None and isinstance(dst_type, dict):
|
||||||
|
if dst_type.get("kind") == "handle" and dst_type.get("box_type") == "StringBox":
|
||||||
|
if hasattr(owner.resolver, 'mark_string'):
|
||||||
|
owner.resolver.mark_string(int(dst))
|
||||||
|
# Track last substring for optional esc_json fallback
|
||||||
|
try:
|
||||||
|
if isinstance(method, str) and method == 'substring' and isinstance(dst, int):
|
||||||
|
owner._last_substring_vid = int(dst)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif op == "externcall":
|
||||||
|
func_name = inst.get("func")
|
||||||
|
args = inst.get("args", [])
|
||||||
|
dst = inst.get("dst")
|
||||||
|
lower_externcall(builder, owner.module, func_name, args, dst,
|
||||||
|
vmap_ctx, owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "newbox":
|
||||||
|
box_type = inst.get("type")
|
||||||
|
args = inst.get("args", [])
|
||||||
|
dst = inst.get("dst")
|
||||||
|
lower_newbox(builder, owner.module, box_type, args, dst,
|
||||||
|
vmap_ctx, owner.resolver, getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "typeop":
|
||||||
|
operation = inst.get("operation")
|
||||||
|
src = inst.get("src")
|
||||||
|
dst = inst.get("dst")
|
||||||
|
target_type = inst.get("target_type")
|
||||||
|
lower_typeop(builder, operation, src, dst, target_type,
|
||||||
|
vmap_ctx, owner.resolver, owner.preds, owner.block_end_values, owner.bb_map, getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "safepoint":
|
||||||
|
live = inst.get("live", [])
|
||||||
|
lower_safepoint(builder, owner.module, live, vmap_ctx,
|
||||||
|
resolver=owner.resolver, preds=owner.preds,
|
||||||
|
block_end_values=owner.block_end_values, bb_map=owner.bb_map,
|
||||||
|
ctx=getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "barrier":
|
||||||
|
barrier_type = inst.get("type", "memory")
|
||||||
|
lower_barrier(builder, barrier_type, ctx=getattr(owner, 'ctx', None))
|
||||||
|
|
||||||
|
elif op == "while":
|
||||||
|
# Experimental LoopForm lowering inside a block
|
||||||
|
cond = inst.get("cond")
|
||||||
|
body = inst.get("body", [])
|
||||||
|
owner.loop_count += 1
|
||||||
|
if not lower_while_loopform(builder, func, cond, body,
|
||||||
|
owner.loop_count, owner.vmap, owner.bb_map,
|
||||||
|
owner.resolver, owner.preds, owner.block_end_values,
|
||||||
|
getattr(owner, 'ctx', None)):
|
||||||
|
# Fallback to regular while (structured)
|
||||||
|
try:
|
||||||
|
owner.resolver._owner_lower_instruction = owner.lower_instruction
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
lower_while_regular(builder, func, cond, body,
|
||||||
|
owner.loop_count, owner.vmap, owner.bb_map,
|
||||||
|
owner.resolver, owner.preds, owner.block_end_values)
|
||||||
|
else:
|
||||||
|
trace_debug(f"[Python LLVM] Unknown instruction: {op}")
|
||||||
|
|
||||||
|
# Record per-inst definition for lifetime hinting as soon as available
|
||||||
|
try:
|
||||||
|
dst_maybe = inst.get("dst")
|
||||||
|
if isinstance(dst_maybe, int) and dst_maybe in owner.vmap:
|
||||||
|
cur_bid = None
|
||||||
|
try:
|
||||||
|
cur_bid = int(str(builder.block.name).replace('bb',''))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if cur_bid is not None:
|
||||||
|
owner.def_blocks.setdefault(dst_maybe, set()).add(cur_bid)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
@ -184,13 +184,12 @@ class NyashLLVMBuilder:
|
|||||||
|
|
||||||
def lower_function(self, func_data: Dict[str, Any]):
|
def lower_function(self, func_data: Dict[str, Any]):
|
||||||
"""Lower a single MIR function to LLVM IR"""
|
"""Lower a single MIR function to LLVM IR"""
|
||||||
# Optional: delegate to external helper when gated (incremental split)
|
# Prefer delegated helper (incremental split); fall back on failure
|
||||||
try:
|
|
||||||
if os.environ.get('NYASH_LLVM_USE_HELPER_LOWER') == '1':
|
|
||||||
try:
|
try:
|
||||||
from builders.function_lower import lower_function as _lower
|
from builders.function_lower import lower_function as _lower
|
||||||
return _lower(self, func_data)
|
return _lower(self, func_data)
|
||||||
except Exception as _e:
|
except Exception as _e:
|
||||||
|
try:
|
||||||
trace_debug(f"[Python LLVM] helper lower_function failed, falling back: {_e}")
|
trace_debug(f"[Python LLVM] helper lower_function failed, falling back: {_e}")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@ -853,156 +852,8 @@ class NyashLLVMBuilder:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def lower_instruction(self, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
|
def lower_instruction(self, builder: ir.IRBuilder, inst: Dict[str, Any], func: ir.Function):
|
||||||
"""Dispatch instruction to appropriate handler"""
|
from builders.instruction_lower import lower_instruction as _li
|
||||||
op = inst.get("op")
|
return _li(self, builder, inst, func)
|
||||||
# Pick current vmap context
|
|
||||||
vmap_ctx = getattr(self, '_current_vmap', self.vmap)
|
|
||||||
|
|
||||||
if op == "const":
|
|
||||||
dst = inst.get("dst")
|
|
||||||
value = inst.get("value")
|
|
||||||
lower_const(builder, self.module, dst, value, vmap_ctx, self.resolver)
|
|
||||||
|
|
||||||
elif op == "binop":
|
|
||||||
operation = inst.get("operation")
|
|
||||||
lhs = inst.get("lhs")
|
|
||||||
rhs = inst.get("rhs")
|
|
||||||
dst = inst.get("dst")
|
|
||||||
dst_type = inst.get("dst_type")
|
|
||||||
lower_binop(builder, self.resolver, operation, lhs, rhs, dst,
|
|
||||||
vmap_ctx, builder.block, self.preds, self.block_end_values, self.bb_map,
|
|
||||||
dst_type=dst_type)
|
|
||||||
|
|
||||||
elif op == "jump":
|
|
||||||
target = inst.get("target")
|
|
||||||
lower_jump(builder, target, self.bb_map)
|
|
||||||
|
|
||||||
elif op == "copy":
|
|
||||||
dst = inst.get("dst")
|
|
||||||
src = inst.get("src")
|
|
||||||
lower_copy(builder, dst, src, vmap_ctx, self.resolver, builder.block, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "branch":
|
|
||||||
cond = inst.get("cond")
|
|
||||||
then_bid = inst.get("then")
|
|
||||||
else_bid = inst.get("else")
|
|
||||||
lower_branch(builder, cond, then_bid, else_bid, vmap_ctx, self.bb_map, self.resolver, self.preds, self.block_end_values)
|
|
||||||
|
|
||||||
elif op == "ret":
|
|
||||||
value = inst.get("value")
|
|
||||||
lower_return(builder, value, vmap_ctx, func.function_type.return_type,
|
|
||||||
self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "phi":
|
|
||||||
# No-op here: PHIはメタのみ(resolverがon‑demand生成)
|
|
||||||
return
|
|
||||||
|
|
||||||
elif op == "compare":
|
|
||||||
# Dedicated compare op
|
|
||||||
operation = inst.get("operation") or inst.get("op")
|
|
||||||
lhs = inst.get("lhs")
|
|
||||||
rhs = inst.get("rhs")
|
|
||||||
dst = inst.get("dst")
|
|
||||||
cmp_kind = inst.get("cmp_kind")
|
|
||||||
lower_compare(builder, operation, lhs, rhs, dst, vmap_ctx,
|
|
||||||
self.resolver, builder.block, self.preds, self.block_end_values, self.bb_map,
|
|
||||||
meta={"cmp_kind": cmp_kind} if cmp_kind else None,
|
|
||||||
ctx=getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "call":
|
|
||||||
func_name = inst.get("func")
|
|
||||||
args = inst.get("args", [])
|
|
||||||
dst = inst.get("dst")
|
|
||||||
lower_call(builder, self.module, func_name, args, dst, vmap_ctx, self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "boxcall":
|
|
||||||
box_vid = inst.get("box")
|
|
||||||
method = inst.get("method")
|
|
||||||
args = inst.get("args", [])
|
|
||||||
dst = inst.get("dst")
|
|
||||||
lower_boxcall(builder, self.module, box_vid, method, args, dst,
|
|
||||||
vmap_ctx, self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
|
|
||||||
# Optional: honor explicit dst_type for tagging (string handle)
|
|
||||||
try:
|
|
||||||
dst_type = inst.get("dst_type")
|
|
||||||
if dst is not None and isinstance(dst_type, dict):
|
|
||||||
if dst_type.get("kind") == "handle" and dst_type.get("box_type") == "StringBox":
|
|
||||||
if hasattr(self.resolver, 'mark_string'):
|
|
||||||
self.resolver.mark_string(int(dst))
|
|
||||||
# Track last substring for optional esc_json fallback
|
|
||||||
try:
|
|
||||||
if isinstance(method, str) and method == 'substring' and isinstance(dst, int):
|
|
||||||
self._last_substring_vid = int(dst)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
elif op == "externcall":
|
|
||||||
func_name = inst.get("func")
|
|
||||||
args = inst.get("args", [])
|
|
||||||
dst = inst.get("dst")
|
|
||||||
lower_externcall(builder, self.module, func_name, args, dst,
|
|
||||||
vmap_ctx, self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "newbox":
|
|
||||||
box_type = inst.get("type")
|
|
||||||
args = inst.get("args", [])
|
|
||||||
dst = inst.get("dst")
|
|
||||||
lower_newbox(builder, self.module, box_type, args, dst,
|
|
||||||
vmap_ctx, self.resolver, getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "typeop":
|
|
||||||
operation = inst.get("operation")
|
|
||||||
src = inst.get("src")
|
|
||||||
dst = inst.get("dst")
|
|
||||||
target_type = inst.get("target_type")
|
|
||||||
lower_typeop(builder, operation, src, dst, target_type,
|
|
||||||
vmap_ctx, self.resolver, self.preds, self.block_end_values, self.bb_map, getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "safepoint":
|
|
||||||
live = inst.get("live", [])
|
|
||||||
lower_safepoint(builder, self.module, live, vmap_ctx,
|
|
||||||
resolver=self.resolver, preds=self.preds,
|
|
||||||
block_end_values=self.block_end_values, bb_map=self.bb_map,
|
|
||||||
ctx=getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "barrier":
|
|
||||||
barrier_type = inst.get("type", "memory")
|
|
||||||
lower_barrier(builder, barrier_type, ctx=getattr(self, 'ctx', None))
|
|
||||||
|
|
||||||
elif op == "while":
|
|
||||||
# Experimental LoopForm lowering
|
|
||||||
cond = inst.get("cond")
|
|
||||||
body = inst.get("body", [])
|
|
||||||
self.loop_count += 1
|
|
||||||
if not lower_while_loopform(builder, func, cond, body,
|
|
||||||
self.loop_count, self.vmap, self.bb_map,
|
|
||||||
self.resolver, self.preds, self.block_end_values,
|
|
||||||
getattr(self, 'ctx', None)):
|
|
||||||
# Fallback to regular while (structured)
|
|
||||||
try:
|
|
||||||
self.resolver._owner_lower_instruction = self.lower_instruction
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
lower_while_regular(builder, func, cond, body,
|
|
||||||
self.loop_count, self.vmap, self.bb_map,
|
|
||||||
self.resolver, self.preds, self.block_end_values)
|
|
||||||
else:
|
|
||||||
trace_debug(f"[Python LLVM] Unknown instruction: {op}")
|
|
||||||
# Record per-inst definition for lifetime hinting as soon as available
|
|
||||||
try:
|
|
||||||
dst_maybe = inst.get("dst")
|
|
||||||
if isinstance(dst_maybe, int) and dst_maybe in self.vmap:
|
|
||||||
cur_bid = None
|
|
||||||
try:
|
|
||||||
cur_bid = int(str(builder.block.name).replace('bb',''))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if cur_bid is not None:
|
|
||||||
self.def_blocks.setdefault(dst_maybe, set()).add(cur_bid)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# NOTE: regular while lowering is implemented in
|
# NOTE: regular while lowering is implemented in
|
||||||
# instructions/controlflow/while_.py::lower_while_regular and invoked
|
# instructions/controlflow/while_.py::lower_while_regular and invoked
|
||||||
|
|||||||
@ -3,6 +3,7 @@ use std::collections::HashSet;
|
|||||||
|
|
||||||
/// Collect free variables used in `node` into `used`, excluding names present in `locals`.
|
/// Collect free variables used in `node` into `used`, excluding names present in `locals`.
|
||||||
/// `locals` is updated as new local declarations are encountered.
|
/// `locals` is updated as new local declarations are encountered.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(super) fn collect_free_vars(
|
pub(super) fn collect_free_vars(
|
||||||
node: &ASTNode,
|
node: &ASTNode,
|
||||||
used: &mut HashSet<String>,
|
used: &mut HashSet<String>,
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
* - Dead code elimination
|
* - Dead code elimination
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::{Effect, EffectMask, MirFunction, MirInstruction, MirModule, MirType, ValueId};
|
use super::{MirFunction, MirInstruction, MirModule, MirType, ValueId};
|
||||||
use crate::mir::optimizer_stats::OptimizationStats;
|
use crate::mir::optimizer_stats::OptimizationStats;
|
||||||
// std::collections imports removed (local DCE/CSE impls deleted)
|
// std::collections imports removed (local DCE/CSE impls deleted)
|
||||||
|
|
||||||
@ -514,4 +514,3 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ impl NyashParser {
|
|||||||
enum MatchArm {
|
enum MatchArm {
|
||||||
Lit { lits: Vec<LiteralValue>, guard: Option<ASTNode>, body: ASTNode },
|
Lit { lits: Vec<LiteralValue>, guard: Option<ASTNode>, body: ASTNode },
|
||||||
Type { ty: String, bind: String, guard: Option<ASTNode>, body: ASTNode },
|
Type { ty: String, bind: String, guard: Option<ASTNode>, body: ASTNode },
|
||||||
Default(ASTNode),
|
Default,
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut arms_any: Vec<MatchArm> = Vec::new();
|
let mut arms_any: Vec<MatchArm> = Vec::new();
|
||||||
@ -67,7 +67,7 @@ impl NyashParser {
|
|||||||
self.parse_expression()?
|
self.parse_expression()?
|
||||||
};
|
};
|
||||||
default_expr = Some(expr.clone());
|
default_expr = Some(expr.clone());
|
||||||
arms_any.push(MatchArm::Default(expr));
|
arms_any.push(MatchArm::Default);
|
||||||
} else {
|
} else {
|
||||||
// arm head
|
// arm head
|
||||||
// Type pattern? IDENT '(' IDENT ')'
|
// Type pattern? IDENT '(' IDENT ')'
|
||||||
@ -187,7 +187,7 @@ impl NyashParser {
|
|||||||
lit_arms.push((lit, body.clone()));
|
lit_arms.push((lit, body.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MatchArm::Default(_) => { /* handled via else_expr above */ }
|
MatchArm::Default => { /* handled via else_expr above */ }
|
||||||
MatchArm::Type { .. } => unreachable!(),
|
MatchArm::Type { .. } => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,7 +216,7 @@ impl NyashParser {
|
|||||||
// Process arms in reverse to build nested If
|
// Process arms in reverse to build nested If
|
||||||
for arm in arms_any.into_iter().rev() {
|
for arm in arms_any.into_iter().rev() {
|
||||||
match arm {
|
match arm {
|
||||||
MatchArm::Default(_) => {
|
MatchArm::Default => {
|
||||||
// already handled as else_node
|
// already handled as else_node
|
||||||
}
|
}
|
||||||
MatchArm::Lit { lits, guard, body } => {
|
MatchArm::Lit { lits, guard, body } => {
|
||||||
|
|||||||
@ -54,9 +54,9 @@ impl NyashRunner {
|
|||||||
|
|
||||||
/// Run Nyash based on the configuration
|
/// Run Nyash based on the configuration
|
||||||
pub fn run(&self) {
|
pub fn run(&self) {
|
||||||
let groups = self.config.as_groups();
|
|
||||||
// Build system (MVP): nyash --build <nyash.toml>
|
// Build system (MVP): nyash --build <nyash.toml>
|
||||||
if let Some(cfg_path) = self.config.build_path.clone() {
|
let groups = self.config.as_groups();
|
||||||
|
if let Some(cfg_path) = groups.build.path.clone() {
|
||||||
if let Err(e) = self.run_build_mvp(&cfg_path) {
|
if let Err(e) = self.run_build_mvp(&cfg_path) {
|
||||||
eprintln!("❌ build error: {}", e);
|
eprintln!("❌ build error: {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
@ -67,7 +67,7 @@ impl NyashRunner {
|
|||||||
let mut using_ctx = self.init_using_context();
|
let mut using_ctx = self.init_using_context();
|
||||||
let mut pending_using: Vec<(String, Option<String>)> = Vec::new();
|
let mut pending_using: Vec<(String, Option<String>)> = Vec::new();
|
||||||
// CLI --using SPEC entries (SPEC: 'ns', 'ns as Alias', '"path" as Alias')
|
// CLI --using SPEC entries (SPEC: 'ns', 'ns as Alias', '"path" as Alias')
|
||||||
for spec in &self.config.cli_usings {
|
for spec in &groups.input.cli_usings {
|
||||||
let s = spec.trim();
|
let s = spec.trim();
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@ -133,7 +133,7 @@ impl NyashRunner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Run named task from nyash.toml (MVP)
|
// Run named task from nyash.toml (MVP)
|
||||||
if let Some(task) = self.config.run_task.clone() {
|
if let Some(task) = groups.run_task.clone() {
|
||||||
if let Err(e) = run_named_task(&task) {
|
if let Err(e) = run_named_task(&task) {
|
||||||
eprintln!("❌ Task error: {}", e);
|
eprintln!("❌ Task error: {}", e);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
@ -141,11 +141,11 @@ impl NyashRunner {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Verbose CLI flag maps to env for downstream helpers/scripts
|
// Verbose CLI flag maps to env for downstream helpers/scripts
|
||||||
if self.config.cli_verbose {
|
if groups.debug.cli_verbose {
|
||||||
std::env::set_var("NYASH_CLI_VERBOSE", "1");
|
std::env::set_var("NYASH_CLI_VERBOSE", "1");
|
||||||
}
|
}
|
||||||
// GC mode forwarding: map CLI --gc to NYASH_GC_MODE for downstream runtimes
|
// GC mode forwarding: map CLI --gc to NYASH_GC_MODE for downstream runtimes
|
||||||
if let Some(ref m) = self.config.gc_mode {
|
if let Some(ref m) = groups.gc_mode {
|
||||||
if !m.trim().is_empty() {
|
if !m.trim().is_empty() {
|
||||||
std::env::set_var("NYASH_GC_MODE", m);
|
std::env::set_var("NYASH_GC_MODE", m);
|
||||||
}
|
}
|
||||||
@ -155,7 +155,7 @@ impl NyashRunner {
|
|||||||
// // @env KEY=VALUE
|
// // @env KEY=VALUE
|
||||||
// // @jit-debug (preset: exec, threshold=1, events+trace)
|
// // @jit-debug (preset: exec, threshold=1, events+trace)
|
||||||
// // @plugin-builtins (NYASH_USE_PLUGIN_BUILTINS=1)
|
// // @plugin-builtins (NYASH_USE_PLUGIN_BUILTINS=1)
|
||||||
if let Some(ref filename) = self.config.file {
|
if let Some(ref filename) = groups.input.file {
|
||||||
if let Ok(code) = fs::read_to_string(filename) {
|
if let Ok(code) = fs::read_to_string(filename) {
|
||||||
// Apply script-level directives and lint
|
// Apply script-level directives and lint
|
||||||
let strict_fields =
|
let strict_fields =
|
||||||
@ -163,7 +163,7 @@ impl NyashRunner {
|
|||||||
if let Err(e) = cli_directives::apply_cli_directives_from_source(
|
if let Err(e) = cli_directives::apply_cli_directives_from_source(
|
||||||
&code,
|
&code,
|
||||||
strict_fields,
|
strict_fields,
|
||||||
self.config.cli_verbose,
|
groups.debug.cli_verbose,
|
||||||
) {
|
) {
|
||||||
eprintln!("❌ Lint/Directive error: {}", e);
|
eprintln!("❌ Lint/Directive error: {}", e);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
@ -273,7 +273,7 @@ impl NyashRunner {
|
|||||||
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
|
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", override_types.join(","));
|
||||||
|
|
||||||
// Opt-in: load Ny script plugins listed in nyash.toml [ny_plugins]
|
// Opt-in: load Ny script plugins listed in nyash.toml [ny_plugins]
|
||||||
if self.config.load_ny_plugins
|
if groups.load_ny_plugins
|
||||||
|| std::env::var("NYASH_LOAD_NY_PLUGINS").ok().as_deref() == Some("1")
|
|| std::env::var("NYASH_LOAD_NY_PLUGINS").ok().as_deref() == Some("1")
|
||||||
{
|
{
|
||||||
if let Ok(text) = std::fs::read_to_string("nyash.toml") {
|
if let Ok(text) = std::fs::read_to_string("nyash.toml") {
|
||||||
@ -406,7 +406,7 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
// Architectural pivot: JIT is compiler-only (EXE/AOT). Ensure VM runtime does not dispatch to JIT
|
// Architectural pivot: JIT is compiler-only (EXE/AOT). Ensure VM runtime does not dispatch to JIT
|
||||||
// unless explicitly requested via independent JIT mode, or when emitting AOT objects.
|
// unless explicitly requested via independent JIT mode, or when emitting AOT objects.
|
||||||
if !self.config.compile_native && !self.config.jit_direct {
|
if !groups.compile_native && !groups.backend.jit.direct {
|
||||||
// When AOT object emission is requested, allow JIT to run for object generation
|
// When AOT object emission is requested, allow JIT to run for object generation
|
||||||
let aot_obj = std::env::var("NYASH_AOT_OBJECT_OUT").ok();
|
let aot_obj = std::env::var("NYASH_AOT_OBJECT_OUT").ok();
|
||||||
if aot_obj.is_none() || aot_obj.as_deref() == Some("") {
|
if aot_obj.is_none() || aot_obj.as_deref() == Some("") {
|
||||||
@ -415,10 +415,10 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Benchmark mode - can run without a file
|
// Benchmark mode - can run without a file
|
||||||
if self.config.benchmark {
|
if groups.benchmark {
|
||||||
println!("📊 Nyash Performance Benchmark Suite");
|
println!("📊 Nyash Performance Benchmark Suite");
|
||||||
println!("====================================");
|
println!("====================================");
|
||||||
println!("Running {} iterations per test...", self.config.iterations);
|
println!("Running {} iterations per test...", groups.iterations);
|
||||||
println!();
|
println!();
|
||||||
#[cfg(feature = "vm-legacy")]
|
#[cfg(feature = "vm-legacy")]
|
||||||
{
|
{
|
||||||
@ -434,9 +434,9 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref filename) = self.config.file {
|
if let Some(ref filename) = groups.input.file {
|
||||||
// Independent JIT direct mode (no VM execute path)
|
// Independent JIT direct mode (no VM execute path)
|
||||||
if self.config.jit_direct {
|
if groups.backend.jit.direct {
|
||||||
self.run_file_jit_direct(filename);
|
self.run_file_jit_direct(filename);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,8 @@ impl NyashRunner {
|
|||||||
/// Execute AOT compilation mode (split)
|
/// Execute AOT compilation mode (split)
|
||||||
#[cfg(feature = "cranelift-jit")]
|
#[cfg(feature = "cranelift-jit")]
|
||||||
pub(crate) fn execute_aot_mode(&self, filename: &str) {
|
pub(crate) fn execute_aot_mode(&self, filename: &str) {
|
||||||
let output = self.config.output_file.as_deref().unwrap_or("app");
|
let groups = self.config.as_groups();
|
||||||
|
let output = groups.output_file.as_deref().unwrap_or("app");
|
||||||
// Prefer using provided helper scripts to ensure link flags and runtime integration
|
// Prefer using provided helper scripts to ensure link flags and runtime integration
|
||||||
let status = if cfg!(target_os = "windows") {
|
let status = if cfg!(target_os = "windows") {
|
||||||
// Use PowerShell helper; falls back to bash if available inside the script
|
// Use PowerShell helper; falls back to bash if available inside the script
|
||||||
|
|||||||
@ -6,9 +6,10 @@ use nyash_rust::{
|
|||||||
impl NyashRunner {
|
impl NyashRunner {
|
||||||
/// Execute benchmark mode (split)
|
/// Execute benchmark mode (split)
|
||||||
pub(crate) fn execute_benchmark_mode(&self) {
|
pub(crate) fn execute_benchmark_mode(&self) {
|
||||||
|
let groups = self.config.as_groups();
|
||||||
println!(
|
println!(
|
||||||
"🏁 Running benchmark mode with {} iterations",
|
"🏁 Running benchmark mode with {} iterations",
|
||||||
self.config.iterations
|
groups.iterations
|
||||||
);
|
);
|
||||||
// Tests: some run on all backends, some are JIT+f64 only
|
// Tests: some run on all backends, some are JIT+f64 only
|
||||||
// Third element indicates JIT+f64 only (skip VM/Interpreter)
|
// Third element indicates JIT+f64 only (skip VM/Interpreter)
|
||||||
@ -72,16 +73,16 @@ impl NyashRunner {
|
|||||||
"(JIT+f64 only) Skipping VM/Interpreter; requires --features cranelift-jit"
|
"(JIT+f64 only) Skipping VM/Interpreter; requires --features cranelift-jit"
|
||||||
);
|
);
|
||||||
// Warmup JIT
|
// Warmup JIT
|
||||||
let warmup = (self.config.iterations / 10).max(1);
|
let warmup = (groups.iterations / 10).max(1);
|
||||||
self.bench_jit(code, warmup);
|
self.bench_jit(code, warmup);
|
||||||
// Measured
|
// Measured
|
||||||
let jit_time = self.bench_jit(code, self.config.iterations);
|
let jit_time = self.bench_jit(code, groups.iterations);
|
||||||
println!("\n📊 Performance Summary [{}]:", name);
|
println!("\n📊 Performance Summary [{}]:", name);
|
||||||
println!(
|
println!(
|
||||||
" JIT f64 ops: {} iters in {:?} ({:.2} ops/sec)",
|
" JIT f64 ops: {} iters in {:?} ({:.2} ops/sec)",
|
||||||
self.config.iterations,
|
groups.iterations,
|
||||||
jit_time,
|
jit_time,
|
||||||
self.config.iterations as f64 / jit_time.as_secs_f64()
|
groups.iterations as f64 / jit_time.as_secs_f64()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Quick correctness check across modes (golden): Interpreter vs VM vs VM+JIT
|
// Quick correctness check across modes (golden): Interpreter vs VM vs VM+JIT
|
||||||
@ -91,15 +92,15 @@ impl NyashRunner {
|
|||||||
println!("✅ Outputs match across Interpreter/VM/JIT");
|
println!("✅ Outputs match across Interpreter/VM/JIT");
|
||||||
}
|
}
|
||||||
// Warmup (not measured)
|
// Warmup (not measured)
|
||||||
let warmup = (self.config.iterations / 10).max(1);
|
let warmup = (groups.iterations / 10).max(1);
|
||||||
self.bench_interpreter(code, warmup);
|
self.bench_interpreter(code, warmup);
|
||||||
self.bench_vm(code, warmup);
|
self.bench_vm(code, warmup);
|
||||||
self.bench_jit(code, warmup);
|
self.bench_jit(code, warmup);
|
||||||
|
|
||||||
// Measured runs
|
// Measured runs
|
||||||
let interpreter_time = self.bench_interpreter(code, self.config.iterations);
|
let interpreter_time = self.bench_interpreter(code, groups.iterations);
|
||||||
let vm_time = self.bench_vm(code, self.config.iterations);
|
let vm_time = self.bench_vm(code, groups.iterations);
|
||||||
let jit_time = self.bench_jit(code, self.config.iterations);
|
let jit_time = self.bench_jit(code, groups.iterations);
|
||||||
|
|
||||||
// Summary
|
// Summary
|
||||||
let vm_vs_interp = interpreter_time.as_secs_f64() / vm_time.as_secs_f64();
|
let vm_vs_interp = interpreter_time.as_secs_f64() / vm_time.as_secs_f64();
|
||||||
|
|||||||
@ -110,7 +110,8 @@ impl NyashRunner {
|
|||||||
_ => {
|
_ => {
|
||||||
if cli_verbose() {
|
if cli_verbose() {
|
||||||
println!("🦀 Nyash Rust Implementation - Executing file: {} 🦀", filename);
|
println!("🦀 Nyash Rust Implementation - Executing file: {} 🦀", filename);
|
||||||
if let Some(fuel) = self.config.debug_fuel {
|
let groups = self.config.as_groups();
|
||||||
|
if let Some(fuel) = groups.debug.debug_fuel {
|
||||||
println!("🔥 Debug fuel limit: {} iterations", fuel);
|
println!("🔥 Debug fuel limit: {} iterations", fuel);
|
||||||
} else {
|
} else {
|
||||||
println!("🔥 Debug fuel limit: unlimited");
|
println!("🔥 Debug fuel limit: unlimited");
|
||||||
@ -498,8 +499,9 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the code with debug fuel limit
|
// Parse the code with debug fuel limit
|
||||||
eprintln!("🔍 DEBUG: Starting parse with fuel: {:?}...", self.config.debug_fuel);
|
let groups = self.config.as_groups();
|
||||||
let ast = match NyashParser::parse_from_string_with_fuel(code_ref, self.config.debug_fuel) {
|
eprintln!("🔍 DEBUG: Starting parse with fuel: {:?}...", groups.debug.debug_fuel);
|
||||||
|
let ast = match NyashParser::parse_from_string_with_fuel(code_ref, groups.debug.debug_fuel) {
|
||||||
Ok(ast) => { eprintln!("🔍 DEBUG: Parse completed, AST created"); ast },
|
Ok(ast) => { eprintln!("🔍 DEBUG: Parse completed, AST created"); ast },
|
||||||
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
|
Err(e) => { eprintln!("❌ Parse error: {}", e); process::exit(1); }
|
||||||
};
|
};
|
||||||
|
|||||||
@ -35,7 +35,8 @@ impl NyashRunner {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Determine output file
|
// Determine output file
|
||||||
let output = self.config.output_file.as_deref().unwrap_or_else(|| {
|
let groups = self.config.as_groups();
|
||||||
|
let output = groups.output_file.as_deref().unwrap_or_else(|| {
|
||||||
if filename.ends_with(".nyash") { filename.strip_suffix(".nyash").unwrap_or(filename) } else { filename }
|
if filename.ends_with(".nyash") { filename.strip_suffix(".nyash").unwrap_or(filename) } else { filename }
|
||||||
});
|
});
|
||||||
let output_file = format!("{}.wat", output);
|
let output_file = format!("{}.wat", output);
|
||||||
@ -46,4 +47,3 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@ pub fn clear_current_vm() {
|
|||||||
CURRENT_VM.with(|c| c.set(std::ptr::null_mut()));
|
CURRENT_VM.with(|c| c.set(std::ptr::null_mut()));
|
||||||
}
|
}
|
||||||
#[cfg(feature = "vm-legacy")]
|
#[cfg(feature = "vm-legacy")]
|
||||||
|
#[allow(dead_code)]
|
||||||
fn with_current_vm_mut<F, R>(f: F) -> Option<R>
|
fn with_current_vm_mut<F, R>(f: F) -> Option<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut crate::backend::vm::VM) -> R,
|
F: FnOnce(&mut crate::backend::vm::VM) -> R,
|
||||||
@ -44,6 +45,7 @@ pub fn set_current_vm(_ptr: *mut ()) {}
|
|||||||
#[cfg(not(feature = "vm-legacy"))]
|
#[cfg(not(feature = "vm-legacy"))]
|
||||||
pub fn clear_current_vm() {}
|
pub fn clear_current_vm() {}
|
||||||
#[cfg(not(feature = "vm-legacy"))]
|
#[cfg(not(feature = "vm-legacy"))]
|
||||||
|
#[allow(dead_code)]
|
||||||
fn with_current_vm_mut<F, R>(_f: F) -> Option<R>
|
fn with_current_vm_mut<F, R>(_f: F) -> Option<R>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut ()) -> R,
|
F: FnOnce(&mut ()) -> R,
|
||||||
|
|||||||
Reference in New Issue
Block a user