merge: selfhosting-dev <- origin/main; prefer main updates in cranelift builder (ARROW removal/SHR adoption)
This commit is contained in:
54
src/tests/llvm_bitops_test.rs
Normal file
54
src/tests/llvm_bitops_test.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
83
src/tests/mir_core13_normalize.rs
Normal file
83
src/tests/mir_core13_normalize.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
20
src/tests/parser_bitops_test.rs
Normal file
20
src/tests/parser_bitops_test.rs
Normal 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));
|
||||
}
|
||||
|
||||
25
src/tests/vm_bitops_test.rs
Normal file
25
src/tests/vm_bitops_test.rs
Normal 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());
|
||||
}
|
||||
Reference in New Issue
Block a user