merge: selfhosting-dev <- origin/main; prefer main updates in cranelift builder (ARROW removal/SHR adoption)

This commit is contained in:
Selfhosting Dev
2025-09-08 04:33:50 +09:00
407 changed files with 15841 additions and 1015 deletions

View File

@ -0,0 +1,54 @@
#[test]
fn llvm_bitops_compile_and_exec() {
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, BasicBlockId, ConstValue, MirType, instruction::BinaryOp};
use crate::backend::vm::VM;
// Build MIR: compute sum of bitwise/shift ops -> 48
let sig = FunctionSignature { name: "Main.main".into(), params: vec![], return_type: MirType::Integer, effects: Default::default() };
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
let bb = f.entry_block;
// Constants
let c5 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c5, value: ConstValue::Integer(5) });
let c3 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c3, value: ConstValue::Integer(3) });
let c2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c2, value: ConstValue::Integer(2) });
let c1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c1, value: ConstValue::Integer(1) });
let c32 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c32, value: ConstValue::Integer(32) });
let c5_sh = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c5_sh, value: ConstValue::Integer(5) });
let c3_sh = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: c3_sh, value: ConstValue::Integer(3) });
// a = 5 & 3 -> 1
let a = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: a, op: BinaryOp::BitAnd, lhs: c5, rhs: c3 });
// b = 5 | 2 -> 7
let b = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: b, op: BinaryOp::BitOr, lhs: c5, rhs: c2 });
// c = 5 ^ 1 -> 4
let c = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: c, op: BinaryOp::BitXor, lhs: c5, rhs: c1 });
// d = 1 << 5 -> 32
let d = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: d, op: BinaryOp::Shl, lhs: c1, rhs: c5_sh });
// e = 32 >> 3 -> 4
let e = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: e, op: BinaryOp::Shr, lhs: c32, rhs: c3_sh });
// sum = a + b + c + d + e
let t1 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: t1, op: BinaryOp::Add, lhs: a, rhs: b });
let t2 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: t2, op: BinaryOp::Add, lhs: t1, rhs: c });
let t3 = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: t3, op: BinaryOp::Add, lhs: t2, rhs: d });
let sum = f.next_value_id(); f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BinOp { dst: sum, op: BinaryOp::Add, lhs: t3, rhs: e });
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sum) });
let mut m = MirModule::new("bitops".into()); m.add_function(f);
// VM executes to 48
let mut vm = VM::new();
let out = vm.execute_module(&m).expect("vm exec");
assert_eq!(out.to_string_box().value, "48");
// LLVM: ensure lowering/emit succeeds; compile_and_execute should also return 48 (via MIR interpreter fallback)
#[cfg(feature = "llvm")]
{
use crate::backend::llvm;
let tmp = format!("{}/target/aot_objects/test_bitops", env!("CARGO_MANIFEST_DIR"));
llvm::compile_to_object(&m, &format!("{}.o", tmp)).expect("llvm emit");
let out2 = llvm::compile_and_execute(&m, &tmp).expect("llvm compile&exec");
assert_eq!(out2.to_string_box().value, "48");
}
}

View File

@ -0,0 +1,83 @@
mod tests {
use crate::mir::{
MirModule, MirFunction, FunctionSignature, MirType, BasicBlock, BasicBlockId, ValueId,
MirInstruction as I,
};
use crate::mir::optimizer::MirOptimizer;
fn mk_func(name: &str) -> (MirFunction, BasicBlockId) {
let sig = FunctionSignature { name: name.to_string(), params: vec![], return_type: MirType::Void, effects: crate::mir::effect::EffectMask::PURE };
let entry = BasicBlockId::new(0);
(MirFunction::new(sig, entry), entry)
}
#[test]
fn core13_normalizes_array_get_set_to_boxcall() {
// Build function with ArrayGet/ArraySet, then optimize under Core-13
let (mut f, bb0) = mk_func("core13_array_norm");
let mut b0 = BasicBlock::new(bb0);
let arr = ValueId::new(0);
let idx = ValueId::new(1);
let val = ValueId::new(2);
let dst = ValueId::new(3);
b0.add_instruction(I::ArrayGet { dst, array: arr, index: idx });
b0.add_instruction(I::ArraySet { array: arr, index: idx, value: val });
b0.add_instruction(I::Return { value: None });
f.add_block(b0);
let mut m = MirModule::new("test_core13_array".into());
m.add_function(f);
let mut opt = MirOptimizer::new();
let _ = opt.optimize_module(&mut m);
let func = m.get_function("core13_array_norm").unwrap();
let block = func.get_block(bb0).unwrap();
// Expect only BoxCall in place of ArrayGet/ArraySet
let mut saw_get = false;
let mut saw_set = false;
for inst in block.all_instructions() {
match inst {
I::BoxCall { method, .. } if method == "get" => { saw_get = true; },
I::BoxCall { method, .. } if method == "set" => { saw_set = true; },
I::ArrayGet { .. } | I::ArraySet { .. } => panic!("legacy Array* must be normalized under Core-13"),
_ => {}
}
}
assert!(saw_get && saw_set, "expected BoxCall(get) and BoxCall(set) present");
}
#[test]
fn core13_normalizes_ref_get_set_to_boxcall() {
// Build function with RefGet/RefSet, then optimize under Core-13
let (mut f, bb0) = mk_func("core13_ref_norm");
let mut b0 = BasicBlock::new(bb0);
let r = ValueId::new(10);
let v = ValueId::new(11);
let dst = ValueId::new(12);
b0.add_instruction(I::RefGet { dst, reference: r, field: "foo".to_string() });
b0.add_instruction(I::RefSet { reference: r, field: "bar".to_string(), value: v });
b0.add_instruction(I::Return { value: None });
f.add_block(b0);
let mut m = MirModule::new("test_core13_ref".into());
m.add_function(f);
let mut opt = MirOptimizer::new();
let _ = opt.optimize_module(&mut m);
let func = m.get_function("core13_ref_norm").unwrap();
let block = func.get_block(bb0).unwrap();
// Expect BoxCall(getField/setField), a Barrier(Write) before setField, and no RefGet/RefSet remain
let mut saw_get_field = false;
let mut saw_set_field = false;
for inst in block.all_instructions() {
match inst {
I::BoxCall { method, .. } if method == "getField" => { saw_get_field = true; },
I::BoxCall { method, .. } if method == "setField" => { saw_set_field = true; },
I::RefGet { .. } | I::RefSet { .. } => panic!("legacy Ref* must be normalized under Core-13"),
_ => {}
}
}
assert!(saw_get_field && saw_set_field, "expected BoxCall(getField) and BoxCall(setField) present");
}
}

View File

@ -0,0 +1,20 @@
use crate::parser::NyashParser;
use crate::ast::ASTNode;
#[test]
fn parse_bitops_and_shift_precedence() {
// Expression: 1 + 2 << 3 & 7
// Precedence: shift before add, then &: (1 + (2 << 3)) & 7
let code = "return 1 + 2 << 3 & 7";
let ast = NyashParser::parse_from_string(code).expect("parse ok");
// Just ensure it parses into a Program and contains a Return; deeper tree checks are optional here
fn has_return(n: &ASTNode) -> bool {
match n {
ASTNode::Program { statements, .. } => statements.iter().any(has_return),
ASTNode::Return { .. } => true,
_ => false,
}
}
assert!(has_return(&ast));
}

View File

@ -0,0 +1,25 @@
use crate::parser::NyashParser;
use crate::interpreter::NyashInterpreter;
#[test]
fn vm_exec_bitwise_and_shift() {
let code = r#"
return (5 & 3) + (5 | 2) + (5 ^ 1) + (1 << 5) + (32 >> 3)
"#;
let ast = NyashParser::parse_from_string(code).expect("parse ok");
let mut interp = NyashInterpreter::new();
let out = interp.execute(ast).expect("exec ok");
assert_eq!(out.to_string_box().value, "48");
}
#[test]
fn vm_exec_shift_masking() {
// 1 << 100 should mask to 1 << (100 & 63) = 1 << 36
let code = r#" return 1 << 100 "#;
let ast = NyashParser::parse_from_string(code).expect("parse ok");
let mut interp = NyashInterpreter::new();
let out = interp.execute(ast).expect("exec ok");
// compute expected as i64
let expected = (1_i64 as i64).wrapping_shl((100_u32) & 63);
assert_eq!(out.to_string_box().value, expected.to_string());
}