Core-13 pure: add CI workflows, VM e2e tests, LLVM parity bridge (minimal); do not touch private docs
This commit is contained in:
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");
|
||||
}
|
||||
}
|
||||
|
||||
19
src/tests/mir_pure_e2e_arith.rs
Normal file
19
src/tests/mir_pure_e2e_arith.rs
Normal file
@ -0,0 +1,19 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parser::NyashParser;
|
||||
use crate::backend::VM;
|
||||
|
||||
#[test]
|
||||
fn vm_exec_addition_under_pure_mode() {
|
||||
std::env::set_var("NYASH_MIR_CORE13_PURE", "1");
|
||||
let code = "\nreturn 7 + 35\n";
|
||||
let ast = NyashParser::parse_from_string(code).expect("parse");
|
||||
let mut compiler = crate::mir::MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
let mut vm = VM::new();
|
||||
let out = vm.execute_module(&result.module).expect("vm exec");
|
||||
assert_eq!(out.to_string_box().value, "42");
|
||||
std::env::remove_var("NYASH_MIR_CORE13_PURE");
|
||||
}
|
||||
}
|
||||
|
||||
19
src/tests/mir_pure_e2e_branch.rs
Normal file
19
src/tests/mir_pure_e2e_branch.rs
Normal file
@ -0,0 +1,19 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parser::NyashParser;
|
||||
use crate::backend::VM;
|
||||
|
||||
#[test]
|
||||
fn vm_exec_if_then_return_under_pure_mode() {
|
||||
std::env::set_var("NYASH_MIR_CORE13_PURE", "1");
|
||||
let code = "\nif (1) { return 1 }\nreturn 2\n";
|
||||
let ast = NyashParser::parse_from_string(code).expect("parse");
|
||||
let mut compiler = crate::mir::MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
let mut vm = VM::new();
|
||||
let out = vm.execute_module(&result.module).expect("vm exec");
|
||||
assert_eq!(out.to_string_box().value, "1");
|
||||
std::env::remove_var("NYASH_MIR_CORE13_PURE");
|
||||
}
|
||||
}
|
||||
|
||||
30
src/tests/mir_pure_e2e_vm.rs
Normal file
30
src/tests/mir_pure_e2e_vm.rs
Normal file
@ -0,0 +1,30 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parser::NyashParser;
|
||||
use crate::backend::VM;
|
||||
|
||||
#[test]
|
||||
fn vm_exec_new_string_length_under_pure_mode() {
|
||||
// Enable Core-13 pure mode
|
||||
std::env::set_var("NYASH_MIR_CORE13_PURE", "1");
|
||||
|
||||
// Nyash code: return (new StringBox("Hello")).length()
|
||||
let code = r#"
|
||||
return (new StringBox("Hello")).length()
|
||||
"#;
|
||||
|
||||
// Parse -> MIR -> VM execute
|
||||
let ast = NyashParser::parse_from_string(code).expect("parse");
|
||||
let mut compiler = crate::mir::MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
|
||||
let mut vm = VM::new();
|
||||
let out = vm.execute_module(&result.module).expect("vm exec");
|
||||
// Expect 5 as string (to_string_box) for convenience
|
||||
assert_eq!(out.to_string_box().value, "5");
|
||||
|
||||
// Cleanup
|
||||
std::env::remove_var("NYASH_MIR_CORE13_PURE");
|
||||
}
|
||||
}
|
||||
|
||||
23
src/tests/mir_pure_envbox.rs
Normal file
23
src/tests/mir_pure_envbox.rs
Normal file
@ -0,0 +1,23 @@
|
||||
mod tests {
|
||||
use crate::ast::{ASTNode, LiteralValue, Span};
|
||||
use crate::mir::{MirCompiler, MirPrinter};
|
||||
|
||||
#[test]
|
||||
fn pure_mode_new_emits_env_box_new() {
|
||||
// Enable pure mode
|
||||
std::env::set_var("NYASH_MIR_CORE13_PURE", "1");
|
||||
// new StringBox("Hello")
|
||||
let ast = ASTNode::New {
|
||||
class: "StringBox".to_string(),
|
||||
arguments: vec![ASTNode::Literal { value: LiteralValue::String("Hello".into()), span: Span::unknown() }],
|
||||
type_arguments: vec![],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let mut c = MirCompiler::new();
|
||||
let result = c.compile(ast).expect("compile");
|
||||
let dump = MirPrinter::new().print_module(&result.module);
|
||||
assert!(dump.contains("extern_call env.box.new"), "expected env.box.new in MIR. dump=\n{}", dump);
|
||||
std::env::remove_var("NYASH_MIR_CORE13_PURE");
|
||||
}
|
||||
}
|
||||
|
||||
35
src/tests/mir_pure_llvm_build.rs
Normal file
35
src/tests/mir_pure_llvm_build.rs
Normal file
@ -0,0 +1,35 @@
|
||||
#[cfg(all(test, feature = "llvm"))]
|
||||
mod tests {
|
||||
use crate::parser::NyashParser;
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn llvm_can_build_object_under_pure_mode() {
|
||||
// Enable Core-13 pure mode
|
||||
std::env::set_var("NYASH_MIR_CORE13_PURE", "1");
|
||||
|
||||
// A simple program that exercises env.box.new and locals
|
||||
let code = r#"
|
||||
local s
|
||||
s = new StringBox("abc")
|
||||
return s.length()
|
||||
"#;
|
||||
|
||||
let ast = NyashParser::parse_from_string(code).expect("parse");
|
||||
let mut compiler = crate::mir::MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
|
||||
// Build object via LLVM backend
|
||||
let out = "nyash_pure_llvm_build_test";
|
||||
crate::backend::llvm::compile_to_object(&result.module, &format!("{}.o", out)).expect("llvm object build");
|
||||
|
||||
// Verify object exists and has content
|
||||
let meta = fs::metadata(format!("{}.o", out)).expect("obj exists");
|
||||
assert!(meta.len() > 0, "object file should be non-empty");
|
||||
|
||||
// Cleanup
|
||||
let _ = fs::remove_file(format!("{}.o", out));
|
||||
std::env::remove_var("NYASH_MIR_CORE13_PURE");
|
||||
}
|
||||
}
|
||||
|
||||
27
src/tests/mir_pure_llvm_parity.rs
Normal file
27
src/tests/mir_pure_llvm_parity.rs
Normal file
@ -0,0 +1,27 @@
|
||||
#[cfg(all(test, feature = "llvm"))]
|
||||
mod tests {
|
||||
use crate::parser::NyashParser;
|
||||
use crate::backend::VM;
|
||||
|
||||
#[test]
|
||||
fn llvm_exec_matches_vm_for_addition_under_pure_mode() {
|
||||
std::env::set_var("NYASH_MIR_CORE13_PURE", "1");
|
||||
let code = "\nreturn 7 + 35\n";
|
||||
let ast = NyashParser::parse_from_string(code).expect("parse");
|
||||
let mut compiler = crate::mir::MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
|
||||
// VM result
|
||||
let mut vm = VM::new();
|
||||
let vm_out = vm.execute_module(&result.module).expect("vm exec");
|
||||
let vm_s = vm_out.to_string_box().value;
|
||||
|
||||
// LLVM result (compile+execute parity path)
|
||||
let llvm_out = crate::backend::llvm::compile_and_execute(&result.module, "pure_llvm_parity").expect("llvm exec");
|
||||
let llvm_s = llvm_out.to_string_box().value;
|
||||
|
||||
assert_eq!(vm_s, llvm_s, "VM and LLVM outputs should match");
|
||||
std::env::remove_var("NYASH_MIR_CORE13_PURE");
|
||||
}
|
||||
}
|
||||
|
||||
31
src/tests/mir_pure_locals_normalized.rs
Normal file
31
src/tests/mir_pure_locals_normalized.rs
Normal file
@ -0,0 +1,31 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parser::NyashParser;
|
||||
use crate::mir::MirPrinter;
|
||||
|
||||
#[test]
|
||||
fn locals_rewritten_to_env_local_calls_in_pure_mode() {
|
||||
// Enable Core-13 pure mode
|
||||
std::env::set_var("NYASH_MIR_CORE13_PURE", "1");
|
||||
|
||||
// Use locals and arithmetic so Load/Store would appear without normalization
|
||||
let code = r#"
|
||||
local x
|
||||
x = 10
|
||||
x = x + 32
|
||||
return x
|
||||
"#;
|
||||
|
||||
let ast = NyashParser::parse_from_string(code).expect("parse");
|
||||
let mut compiler = crate::mir::MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
|
||||
let dump = MirPrinter::new().print_module(&result.module);
|
||||
// Expect env.local.get/set present (pure-mode normalization)
|
||||
assert!(dump.contains("extern_call env.local.get"), "expected env.local.get in MIR. dump=\n{}", dump);
|
||||
assert!(dump.contains("extern_call env.local.set"), "expected env.local.set in MIR. dump=\n{}", dump);
|
||||
|
||||
std::env::remove_var("NYASH_MIR_CORE13_PURE");
|
||||
}
|
||||
}
|
||||
|
||||
48
src/tests/mir_pure_only_core13.rs
Normal file
48
src/tests/mir_pure_only_core13.rs
Normal file
@ -0,0 +1,48 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::parser::NyashParser;
|
||||
|
||||
fn is_allowed_core13(inst: &crate::mir::MirInstruction) -> bool {
|
||||
use crate::mir::MirInstruction as I;
|
||||
matches!(inst,
|
||||
I::Const { .. }
|
||||
| I::BinOp { .. }
|
||||
| I::Compare { .. }
|
||||
| I::Jump { .. }
|
||||
| I::Branch { .. }
|
||||
| I::Return { .. }
|
||||
| I::Phi { .. }
|
||||
| I::Call { .. }
|
||||
| I::BoxCall { .. }
|
||||
| I::ExternCall { .. }
|
||||
| I::TypeOp { .. }
|
||||
| I::Safepoint
|
||||
| I::Barrier { .. }
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn final_mir_contains_only_core13_instructions() {
|
||||
std::env::set_var("NYASH_MIR_CORE13_PURE", "1");
|
||||
let code = r#"
|
||||
local x
|
||||
x = 1
|
||||
if (x == 1) { x = x + 41 }
|
||||
return new StringBox("ok").length()
|
||||
"#;
|
||||
let ast = NyashParser::parse_from_string(code).expect("parse");
|
||||
let mut compiler = crate::mir::MirCompiler::new();
|
||||
let result = compiler.compile(ast).expect("compile");
|
||||
// Count non-Core13 instructions
|
||||
let mut bad = 0usize;
|
||||
for (_name, f) in &result.module.functions {
|
||||
for (_bb, b) in &f.blocks {
|
||||
for i in &b.instructions { if !is_allowed_core13(i) { bad += 1; } }
|
||||
if let Some(t) = &b.terminator { if !is_allowed_core13(t) { bad += 1; } }
|
||||
}
|
||||
}
|
||||
assert_eq!(bad, 0, "final MIR must contain only Core-13 instructions");
|
||||
std::env::remove_var("NYASH_MIR_CORE13_PURE");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user