Phase 22.x WIP: LLVM backend improvements + MIR builder enhancements

LLVM backend improvements:
- Add native LLVM backend support (NYASH_LLVM_BACKEND=native)
- Add crate backend selector with priority (crate > llvmlite)
- Add native_llvm_builder.py for native IR generation
- Add NYASH_LLVM_NATIVE_TRACE=1 for IR dump

MIR builder enhancements:
- Refactor lower_if_compare_* boxes for better code generation
- Refactor lower_return_* boxes for optimized returns
- Refactor lower_loop_* boxes for loop handling
- Refactor lower_method_* boxes for method calls
- Update pattern_util_box for better pattern matching

Smoke tests:
- Add phase2100 S3 backend selector tests (17 new tests)
- Add phase2120 native backend tests (4 new tests)
- Add phase2034 MIR builder internal tests (2 new tests)
- Add phase2211 TLV shim parity test

Documentation:
- Update ENV_VARS.md with LLVM backend variables
- Update CURRENT_TASK.md with progress
- Update README.md and CHANGELOG.md

Config:
- Add NYASH_LLVM_BACKEND env support in src/config/env.rs
- Update ny_mir_builder.sh for backend selection
- Update dispatch.rs for backend routing

Tools:
- Add tools/native_llvm_builder.py
- Update smokes/v2/profiles/quick/core/phase2100/run_all.sh

Known: Many Hako builder internal files modified for optimization
This commit is contained in:
nyash-codex
2025-11-09 23:40:36 +09:00
parent fb6129183d
commit f6c5dc9e43
65 changed files with 1965 additions and 434 deletions

View File

@ -33,9 +33,12 @@ impl NyashEnv {
// Global current env config (thread-safe)
use once_cell::sync::OnceCell;
use std::collections::HashSet;
use std::sync::Mutex;
use std::sync::RwLock;
static GLOBAL_ENV: OnceCell<RwLock<NyashEnv>> = OnceCell::new();
static WARNED_ALIASES: OnceCell<Mutex<HashSet<String>>> = OnceCell::new();
// フェーズM.2: PHI_ON_GATED_WARNED削除phi-legacy簡略化により不要
pub fn current() -> NyashEnv {
@ -376,11 +379,19 @@ pub fn cli_verbose() -> bool {
}
pub fn enable_using() -> bool {
// Phase 15: デフォルトONusing systemはメイン機能
// NYASH_ENABLE_USING=0 で明示的に無効化可能
// NYASH_ENABLE_USING=0 で明示的に無効化可能。HAKO_ENABLE_USING は互換のため受理(警告)。
match std::env::var("NYASH_ENABLE_USING").ok().as_deref() {
Some("0") | Some("false") | Some("off") => false,
_ => true, // デフォルト: ON
Some("0") | Some("false") | Some("off") => return false,
Some(_) => return true,
None => {}
}
// Fallback to alias
if let Some(v) = std::env::var("HAKO_ENABLE_USING").ok() {
warn_alias_once("HAKO_ENABLE_USING", "NYASH_ENABLE_USING");
let lv = v.to_ascii_lowercase();
return !(lv == "0" || lv == "false" || lv == "off");
}
true // default ON
}
// ---- Using profiles (dev|ci|prod) ----
@ -466,7 +477,14 @@ pub fn ny_compiler_stage3() -> bool {
/// When enabled, the Rust parser accepts Stage-3 surface (try/catch/finally, throw).
/// Default is OFF to keep Stage-2 stable.
pub fn parser_stage3() -> bool {
std::env::var("NYASH_PARSER_STAGE3").ok().as_deref() == Some("1")
if std::env::var("NYASH_PARSER_STAGE3").ok().as_deref() == Some("1") {
return true;
}
if std::env::var("HAKO_PARSER_STAGE3").ok().as_deref() == Some("1") {
warn_alias_once("HAKO_PARSER_STAGE3", "NYASH_PARSER_STAGE3");
return true;
}
false
}
/// Parser gate for BlockPostfix Catch acceptance
@ -578,3 +596,72 @@ pub fn oob_strict_fail() -> bool {
.or_else(|| env_flag("NYASH_OOB_STRICT_FAIL"))
.unwrap_or(false)
}
/// Primary verification route: return true when Hakorune VM is requested as primary.
/// Accepts HAKO_VERIFY_PRIMARY=hakovm (preferred) or legacy HAKO_ROUTE_HAKOVM=1 (deprecated, warns).
pub fn verify_primary_is_hakovm() -> bool {
if std::env::var("HAKO_VERIFY_PRIMARY").ok().as_deref() == Some("hakovm") {
return true;
}
if env_bool("HAKO_ROUTE_HAKOVM") {
warn_alias_once("HAKO_ROUTE_HAKOVM", "HAKO_VERIFY_PRIMARY=hakovm");
return true;
}
false
}
fn warn_alias_once(alias: &str, primary: &str) {
let set = WARNED_ALIASES.get_or_init(|| Mutex::new(HashSet::new()));
if let Ok(mut s) = set.lock() {
if !s.contains(alias) {
eprintln!("[deprecate/env] '{}' is deprecated; use '{}'", alias, primary);
s.insert(alias.to_string());
}
}
}
// ---- ENV consolidation helpers (Phase 21.10/22.1) ----
/// LLVM opt level (primary: NYASH_LLVM_OPT_LEVEL; alias: HAKO_LLVM_OPT_LEVEL)
/// Returns string level (e.g., "0", "1", ...). Default: "0" when unset.
pub fn llvm_opt_level() -> String {
if let Some(v) = std::env::var("NYASH_LLVM_OPT_LEVEL").ok() {
return v;
}
if let Some(v) = std::env::var("HAKO_LLVM_OPT_LEVEL").ok() {
warn_alias_once("HAKO_LLVM_OPT_LEVEL", "NYASH_LLVM_OPT_LEVEL");
return v;
}
"0".to_string()
}
/// GateC(Core) route request (primary: NYASH_GATE_C_CORE; alias: HAKO_GATE_C_CORE)
pub fn gate_c_core() -> bool {
if env_bool("NYASH_GATE_C_CORE") { return true; }
if env_bool("HAKO_GATE_C_CORE") {
warn_alias_once("HAKO_GATE_C_CORE", "NYASH_GATE_C_CORE");
return true;
}
false
}
/// Consolidated toggle for selfhost NY compiler pipeline.
/// Primary: NYASH_USE_NY_COMPILER=0|1. Legacy disables accepted (with warning):
/// NYASH_DISABLE_NY_COMPILER/HAKO_DISABLE_NY_COMPILER (any true value disables).
pub fn use_ny_compiler() -> bool {
// Primary knob takes precedence when explicitly set
if let Some(v) = std::env::var("NYASH_USE_NY_COMPILER").ok() {
let lv = v.trim().to_ascii_lowercase();
return lv == "1" || lv == "true" || lv == "on";
}
// Legacy disable aliases — if any is true, treat as disabled and warn
if env_bool("NYASH_DISABLE_NY_COMPILER") {
warn_alias_once("NYASH_DISABLE_NY_COMPILER", "NYASH_USE_NY_COMPILER=0");
return false;
}
if env_bool("HAKO_DISABLE_NY_COMPILER") {
warn_alias_once("HAKO_DISABLE_NY_COMPILER", "NYASH_USE_NY_COMPILER=0");
return false;
}
// Default: ON (MVP selfhost path)
true
}

View File

@ -24,8 +24,8 @@ def lower_function(builder, func_data: Dict[str, Any]):
# Determine function signature
if name == "ny_main":
# Special case: ny_main returns i32
func_ty = ir.FunctionType(builder.i32, [])
# Special case: ny_main returns i64 to match runtime (nyrt) expectations
func_ty = ir.FunctionType(builder.i64, [])
else:
# Default: i64(i64, ...) signature; derive arity from '/N' suffix when params missing
m = re.search(r"/(\d+)$", name)

View File

@ -45,7 +45,23 @@ def lower_binop(
lhs_val = ir.Constant(ir.IntType(64), 0)
if rhs_val is None:
rhs_val = ir.Constant(ir.IntType(64), 0)
# Normalize operation aliases (textual -> symbolic)
op_raw = op or ''
op_l = op_raw.lower()
alias = {
'add': '+', 'plus': '+',
'sub': '-', 'minus': '-',
'mul': '*', 'times': '*',
'div': '/',
'mod': '%', 'rem': '%',
'band': '&', 'bitand': '&',
'bor': '|', 'bitor': '|',
'bxor': '^', 'xor': '^',
'shl': '<<',
'shr': '>>', 'ashr': '>>',
}
op = alias.get(op_l, op_raw)
# Relational/equality operators delegate to compare
if op in ('==','!=','<','>','<=','>='):
# Delegate to compare with resolver/preds context to maintain dominance via localization

View File

@ -97,9 +97,16 @@ def lower_return(
# Pointer type - null
ret_val = ir.Constant(return_type, None)
# If still zero-like and we have predecessor snapshots, synthesize a minimal PHI at block head.
# If still zero-like (typed zero) and we have predecessor snapshots, synthesize a minimal PHI at block head.
try:
zero_like = isinstance(ret_val, ir.Constant)
zero_like = False
if isinstance(ret_val, ir.Constant):
if isinstance(return_type, ir.IntType):
zero_like = (str(ret_val) == str(ir.Constant(return_type, 0)))
elif isinstance(return_type, ir.DoubleType):
zero_like = (str(ret_val) == str(ir.Constant(return_type, 0.0)))
elif isinstance(return_type, ir.PointerType):
zero_like = (str(ret_val) == str(ir.Constant(return_type, None)))
if zero_like and preds is not None and block_end_values is not None and bb_map is not None and isinstance(value_id, int):
# Derive current block id from name like 'bb3'
cur_bid = None

View File

@ -152,7 +152,8 @@ class NyashLLVMBuilder:
else:
arity = int(m.group(1)) if m else len(params_list)
if name == "ny_main":
fty = ir.FunctionType(self.i32, [])
# Align with runtime expectation: ny_main returns i64
fty = ir.FunctionType(self.i64, [])
else:
fty = ir.FunctionType(self.i64, [self.i64] * arity)
exists = False

View File

@ -13,8 +13,7 @@ fn main() {
// If NYASH_VERIFY_JSON is present and route is requested, execute and exit.
// This avoids plugin host/registry initialization and keeps output minimal.
let has_json = std::env::var("NYASH_VERIFY_JSON").is_ok();
let route = nyash_rust::config::env::env_bool("HAKO_ROUTE_HAKOVM")
|| std::env::var("HAKO_VERIFY_PRIMARY").ok().as_deref() == Some("hakovm");
let route = nyash_rust::config::env::verify_primary_is_hakovm();
// Force flag may allow hv1-inline without route
let force_hv1_flag = nyash_rust::config::env::env_bool("HAKO_VERIFY_V1_FORCE_HAKOVM");
if has_json && (route || force_hv1_flag) {

View File

@ -12,30 +12,9 @@ pub(crate) fn execute_file_with_backend(runner: &NyashRunner, filename: &str) {
// Note: hv1 direct route is now handled at main.rs entry point (before NyashRunner creation).
// This function is only called after plugins and runner initialization have already occurred.
// Selfhost pipeline (Ny -> JSON v0)
// Default: ON. Backwardcompat envs:
// - NYASH_USE_NY_COMPILER={1|true|on} to force ON
// - NYASH_USE_NY_COMPILER={0|false|off} or NYASH_DISABLE_NY_COMPILER/HAKO_DISABLE_NY_COMPILER to disable
let mut use_selfhost = true;
if let Ok(v) = std::env::var("NYASH_USE_NY_COMPILER") {
let lv = v.trim().to_ascii_lowercase();
use_selfhost = matches!(lv.as_str(), "1" | "true" | "on");
}
let disabled = std::env::var("NYASH_DISABLE_NY_COMPILER")
.ok()
.map(|v| {
let lv = v.trim().to_ascii_lowercase();
matches!(lv.as_str(), "1" | "true" | "on")
})
.unwrap_or(false)
|| std::env::var("HAKO_DISABLE_NY_COMPILER")
.ok()
.map(|v| {
let lv = v.trim().to_ascii_lowercase();
matches!(lv.as_str(), "1" | "true" | "on")
})
.unwrap_or(false);
if use_selfhost && !disabled {
// Selfhost pipeline (Ny -> JSON v0) — consolidated env toggle
// Primary: NYASH_USE_NY_COMPILER=0|1; legacy disables accepted with deprecation warning
if crate::config::env::use_ny_compiler() {
if runner.try_run_selfhost_pipeline(filename) {
return;
} else {