Phase 10.7 (NYASH_JIT_PHI_MIN=1): Minimal PHI support for single-diamond via block params; extend IRBuilder with block-param/jump-args APIs; implement in CraneliftBuilder; update LowerCore two-pass for PHI wiring.
This commit is contained in:
@ -36,6 +36,17 @@ pub trait IRBuilder {
|
||||
fn br_if_top_is_true(&mut self, _then_index: usize, _else_index: usize) { }
|
||||
/// Optional: unconditional jump to target block index
|
||||
fn jump_to(&mut self, _target_index: usize) { }
|
||||
/// Optional: ensure target block has one i64 block param (for minimal PHI)
|
||||
fn ensure_block_param_i64(&mut self, _index: usize) { }
|
||||
/// Optional: push current block's first param (i64) onto the value stack
|
||||
fn push_block_param_i64(&mut self) { }
|
||||
/// Optional: conditional branch with explicit arg counts for then/else; pops args from stack
|
||||
fn br_if_with_args(&mut self, _then_index: usize, _else_index: usize, _then_n: usize, _else_n: usize) {
|
||||
// fallback to no-arg br_if
|
||||
self.br_if_top_is_true(_then_index, _else_index);
|
||||
}
|
||||
/// Optional: jump with explicit arg count; pops args from stack
|
||||
fn jump_with_args(&mut self, _target_index: usize, _n: usize) { self.jump_to(_target_index); }
|
||||
}
|
||||
|
||||
pub struct NoopBuilder {
|
||||
@ -76,6 +87,7 @@ pub struct CraneliftBuilder {
|
||||
// Phase 10.7: basic block wiring state
|
||||
blocks: Vec<cranelift_codegen::ir::Block>,
|
||||
current_block_index: Option<usize>,
|
||||
block_param_counts: std::collections::HashMap<usize, usize>,
|
||||
// Finalized function pointer (if any)
|
||||
compiled_closure: Option<std::sync::Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>,
|
||||
// Desired simple ABI (Phase 10_c minimal): i64 params count and i64 return
|
||||
@ -427,6 +439,74 @@ impl IRBuilder for CraneliftBuilder {
|
||||
self.stats.3 += 1;
|
||||
fb.finalize();
|
||||
}
|
||||
fn ensure_block_param_i64(&mut self, index: usize) {
|
||||
use cranelift_codegen::ir::types;
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if index >= self.blocks.len() { return; }
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
let count = self.block_param_counts.get(&index).copied().unwrap_or(0);
|
||||
if count == 0 {
|
||||
let b = self.blocks[index];
|
||||
let _v = fb.append_block_param(b, types::I64);
|
||||
self.block_param_counts.insert(index, 1);
|
||||
}
|
||||
fb.finalize();
|
||||
}
|
||||
fn push_block_param_i64(&mut self) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
use cranelift_codegen::ir::types;
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
// Determine current block
|
||||
let b = if let Some(idx) = self.current_block_index { self.blocks[idx] } else if let Some(b) = self.entry_block { b } else { fb.create_block() };
|
||||
// Fetch first param if exists
|
||||
let params = fb.func.dfg.block_params(b).to_vec();
|
||||
if let Some(v) = params.get(0).copied() { self.value_stack.push(v); }
|
||||
else {
|
||||
// defensive: push 0
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
self.value_stack.push(zero);
|
||||
}
|
||||
fb.finalize();
|
||||
}
|
||||
fn br_if_with_args(&mut self, then_index: usize, else_index: usize, then_n: usize, else_n: usize) {
|
||||
use cranelift_codegen::ir::{types, condcodes::IntCC};
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; }
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
||||
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||
// Condition
|
||||
let cond_b1 = if let Some(v) = self.value_stack.pop() {
|
||||
let ty = fb.func.dfg.value_type(v);
|
||||
if ty == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, v, 0) } else { v }
|
||||
} else {
|
||||
let zero = fb.ins().iconst(types::I64, 0);
|
||||
fb.ins().icmp_imm(IntCC::NotEqual, zero, 0)
|
||||
};
|
||||
// Pop else args then then args (so stack order can be value-friendly)
|
||||
let mut else_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
for _ in 0..else_n { if let Some(v) = self.value_stack.pop() { else_args.push(v); } }
|
||||
else_args.reverse();
|
||||
let mut then_args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
for _ in 0..then_n { if let Some(v) = self.value_stack.pop() { then_args.push(v); } }
|
||||
then_args.reverse();
|
||||
fb.ins().brif(cond_b1, self.blocks[then_index], &then_args, self.blocks[else_index], &else_args);
|
||||
self.stats.3 += 1;
|
||||
fb.finalize();
|
||||
}
|
||||
fn jump_with_args(&mut self, target_index: usize, n: usize) {
|
||||
use cranelift_frontend::FunctionBuilder;
|
||||
if target_index >= self.blocks.len() { return; }
|
||||
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
|
||||
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
|
||||
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
|
||||
let mut args: Vec<cranelift_codegen::ir::Value> = Vec::new();
|
||||
for _ in 0..n { if let Some(v) = self.value_stack.pop() { args.push(v); } }
|
||||
args.reverse();
|
||||
fb.ins().jump(self.blocks[target_index], &args);
|
||||
self.stats.3 += 1;
|
||||
fb.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
@ -474,6 +554,7 @@ impl CraneliftBuilder {
|
||||
entry_block: None,
|
||||
blocks: Vec::new(),
|
||||
current_block_index: None,
|
||||
block_param_counts: std::collections::HashMap::new(),
|
||||
compiled_closure: None,
|
||||
desired_argc: 0,
|
||||
desired_has_ret: true,
|
||||
|
||||
@ -24,10 +24,27 @@ impl LowerCore {
|
||||
for (i, v) in func.params.iter().copied().enumerate() {
|
||||
self.param_index.insert(v, i);
|
||||
}
|
||||
// Prepare block mapping (Phase 10.7 stub): deterministic ordering by sorted keys
|
||||
// Prepare block mapping (Phase 10.7): deterministic ordering by sorted keys
|
||||
let mut bb_ids: Vec<_> = func.blocks.keys().copied().collect();
|
||||
bb_ids.sort_by_key(|b| b.0);
|
||||
builder.prepare_blocks(bb_ids.len());
|
||||
// Optional: collect single-PHI targets for minimal PHI path
|
||||
let enable_phi_min = std::env::var("NYASH_JIT_PHI_MIN").ok().as_deref() == Some("1");
|
||||
let mut phi_targets: std::collections::HashMap<crate::mir::BasicBlockId, std::collections::HashMap<crate::mir::BasicBlockId, crate::mir::ValueId>> = std::collections::HashMap::new();
|
||||
if enable_phi_min {
|
||||
for (bb_id, bb) in func.blocks.iter() {
|
||||
// gather Phi instructions in this block
|
||||
let mut phis: Vec<&crate::mir::MirInstruction> = Vec::new();
|
||||
for ins in bb.instructions.iter() { if let crate::mir::MirInstruction::Phi { .. } = ins { phis.push(ins); } }
|
||||
if phis.len() == 1 {
|
||||
if let crate::mir::MirInstruction::Phi { inputs, .. } = phis[0] {
|
||||
let mut map: std::collections::HashMap<crate::mir::BasicBlockId, crate::mir::ValueId> = std::collections::HashMap::new();
|
||||
for (pred, val) in inputs.iter() { map.insert(*pred, *val); }
|
||||
phi_targets.insert(*bb_id, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.prepare_signature_i64(func.params.len(), true);
|
||||
builder.begin_function(&func.signature.name);
|
||||
// Iterate blocks in the sorted order to keep indices stable
|
||||
@ -48,13 +65,34 @@ impl LowerCore {
|
||||
// Map BasicBlockId -> index
|
||||
let then_index = bb_ids.iter().position(|x| x == then_bb).unwrap_or(0);
|
||||
let else_index = bb_ids.iter().position(|x| x == else_bb).unwrap_or(0);
|
||||
builder.br_if_top_is_true(then_index, else_index);
|
||||
if enable_phi_min {
|
||||
// For minimal PHI, pass one i64 arg if successor defines a single PHI with this block as pred
|
||||
let mut then_n = 0usize;
|
||||
let mut else_n = 0usize;
|
||||
if let Some(pred_map) = phi_targets.get(then_bb) {
|
||||
if let Some(v) = pred_map.get(bb_id) { self.push_value_if_known_or_param(builder, v); then_n = 1; builder.ensure_block_param_i64(then_index); }
|
||||
}
|
||||
if let Some(pred_map) = phi_targets.get(else_bb) {
|
||||
if let Some(v) = pred_map.get(bb_id) { self.push_value_if_known_or_param(builder, v); else_n = 1; builder.ensure_block_param_i64(else_index); }
|
||||
}
|
||||
builder.br_if_with_args(then_index, else_index, then_n, else_n);
|
||||
} else {
|
||||
builder.br_if_top_is_true(then_index, else_index);
|
||||
}
|
||||
builder.seal_block(then_index);
|
||||
builder.seal_block(else_index);
|
||||
}
|
||||
crate::mir::MirInstruction::Jump { target } => {
|
||||
let target_index = bb_ids.iter().position(|x| x == target).unwrap_or(0);
|
||||
builder.jump_to(target_index);
|
||||
if enable_phi_min {
|
||||
let mut n = 0usize;
|
||||
if let Some(pred_map) = phi_targets.get(target) {
|
||||
if let Some(v) = pred_map.get(bb_id) { self.push_value_if_known_or_param(builder, v); n = 1; builder.ensure_block_param_i64(target_index); }
|
||||
}
|
||||
builder.jump_with_args(target_index, n);
|
||||
} else {
|
||||
builder.jump_to(target_index);
|
||||
}
|
||||
builder.seal_block(target_index);
|
||||
}
|
||||
_ => {
|
||||
@ -168,6 +206,10 @@ impl LowerCore {
|
||||
if let Some(v) = value { self.push_value_if_known_or_param(b, v); }
|
||||
b.emit_return()
|
||||
}
|
||||
I::Phi { .. } => {
|
||||
// Minimal PHI: load current block param as value (i64)
|
||||
b.push_block_param_i64();
|
||||
}
|
||||
I::ArrayGet { array, index, .. } => {
|
||||
if std::env::var("NYASH_JIT_HOSTCALL").ok().as_deref() == Some("1") {
|
||||
// Push args: array param index (or -1), index (known or 0)
|
||||
|
||||
Reference in New Issue
Block a user