use crate::compiler::JITCompiler;
use crate::mixed_table::FunctionEntry;
use crate::JITConfig;
fn compile_to_mixed_table(
source: &str,
) -> (
shape_vm::bytecode::BytecodeProgram,
crate::mixed_table::MixedFunctionTable,
) {
use shape_vm::BytecodeCompiler;
shape_runtime::initialize_shared_runtime().ok();
let program = shape_ast::parse_program(source).expect("parse failed");
let mut loader = shape_runtime::module_loader::ModuleLoader::new();
let (graph, stdlib_names, prelude_imports) =
shape_vm::module_resolution::build_graph_and_stdlib_names(
&program,
&mut loader,
&[],
)
.expect("module graph construction failed");
let mut compiler = BytecodeCompiler::new();
compiler.stdlib_function_names = stdlib_names;
compiler.set_source(source);
let bytecode = compiler
.compile_with_graph_and_prelude(&program, graph, &prelude_imports)
.expect("bytecode compilation failed");
let jit_config = JITConfig::default();
let mut jit = JITCompiler::new(jit_config).expect("JIT init failed");
let (_jit_fn, mixed_table) = jit
.compile_program_selective("main", &bytecode)
.expect("JIT compilation failed");
(bytecode, mixed_table)
}
fn has_native_closure(
program: &shape_vm::bytecode::BytecodeProgram,
table: &crate::mixed_table::MixedFunctionTable,
) -> bool {
program
.functions
.iter()
.enumerate()
.any(|(idx, func)| {
func.is_closure
&& matches!(table.get(idx), Some(FunctionEntry::Native(_)))
})
}
fn jit_run(source: &str) -> shape_wire::WireValue {
use shape_runtime::engine::{ProgramExecutor, ShapeEngine};
shape_runtime::initialize_shared_runtime().ok();
let mut engine = ShapeEngine::new().expect("engine creation failed");
let program = shape_ast::parse_program(source).expect("parse failed");
let mut executor = crate::executor::JITExecutor::new();
let result = executor
.execute_program(&mut engine, &program)
.expect("JIT execution failed");
result.wire_value
}
#[test]
fn a1e_jit_var_closure_body_is_natively_compiled() {
let source = r#"
fn main() -> int {
var x: int = 0
let f = || { x = x + 1 }
f()
x
}
main()
"#;
let (program, table) = compile_to_mixed_table(source);
assert!(
has_native_closure(&program, &table),
"A.1E: closure body containing Load/StoreSharedCapture must \
JIT-compile to FunctionEntry::Native. native={} interpreted={}",
table.native_count(),
table.interpreted_count()
);
}
#[test]
fn a1e_closure_layout_shared_kind_classifies() {
use shape_value::v2::closure_layout::{CaptureKind, ClosureLayout};
use shape_value::v2::ConcreteType;
let capture_types = vec![
ConcreteType::I64,
ConcreteType::I64,
ConcreteType::F64,
];
let kinds = vec![
CaptureKind::Immutable,
CaptureKind::Shared,
CaptureKind::OwnedMutable,
];
let layout = ClosureLayout::from_capture_types(&capture_types, &kinds);
assert_eq!(layout.capture_storage_kind(0), CaptureKind::Immutable);
assert_eq!(layout.capture_storage_kind(1), CaptureKind::Shared);
assert_eq!(layout.capture_storage_kind(2), CaptureKind::OwnedMutable);
assert_eq!(
layout.shared_capture_mask, 0b010,
"shared_capture_mask must have exactly bit 1 set for the \
single Shared capture at index 1"
);
assert_eq!(layout.heap_capture_mask, 0);
assert_eq!(layout.owned_mutable_capture_mask, 0b100);
assert_eq!(
layout.shared_capture_mask & layout.owned_mutable_capture_mask,
0,
"shared and owned_mutable masks must be disjoint"
);
assert!(layout.is_shared_capture(1));
assert!(!layout.is_shared_capture(0));
assert!(!layout.is_shared_capture(2));
}
#[test]
fn a1e_jit_var_counter_e2e() {
let source = r#"
fn main() -> int {
var x: int = 0
let inc = || { x = x + 1 }
inc()
inc()
inc()
x
}
main()
"#;
match jit_run(source) {
shape_wire::WireValue::Integer(n) => {
assert_eq!(n, 3, "counter must reach 3 after three inc() calls");
}
shape_wire::WireValue::Number(n) => {
assert!(
(n - 3.0).abs() < 1e-9,
"counter must reach 3 (got Number {})",
n
);
}
other => panic!("expected Integer(3), got {:?}", other),
}
}
#[test]
fn a1e_jit_var_two_closures_share_cell() {
let source = r#"
fn main() -> int {
var x: int = 10
let inc = || { x = x + 1 }
let dec = || { x = x - 1 }
inc()
inc()
dec()
x
}
main()
"#;
match jit_run(source) {
shape_wire::WireValue::Integer(n) => assert_eq!(n, 11),
shape_wire::WireValue::Number(n) => {
assert!((n - 11.0).abs() < 1e-9, "expected 11, got Number {}", n)
}
other => panic!("expected Integer(11), got {:?}", other),
}
}
#[test]
fn a1e_jit_var_no_closure_runs() {
let source = r#"
fn main() -> int {
var x: int = 0
x = x + 1
x = x + 1
x
}
main()
"#;
use shape_runtime::engine::{ProgramExecutor, ShapeEngine};
shape_runtime::initialize_shared_runtime().ok();
let mut engine = ShapeEngine::new().expect("engine creation failed");
let program = shape_ast::parse_program(source).expect("parse failed");
let mut executor = crate::executor::JITExecutor::new();
let result = executor
.execute_program(&mut engine, &program)
.expect("JIT execution failed");
match result.wire_value {
shape_wire::WireValue::Integer(n) => assert_eq!(n, 2),
shape_wire::WireValue::Number(n) => {
assert!((n - 2.0).abs() < 1e-9, "expected 2, got Number {}", n)
}
other => panic!("expected Integer(2), got {:?}", other),
}
}
#[test]
fn a1e_jit_var_captured_then_outer_observe() {
let source = r#"
fn main() -> int {
var x: int = 5
let f = || { x = x + 10 }
f()
x
}
main()
"#;
use shape_runtime::engine::{ProgramExecutor, ShapeEngine};
shape_runtime::initialize_shared_runtime().ok();
let mut engine = ShapeEngine::new().expect("engine creation failed");
let program = shape_ast::parse_program(source).expect("parse failed");
let mut executor = crate::executor::JITExecutor::new();
let result = executor
.execute_program(&mut engine, &program)
.expect("JIT execution failed");
match result.wire_value {
shape_wire::WireValue::Integer(n) => assert_eq!(n, 15),
shape_wire::WireValue::Number(n) => {
assert!((n - 15.0).abs() < 1e-9, "expected 15, got Number {}", n)
}
other => panic!("expected Integer(15), got {:?}", other),
}
}
#[test]
fn a1e_jit_two_closures_share_cell() {
let source = r#"
fn main() -> int {
var x: int = 10
let inc = || { x = x + 1 }
let dec = || { x = x - 1 }
inc()
inc()
dec()
x
}
main()
"#;
use shape_runtime::engine::{ProgramExecutor, ShapeEngine};
shape_runtime::initialize_shared_runtime().ok();
let mut engine = ShapeEngine::new().expect("engine creation failed");
let program = shape_ast::parse_program(source).expect("parse failed");
let mut executor = crate::executor::JITExecutor::new();
let result = executor
.execute_program(&mut engine, &program)
.expect("JIT execution failed");
match result.wire_value {
shape_wire::WireValue::Integer(n) => assert_eq!(n, 11),
shape_wire::WireValue::Number(n) => {
assert!((n - 11.0).abs() < 1e-9, "expected 11, got Number {}", n)
}
other => panic!("expected Integer(11), got {:?}", other),
}
}
#[test]
fn a1e_jit_var_counter_repeated_calls() {
let source = r#"
fn main() -> int {
var x: int = 0
let inc = || { x = x + 1 }
inc()
inc()
inc()
x
}
main()
"#;
use shape_runtime::engine::{ProgramExecutor, ShapeEngine};
shape_runtime::initialize_shared_runtime().ok();
let mut engine = ShapeEngine::new().expect("engine creation failed");
let program = shape_ast::parse_program(source).expect("parse failed");
let mut executor = crate::executor::JITExecutor::new();
let result = executor
.execute_program(&mut engine, &program)
.expect("JIT execution failed");
match result.wire_value {
shape_wire::WireValue::Integer(n) => assert_eq!(n, 3),
shape_wire::WireValue::Number(n) => {
assert!((n - 3.0).abs() < 1e-9, "expected 3, got Number {}", n)
}
other => panic!("expected Integer(3), got {:?}", other),
}
}
#[test]
fn a1e_jit_mixed_let_letmut_var() {
let source = r#"
fn main() -> int {
let base: int = 100
let mut accum: int = 0
var shared: int = 7
let f = || {
accum = accum + base
shared = shared + accum
}
f()
f()
shared
}
main()
"#;
match jit_run(source) {
shape_wire::WireValue::Integer(n) => assert_eq!(n, 307),
shape_wire::WireValue::Number(n) => {
assert!((n - 307.0).abs() < 1e-9, "expected 307, got Number {}", n)
}
other => panic!("expected Integer(307), got {:?}", other),
}
}