llvm(py): introduce BuildCtx + trace hub; refactor if-merge prepass + PHI wiring into module; unify logs; ctx-enable compare/ret/call/boxcall/externcall/typeop/newbox/safepoint; curated smoke option for if-merge; README updates; keep behavior stable

This commit is contained in:
Selfhosting Dev
2025-09-17 16:11:01 +09:00
parent 2720884a20
commit a5054a271b
58 changed files with 2002 additions and 311 deletions

View File

@ -0,0 +1,62 @@
use crate::ast::{ASTNode, LiteralValue, Span, BinaryOperator};
use crate::mir::{MirCompiler, MirInstruction};
fn lit_i(i: i64) -> ASTNode {
ASTNode::Literal { value: LiteralValue::Integer(i), span: Span::unknown() }
}
fn bool_lt(lhs: ASTNode, rhs: ASTNode) -> ASTNode {
ASTNode::BinaryOp { operator: BinaryOperator::LessThan, left: Box::new(lhs), right: Box::new(rhs), span: Span::unknown() }
}
#[test]
fn mir13_no_phi_if_merge_inserts_edge_copies_for_return() {
// Force PHI-off mode
std::env::set_var("NYASH_MIR_NO_PHI", "1");
// if (1 < 2) { return 40 } else { return 50 }
let ast = ASTNode::If {
condition: Box::new(bool_lt(lit_i(1), lit_i(2))),
then_body: vec![ASTNode::Return { value: Some(Box::new(lit_i(40))), span: Span::unknown() }],
else_body: Some(vec![ASTNode::Return { value: Some(Box::new(lit_i(50))), span: Span::unknown() }]),
span: Span::unknown(),
};
let mut mc = MirCompiler::with_options(false);
let cr = mc.compile(ast).expect("compile");
let f = cr.module.functions.get("main").expect("function main");
// Find the block that returns a value and capture that return value id
let mut ret_block_id = None;
let mut ret_val = None;
for (bid, bb) in &f.blocks {
if let Some(MirInstruction::Return { value: Some(v) }) = bb.terminator.clone() {
ret_block_id = Some(*bid);
ret_val = Some(v);
break;
}
}
let ret_block = ret_block_id.expect("ret block");
let out_v = ret_val.expect("ret value");
// In PHI-off mode we expect copies into each predecessor of the merge/ret block
let preds: Vec<_> = f
.blocks
.get(&ret_block)
.expect("ret block present")
.predecessors
.iter()
.copied()
.collect();
assert!(preds.len() >= 2, "expected at least two predecessors at merge");
for p in preds {
let bb = f.blocks.get(&p).expect("pred block present");
let has_copy = bb
.instructions
.iter()
.any(|inst| matches!(inst, MirInstruction::Copy { dst, .. } if *dst == out_v));
assert!(has_copy, "expected Copy to out_v in predecessor {:?}", p);
}
}