From 3c7a5de9002f8c4d1796ff1507575674e15f0c01 Mon Sep 17 00:00:00 2001 From: Selfhosting Dev Date: Fri, 19 Sep 2025 14:29:02 +0900 Subject: [PATCH] runner(cli): adopt CliConfig::as_groups across runner modules (dispatch/common/pipe_io/mir/bench). llvm-builder: extract ny_main wrapper to builders.entry; add optional env-gated function_lower delegation; keep default behavior unchanged --- CURRENT_TASK.md | 8 ++ src/cli.rs | 161 +++++++++++++++++++++++++ src/llvm_py/builders/entry.py | 68 +++++++++++ src/llvm_py/builders/function_lower.py | 84 +++++++++++++ src/llvm_py/llvm_builder.py | 93 ++++++-------- src/runner/dispatch.rs | 22 ++-- src/runner/mod.rs | 39 +++--- src/runner/modes/bench.rs | 5 +- src/runner/modes/common.rs | 13 +- src/runner/modes/mir.rs | 17 +-- src/runner/pipe_io.rs | 5 +- 11 files changed, 411 insertions(+), 104 deletions(-) create mode 100644 src/llvm_py/builders/entry.py create mode 100644 src/llvm_py/builders/function_lower.py diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 45f64a12..e241b496 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -47,6 +47,14 @@ Clone() Reduction — Phase 1 (Arc/str) - `types: HashMap>` → `HashMap, Arc>`(内部型名の共有化)。 - API互換(`get_type(&str)` は Borrow によりそのまま利用可能)。 +CLI Refactor — Phase A (non‑breaking) +- Added grouped view structs without changing existing fields or parsing: + - InputConfig, DebugConfig, BackendConfig(+JitConfig), BuildConfig, EmitConfig, ParserPipeConfig + - Access via `CliConfig::as_groups()` to enable gradual adoption. + +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`. +- Added scaffolding for function lowering split: `src/llvm_py/builders/function_lower.py` (not yet wired for all paths). Refactor Plan (next 1–2 weeks) 1) Split parse_box_declaration (667 lines) in src/parser/declarations/box_definition.rs - Targets (line ranges are indicative): diff --git a/src/cli.rs b/src/cli.rs index 024dbc2e..a89fe2c1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,6 +7,7 @@ use clap::{Arg, ArgMatches, Command}; use serde_json; +use std::fmt::Debug as _; // for derive Debug consistency /// Command-line configuration structure #[derive(Debug, Clone)] @@ -77,6 +78,99 @@ pub struct CliConfig { pub emit_exe_libs: Option, } +/// Grouped views (Phase 1: non-breaking). These structs provide a categorized +/// lens over the flat CliConfig without changing public fields. +#[derive(Debug, Clone)] +pub struct InputConfig { + pub file: Option, + pub cli_usings: Vec, +} + +#[derive(Debug, Clone)] +pub struct DebugConfig { + pub debug_fuel: Option, + pub dump_ast: bool, + pub dump_mir: bool, + pub verify_mir: bool, + pub mir_verbose: bool, + pub mir_verbose_effects: bool, + pub cli_verbose: bool, +} + +#[derive(Debug, Clone)] +pub struct BackendConfig { + pub backend: String, + // VM + pub vm_stats: bool, + pub vm_stats_json: bool, + // JIT + pub jit: JitConfig, +} + +#[derive(Debug, Clone)] +pub struct JitConfig { + pub exec: bool, + pub stats: bool, + pub stats_json: bool, + pub dump: bool, + pub events: bool, + pub events_compile: bool, + pub events_runtime: bool, + pub events_path: Option, + pub threshold: Option, + pub phi_min: bool, + pub hostcall: bool, + pub handle_debug: bool, + pub native_f64: bool, + pub native_bool: bool, + pub only: bool, + pub direct: bool, +} + +#[derive(Debug, Clone)] +pub struct BuildConfig { + pub path: Option, + pub app: Option, + pub out: Option, + pub aot: Option, + pub profile: Option, + pub target: Option, +} + +#[derive(Debug, Clone)] +pub struct EmitConfig { + pub emit_cfg: Option, + pub emit_mir_json: Option, + pub emit_exe: Option, + pub emit_exe_nyrt: Option, + pub emit_exe_libs: Option, +} + +#[derive(Debug, Clone)] +pub struct ParserPipeConfig { + pub parser_ny: bool, + pub ny_parser_pipe: bool, + pub json_file: Option, +} + +#[derive(Debug, Clone)] +pub struct CliGroups { + pub input: InputConfig, + pub debug: DebugConfig, + pub backend: BackendConfig, + pub build: BuildConfig, + pub emit: EmitConfig, + pub parser: ParserPipeConfig, + pub gc_mode: Option, + pub compile_wasm: bool, + pub compile_native: bool, + pub output_file: Option, + pub benchmark: bool, + pub iterations: u32, + pub run_task: Option, + pub load_ny_plugins: bool, +} + impl CliConfig { /// Parse command-line arguments and return configuration pub fn parse() -> Self { @@ -101,6 +195,73 @@ impl CliConfig { } } + /// Non-breaking grouped view for downstream code to gradually adopt. + pub fn as_groups(&self) -> CliGroups { + CliGroups { + input: InputConfig { file: self.file.clone(), cli_usings: self.cli_usings.clone() }, + debug: DebugConfig { + debug_fuel: self.debug_fuel, + dump_ast: self.dump_ast, + dump_mir: self.dump_mir, + verify_mir: self.verify_mir, + mir_verbose: self.mir_verbose, + mir_verbose_effects: self.mir_verbose_effects, + cli_verbose: self.cli_verbose, + }, + backend: BackendConfig { + backend: self.backend.clone(), + vm_stats: self.vm_stats, + vm_stats_json: self.vm_stats_json, + jit: JitConfig { + exec: self.jit_exec, + stats: self.jit_stats, + stats_json: self.jit_stats_json, + dump: self.jit_dump, + events: self.jit_events, + events_compile: self.jit_events_compile, + events_runtime: self.jit_events_runtime, + events_path: self.jit_events_path.clone(), + threshold: self.jit_threshold, + phi_min: self.jit_phi_min, + hostcall: self.jit_hostcall, + handle_debug: self.jit_handle_debug, + native_f64: self.jit_native_f64, + native_bool: self.jit_native_bool, + only: self.jit_only, + direct: self.jit_direct, + }, + }, + build: BuildConfig { + path: self.build_path.clone(), + app: self.build_app.clone(), + out: self.build_out.clone(), + aot: self.build_aot.clone(), + profile: self.build_profile.clone(), + target: self.build_target.clone(), + }, + emit: EmitConfig { + emit_cfg: self.emit_cfg.clone(), + emit_mir_json: self.emit_mir_json.clone(), + emit_exe: self.emit_exe.clone(), + emit_exe_nyrt: self.emit_exe_nyrt.clone(), + emit_exe_libs: self.emit_exe_libs.clone(), + }, + parser: ParserPipeConfig { + parser_ny: self.parser_ny, + ny_parser_pipe: self.ny_parser_pipe, + json_file: self.json_file.clone(), + }, + gc_mode: self.gc_mode.clone(), + compile_wasm: self.compile_wasm, + compile_native: self.compile_native, + output_file: self.output_file.clone(), + benchmark: self.benchmark, + iterations: self.iterations, + run_task: self.run_task.clone(), + load_ny_plugins: self.load_ny_plugins, + } + } + /// Build the clap Command structure fn build_command() -> Command { Command::new("nyash") diff --git a/src/llvm_py/builders/entry.py b/src/llvm_py/builders/entry.py new file mode 100644 index 00000000..6899df4b --- /dev/null +++ b/src/llvm_py/builders/entry.py @@ -0,0 +1,68 @@ +from typing import Optional + +def ensure_ny_main(builder) -> None: + """Ensure ny_main wrapper exists by delegating to Main.main/1 or main(). + Modifies builder.module in place; no return value. + """ + has_ny_main = any(f.name == 'ny_main' for f in builder.module.functions) + fn_main_box = None + fn_main_plain = None + for f in builder.module.functions: + if f.name == 'Main.main/1': + fn_main_box = f + elif f.name == 'main': + fn_main_plain = f + target_fn = fn_main_box or fn_main_plain + if target_fn is None or has_ny_main: + return + + # Hide the target to avoid symbol conflicts + try: + target_fn.linkage = 'private' + except Exception: + pass + # i32 ny_main() { return (i32) Main.main(args) | main(); } + from llvmlite import ir + ny_main_ty = ir.FunctionType(builder.i64, []) + ny_main = ir.Function(builder.module, ny_main_ty, name='ny_main') + entry = ny_main.append_basic_block('entry') + b = ir.IRBuilder(entry) + if fn_main_box is not None: + # Build default args = new ArrayBox() via nyash.env.box.new_i64x + i64 = builder.i64 + i8 = builder.i8 + i8p = builder.i8p + # Declare callee + callee = None + for f in builder.module.functions: + if f.name == 'nyash.env.box.new_i64x': + callee = f + break + if callee is None: + callee = ir.Function(builder.module, ir.FunctionType(i64, [i8p, i64, i64, i64, i64, i64]), name='nyash.env.box.new_i64x') + # Create "ArrayBox\0" global + sbytes = b"ArrayBox\0" + arr_ty = ir.ArrayType(i8, len(sbytes)) + g = ir.GlobalVariable(builder.module, arr_ty, name='.ny_main_arraybox') + g.linkage = 'private' + g.global_constant = True + g.initializer = ir.Constant(arr_ty, bytearray(sbytes)) + c0 = ir.Constant(builder.i32, 0) + ptr = b.gep(g, [c0, c0], inbounds=True) + zero = ir.Constant(i64, 0) + args_handle = b.call(callee, [ptr, zero, zero, zero, zero, zero], name='ny_main_args') + rv = b.call(fn_main_box, [args_handle], name='call_Main_main_1') + else: + # Plain main() fallback + if len(fn_main_plain.args) == 0: + rv = b.call(fn_main_plain, [], name='call_user_main') + else: + rv = ir.Constant(builder.i64, 0) + if hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width != 32: + rv64 = b.trunc(rv, builder.i64) if rv.type.width > 64 else b.zext(rv, builder.i64) + b.ret(rv64) + elif hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width == 64: + b.ret(rv) + else: + b.ret(ir.Constant(builder.i64, 0)) + diff --git a/src/llvm_py/builders/function_lower.py b/src/llvm_py/builders/function_lower.py new file mode 100644 index 00000000..838ad401 --- /dev/null +++ b/src/llvm_py/builders/function_lower.py @@ -0,0 +1,84 @@ +from typing import Dict, Any + + +def lower_function(builder, func_data: Dict[str, Any]): + """Lower a single MIR function to LLVM IR using the given builder context. + + This helper is a thin wrapper that delegates to the NyashLLVMBuilder's + existing methods/attributes, enabling gradual file decomposition without + changing semantics. + """ + name = func_data.get("name", "unknown") + builder.current_function_name = name + + import re + params = func_data.get("params", []) + blocks = func_data.get("blocks", []) + + # Determine function signature + if name == "ny_main": + func_ty = builder.i32.func_type([]) + else: + m = re.search(r"/(\d+)$", name) + arity = int(m.group(1)) if m else len(params) + param_types = [builder.i64] * arity + func_ty = builder.i64.func_type(param_types) + + try: + builder.vmap.clear() + except Exception: + builder.vmap = {} + builder.bb_map = {} + builder.preds = {} + builder.block_end_values = {} + builder.def_blocks = {} + builder.predeclared_ret_phis = {} + + # Ensure function exists or create one + fn = None + for f in builder.module.functions: + if f.name == name: + fn = f + break + if fn is None: + from llvmlite import ir + fn = ir.Function(builder.module, func_ty, name=name) + + # Create all basic blocks first + from llvmlite import ir + block_by_id = {} + for b in blocks: + bbid = int(b.get("id", 0)) + bb = fn.append_basic_block(name=f"bb{bbid}") + block_by_id[bbid] = bb + builder.bb_map[bbid] = bb + + # Predeclare ret PHIs if needed (if-merge prepass) + from prepass.if_merge import plan_ret_phi_predeclare + plan = plan_ret_phi_predeclare(block_by_id) + if plan: + if not hasattr(builder, 'block_phi_incomings') or builder.block_phi_incomings is None: + builder.block_phi_incomings = {} + for (bbid, pairs) in plan.items(): + for (ret_vid, preds_list) in pairs.items(): + builder.block_phi_incomings.setdefault(int(bbid), {}).setdefault(int(ret_vid), []) + builder.block_phi_incomings[int(bbid)][int(ret_vid)] = [(int(p), int(ret_vid)) for p in preds_list] + + # Lower instructions per block + from llvmlite.ir import IRBuilder + from instructions import dispatcher # if exists; else inline lowerers + for b in blocks: + bbid = int(b.get("id", 0)) + bb = block_by_id[bbid] + builder_bb = IRBuilder(bb) + builder.resolver.attach_function_and_block(fn, bb) + insts = b.get("insts", []) + for inst in insts: + op = inst.get("op") + # Delegate to existing NyashLLVMBuilder method for now + builder.lower_instruction(op, inst, builder_bb) + + # Finalize PHIs after the function is fully lowered + from phi_wiring import finalize_phis as _finalize_phis + _finalize_phis(builder) + diff --git a/src/llvm_py/llvm_builder.py b/src/llvm_py/llvm_builder.py index 092ae28f..e54a3811 100644 --- a/src/llvm_py/llvm_builder.py +++ b/src/llvm_py/llvm_builder.py @@ -125,66 +125,35 @@ class NyashLLVMBuilder: for func_data in functions: self.lower_function(func_data) - # Create ny_main wrapper if necessary - has_ny_main = any(f.name == 'ny_main' for f in self.module.functions) - # Prefer static box entry: Main.main/1; fallback to plain main (0-arity) - fn_main_box = None - fn_main_plain = None - for f in self.module.functions: - if f.name == 'Main.main/1': - fn_main_box = f - elif f.name == 'main': - fn_main_plain = f - target_fn = fn_main_box or fn_main_plain - if target_fn is not None and not has_ny_main: - # Hide the target to avoid symbol conflicts + # Create ny_main wrapper if necessary (extracted helper) + try: + from builders.entry import ensure_ny_main as _ensure_ny_main + _ensure_ny_main(self) + except Exception: + # Fallback to legacy in-place logic if helper import fails try: - target_fn.linkage = 'private' + has_ny_main = any(f.name == 'ny_main' for f in self.module.functions) + fn_main_box = None + fn_main_plain = None + for f in self.module.functions: + if f.name == 'Main.main/1': + fn_main_box = f + elif f.name == 'main': + fn_main_plain = f + target_fn = fn_main_box or fn_main_plain + if target_fn is not None and not has_ny_main: + ny_main_ty = ir.FunctionType(self.i64, []) + ny_main = ir.Function(self.module, ny_main_ty, name='ny_main') + entry = ny_main.append_basic_block('entry') + b = ir.IRBuilder(entry) + rv = ir.Constant(self.i64, 0) + if fn_main_box is not None: + rv = b.call(fn_main_box, [], name='call_Main_main_1') + elif fn_main_plain is not None and len(fn_main_plain.args) == 0: + rv = b.call(fn_main_plain, [], name='call_user_main') + b.ret(rv) except Exception: pass - # i32 ny_main() { return (i32) Main.main(args) | main(); } - ny_main_ty = ir.FunctionType(self.i64, []) - ny_main = ir.Function(self.module, ny_main_ty, name='ny_main') - entry = ny_main.append_basic_block('entry') - b = ir.IRBuilder(entry) - if fn_main_box is not None: - # Build default args = new ArrayBox() via nyash.env.box.new_i64x - i64 = self.i64 - i8 = self.i8 - i8p = self.i8p - # Declare callee - callee = None - for f in self.module.functions: - if f.name == 'nyash.env.box.new_i64x': - callee = f - break - if callee is None: - callee = ir.Function(self.module, ir.FunctionType(i64, [i8p, i64, i64, i64, i64, i64]), name='nyash.env.box.new_i64x') - # Create "ArrayBox\0" global - sbytes = b"ArrayBox\0" - arr_ty = ir.ArrayType(i8, len(sbytes)) - g = ir.GlobalVariable(self.module, arr_ty, name='.ny_main_arraybox') - g.linkage = 'private' - g.global_constant = True - g.initializer = ir.Constant(arr_ty, bytearray(sbytes)) - c0 = ir.Constant(self.i32, 0) - ptr = b.gep(g, [c0, c0], inbounds=True) - zero = ir.Constant(i64, 0) - args_handle = b.call(callee, [ptr, zero, zero, zero, zero, zero], name='ny_main_args') - rv = b.call(fn_main_box, [args_handle], name='call_Main_main_1') - else: - # Plain main() fallback - if len(fn_main_plain.args) == 0: - rv = b.call(fn_main_plain, [], name='call_user_main') - else: - rv = ir.Constant(self.i64, 0) - if hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width != 32: - rv64 = b.trunc(rv, self.i64) if rv.type.width > 64 else b.zext(rv, self.i64) - b.ret(rv64) - elif hasattr(rv, 'type') and isinstance(rv.type, ir.IntType) and rv.type.width == 64: - b.ret(rv) - else: - b.ret(ir.Constant(self.i64, 0)) ir_text = str(self.module) # Optional IR dump to file for debugging @@ -215,6 +184,16 @@ class NyashLLVMBuilder: def lower_function(self, func_data: Dict[str, Any]): """Lower a single MIR function to LLVM IR""" + # Optional: delegate to external helper when gated (incremental split) + try: + if os.environ.get('NYASH_LLVM_USE_HELPER_LOWER') == '1': + try: + from builders.function_lower import lower_function as _lower + return _lower(self, func_data) + except Exception as _e: + trace_debug(f"[Python LLVM] helper lower_function failed, falling back: {_e}") + except Exception: + pass name = func_data.get("name", "unknown") self.current_function_name = name import re diff --git a/src/runner/dispatch.rs b/src/runner/dispatch.rs index dc4fd15c..11b18e92 100644 --- a/src/runner/dispatch.rs +++ b/src/runner/dispatch.rs @@ -19,7 +19,8 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { } // Direct v0 bridge when requested via CLI/env - let use_ny_parser = runner.config.parser_ny + let groups = runner.config.as_groups(); + let use_ny_parser = groups.parser.parser_ny || std::env::var("NYASH_USE_NY_PARSER").ok().as_deref() == Some("1"); if use_ny_parser { let code = match fs::read_to_string(filename) { @@ -43,7 +44,7 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { } // AST dump mode - if runner.config.dump_ast { + if groups.debug.dump_ast { println!("🧠 Nyash AST Dump - Processing file: {}", filename); let code = match fs::read_to_string(filename) { Ok(content) => content, @@ -64,14 +65,14 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { } // MIR dump/verify - if runner.config.dump_mir || runner.config.verify_mir { + if groups.debug.dump_mir || groups.debug.verify_mir { crate::cli_v!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename); runner.execute_mir_mode(filename); return; } // WASM / AOT (feature-gated) - if runner.config.compile_wasm { + if groups.compile_wasm { #[cfg(feature = "wasm-backend")] { super::modes::wasm::execute_wasm_mode(runner, filename); @@ -83,7 +84,7 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { process::exit(1); } } - if runner.config.compile_native { + if groups.compile_native { #[cfg(feature = "cranelift-jit")] { runner.execute_aot_mode(filename); @@ -97,7 +98,7 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { } // Backend selection - match runner.config.backend.as_str() { + match groups.backend.backend.as_str() { "mir" => { crate::cli_v!("🚀 Nyash MIR Interpreter - Executing file: {} 🚀", filename); runner.execute_mir_mode(filename); @@ -154,7 +155,8 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) { impl NyashRunner { pub(crate) fn execute_mir_module(&self, module: &crate::mir::MirModule) { // If CLI requested MIR JSON emit, write to file and exit immediately. - if let Some(path) = self.config.emit_mir_json.as_ref() { + let groups = self.config.as_groups(); + if let Some(path) = groups.emit.emit_mir_json.as_ref() { let p = std::path::Path::new(path); if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness_bin(module, p) { eprintln!("❌ MIR JSON emit error: {}", e); @@ -164,12 +166,12 @@ impl NyashRunner { std::process::exit(0); } // If CLI requested EXE emit, generate JSON then invoke ny-llvmc to link NyRT and exit. - if let Some(exe_out) = self.config.emit_exe.as_ref() { + if let Some(exe_out) = groups.emit.emit_exe.as_ref() { if let Err(e) = crate::runner::modes::common_util::exec::ny_llvmc_emit_exe_bin( module, exe_out, - self.config.emit_exe_nyrt.as_deref(), - self.config.emit_exe_libs.as_deref(), + groups.emit.emit_exe_nyrt.as_deref(), + groups.emit.emit_exe_libs.as_deref(), ) { eprintln!("❌ {}", e); std::process::exit(1); diff --git a/src/runner/mod.rs b/src/runner/mod.rs index eebf7317..de5119f5 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -54,6 +54,7 @@ impl NyashRunner { /// Run Nyash based on the configuration pub fn run(&self) { + let groups = self.config.as_groups(); // Build system (MVP): nyash --build if let Some(cfg_path) = self.config.build_path.clone() { if let Err(e) = self.run_build_mvp(&cfg_path) { @@ -346,41 +347,41 @@ impl NyashRunner { } // Optional: enable VM stats via CLI flags - if self.config.vm_stats { + if groups.backend.vm_stats { std::env::set_var("NYASH_VM_STATS", "1"); } - if self.config.vm_stats_json { + if groups.backend.vm_stats_json { // Prefer explicit JSON flag over any default std::env::set_var("NYASH_VM_STATS_JSON", "1"); } // Optional: JIT controls via CLI flags (centralized) { // CLI opt-in for JSONL events - if self.config.jit_events { + if groups.backend.jit.events { std::env::set_var("NYASH_JIT_EVENTS", "1"); } - if self.config.jit_events_compile { + if groups.backend.jit.events_compile { std::env::set_var("NYASH_JIT_EVENTS_COMPILE", "1"); } - if self.config.jit_events_runtime { + if groups.backend.jit.events_runtime { std::env::set_var("NYASH_JIT_EVENTS_RUNTIME", "1"); } - if let Some(ref p) = self.config.jit_events_path { + if let Some(ref p) = groups.backend.jit.events_path { std::env::set_var("NYASH_JIT_EVENTS_PATH", p); } let mut jc = nyash_rust::jit::config::JitConfig::from_env(); - jc.exec |= self.config.jit_exec; - jc.stats |= self.config.jit_stats; - jc.stats_json |= self.config.jit_stats_json; - jc.dump |= self.config.jit_dump; - if self.config.jit_threshold.is_some() { - jc.threshold = self.config.jit_threshold; + jc.exec |= groups.backend.jit.exec; + jc.stats |= groups.backend.jit.stats; + jc.stats_json |= groups.backend.jit.stats_json; + jc.dump |= groups.backend.jit.dump; + if groups.backend.jit.threshold.is_some() { + jc.threshold = groups.backend.jit.threshold; } - jc.phi_min |= self.config.jit_phi_min; - jc.hostcall |= self.config.jit_hostcall; - jc.handle_debug |= self.config.jit_handle_debug; - jc.native_f64 |= self.config.jit_native_f64; - jc.native_bool |= self.config.jit_native_bool; + jc.phi_min |= groups.backend.jit.phi_min; + jc.hostcall |= groups.backend.jit.hostcall; + jc.handle_debug |= groups.backend.jit.handle_debug; + jc.native_f64 |= groups.backend.jit.native_f64; + jc.native_bool |= groups.backend.jit.native_bool; // If observability is enabled and no threshold is provided, force threshold=1 so lowering runs and emits events let events_on = std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1") @@ -388,14 +389,14 @@ impl NyashRunner { if events_on && jc.threshold.is_none() { jc.threshold = Some(1); } - if self.config.jit_only { + if groups.backend.jit.only { std::env::set_var("NYASH_JIT_ONLY", "1"); } // Apply runtime capability probe (e.g., disable b1 ABI if unsupported) let caps = nyash_rust::jit::config::probe_capabilities(); jc = nyash_rust::jit::config::apply_runtime_caps(jc, caps); // Optional DOT emit via CLI (ensures dump is on when path specified) - if let Some(path) = &self.config.emit_cfg { + if let Some(path) = &groups.emit.emit_cfg { std::env::set_var("NYASH_JIT_DOT", path); jc.dump = true; } diff --git a/src/runner/modes/bench.rs b/src/runner/modes/bench.rs index 3deaba3b..9c65f174 100644 --- a/src/runner/modes/bench.rs +++ b/src/runner/modes/bench.rs @@ -176,10 +176,11 @@ impl NyashRunner { // Force JIT mode for this run std::env::set_var("NYASH_JIT_EXEC", "1"); std::env::set_var("NYASH_JIT_THRESHOLD", "1"); - if self.config.jit_stats { + let groups = self.config.as_groups(); + if groups.backend.jit.stats { std::env::set_var("NYASH_JIT_STATS", "1"); } - if self.config.jit_stats_json { + if groups.backend.jit.stats_json { std::env::set_var("NYASH_JIT_STATS_JSON", "1"); } let start = std::time::Instant::now(); diff --git a/src/runner/modes/common.rs b/src/runner/modes/common.rs index 6c4e920e..a3344c08 100644 --- a/src/runner/modes/common.rs +++ b/src/runner/modes/common.rs @@ -27,7 +27,8 @@ impl NyashRunner { } } // Direct v0 bridge when requested via CLI/env - let use_ny_parser = self.config.parser_ny || std::env::var("NYASH_USE_NY_PARSER").ok().as_deref() == Some("1"); + let groups = self.config.as_groups(); + let use_ny_parser = groups.parser.parser_ny || std::env::var("NYASH_USE_NY_PARSER").ok().as_deref() == Some("1"); if use_ny_parser { let code = match fs::read_to_string(filename) { Ok(content) => content, @@ -45,7 +46,7 @@ impl NyashRunner { } } // AST dump mode - if self.config.dump_ast { + if groups.debug.dump_ast { println!("🧠 Nyash AST Dump - Processing file: {}", filename); let code = match fs::read_to_string(filename) { Ok(content) => content, @@ -60,20 +61,20 @@ impl NyashRunner { } // MIR dump/verify - if self.config.dump_mir || self.config.verify_mir { + if groups.debug.dump_mir || groups.debug.verify_mir { crate::cli_v!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename); self.execute_mir_mode(filename); return; } // WASM / AOT (feature-gated) - if self.config.compile_wasm { + if groups.compile_wasm { #[cfg(feature = "wasm-backend")] { self.execute_wasm_mode(filename); return; } #[cfg(not(feature = "wasm-backend"))] { eprintln!("❌ WASM backend not available. Please rebuild with: cargo build --features wasm-backend"); process::exit(1); } } - if self.config.compile_native { + if groups.compile_native { #[cfg(feature = "cranelift-jit")] { self.execute_aot_mode(filename); return; } #[cfg(not(feature = "cranelift-jit"))] @@ -81,7 +82,7 @@ impl NyashRunner { } // Backend selection - match self.config.backend.as_str() { + match groups.backend.backend.as_str() { "mir" => { crate::cli_v!("🚀 Nyash MIR Interpreter - Executing file: {} 🚀", filename); self.execute_mir_interpreter_mode(filename); diff --git a/src/runner/modes/mir.rs b/src/runner/modes/mir.rs index 894c1a0d..1f4f4efe 100644 --- a/src/runner/modes/mir.rs +++ b/src/runner/modes/mir.rs @@ -36,8 +36,9 @@ impl NyashRunner { } }; + let groups = self.config.as_groups(); // Verify MIR if requested - if self.config.verify_mir { + if groups.debug.verify_mir { println!("🔍 Verifying MIR..."); match &compile_result.verification_result { Ok(()) => println!("✅ MIR verification passed!"), @@ -52,13 +53,13 @@ impl NyashRunner { } // Dump MIR if requested - if self.config.dump_mir { - let mut printer = if self.config.mir_verbose { + if groups.debug.dump_mir { + let mut printer = if groups.debug.mir_verbose { MirPrinter::verbose() } else { MirPrinter::new() }; - if self.config.mir_verbose_effects { + if groups.debug.mir_verbose_effects { printer.set_show_effects_inline(true); } println!("🚀 MIR Output for {}:", filename); @@ -66,7 +67,7 @@ impl NyashRunner { } // Emit MIR JSON if requested and exit - if let Some(path) = self.config.emit_mir_json.as_ref() { + if let Some(path) = groups.emit.emit_mir_json.as_ref() { let p = std::path::Path::new(path); if let Err(e) = crate::runner::mir_json_emit::emit_mir_json_for_harness(&compile_result.module, p) { eprintln!("❌ MIR JSON emit error: {}", e); @@ -77,12 +78,12 @@ impl NyashRunner { } // Emit native executable via ny-llvmc (crate) and exit - if let Some(exe_out) = self.config.emit_exe.as_ref() { + if let Some(exe_out) = groups.emit.emit_exe.as_ref() { if let Err(e) = crate::runner::modes::common_util::exec::ny_llvmc_emit_exe_lib( &compile_result.module, exe_out, - self.config.emit_exe_nyrt.as_deref(), - self.config.emit_exe_libs.as_deref(), + groups.emit.emit_exe_nyrt.as_deref(), + groups.emit.emit_exe_libs.as_deref(), ) { eprintln!("❌ {}", e); std::process::exit(1); diff --git a/src/runner/pipe_io.rs b/src/runner/pipe_io.rs index b9c3e7c3..d59ab394 100644 --- a/src/runner/pipe_io.rs +++ b/src/runner/pipe_io.rs @@ -15,10 +15,11 @@ impl NyashRunner { /// Try to handle `--ny-parser-pipe` / `--json-file` flow. /// Returns true if the request was handled (program should return early). pub(super) fn try_run_json_v0_pipe(&self) -> bool { - if !(self.config.ny_parser_pipe || self.config.json_file.is_some()) { + let groups = self.config.as_groups(); + if !(groups.parser.ny_parser_pipe || groups.parser.json_file.is_some()) { return false; } - let json = if let Some(path) = &self.config.json_file { + let json = if let Some(path) = &groups.parser.json_file { match std::fs::read_to_string(path) { Ok(s) => s, Err(e) => {