Core-13 pure: add CI workflows, VM e2e tests, LLVM parity bridge (minimal); do not touch private docs

This commit is contained in:
Tomoaki
2025-09-07 07:28:53 +09:00
parent 07350c5dd9
commit d62114c705
383 changed files with 15221 additions and 382 deletions

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,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");
}
}

View 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");
}
}

View 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");
}
}

View 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");
}
}

View 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");
}
}

View 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");
}
}

View 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");
}
}

View 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");
}
}