Nilton Constantino b4deaa243e
pr 60
2026-02-02 19:57:04 +00:00

104 lines
4.5 KiB
Rust

use prometeu_compiler::backend::emit_bytecode::emit_module;
use prometeu_compiler::ir_core::ids::{ConstId as CoreConstId, FieldId, FunctionId, TypeId as CoreTypeId, ValueId};
use prometeu_compiler::ir_core::{self, Block, ConstPool, ConstantValue, Instr, Program, Terminator};
use prometeu_compiler::ir_vm::InstrKind;
use prometeu_compiler::lowering::lower_program;
use std::collections::HashMap;
#[test]
fn test_hip_conformance_core_to_vm_to_bytecode() {
// 1. Setup Core IR Program
let mut const_pool = ConstPool::new();
let _val_const = const_pool.insert(ConstantValue::Int(42));
let type_id = CoreTypeId(10);
let field_id = FieldId(1);
let mut field_offsets = HashMap::new();
field_offsets.insert(field_id, 0); // Field at offset 0
let mut field_types = HashMap::new();
field_types.insert(field_id, ir_core::Type::Int);
let program = Program {
const_pool,
modules: vec![ir_core::Module {
name: "conformance".to_string(),
functions: vec![ir_core::Function {
id: FunctionId(1),
name: "main".to_string(),
param_slots: 0,
local_slots: 0,
return_slots: 0,
params: vec![],
return_type: ir_core::Type::Void,
local_types: HashMap::new(),
blocks: vec![Block {
id: 0,
instrs: vec![
// allocates a storage struct
Instr::Alloc { ty: type_id, slots: 2 },
Instr::SetLocal(0), // x = alloc
// mutates a field
Instr::BeginMutate { gate: ValueId(0) },
Instr::PushConst(CoreConstId(0)),
Instr::SetLocal(1), // v = 42
Instr::GateStoreField { gate: ValueId(0), field: field_id, value: ValueId(1) },
Instr::EndMutate,
// peeks value
Instr::BeginPeek { gate: ValueId(0) },
Instr::GateLoadField { gate: ValueId(0), field: field_id },
Instr::EndPeek,
Instr::Pop, // clean up the peeked value
],
terminator: Terminator::Return,
}],
}],
}],
field_offsets,
field_types,
};
// 2. Lower to VM IR
let vm_module = lower_program(&program).expect("Lowering failed");
let func = &vm_module.functions[0];
// Assert VM IR contains required instructions
let kinds: Vec<_> = func.body.iter().map(|i| &i.kind).collect();
assert!(kinds.iter().any(|k| matches!(k, InstrKind::Alloc { type_id: tid, slots: 2 } if tid.0 == 10)), "Missing correct Alloc");
assert!(kinds.contains(&&InstrKind::GateBeginMutate), "Missing GateBeginMutate");
assert!(kinds.contains(&&InstrKind::GateEndMutate), "Missing GateEndMutate");
assert!(kinds.iter().any(|k| matches!(k, InstrKind::GateStore { offset: 0 })), "Missing correct GateStore");
assert!(kinds.contains(&&InstrKind::GateBeginPeek), "Missing GateBeginPeek");
assert!(kinds.contains(&&InstrKind::GateEndPeek), "Missing GateEndPeek");
assert!(kinds.iter().any(|k| matches!(k, InstrKind::GateLoad { offset: 0 })), "Missing correct GateLoad");
// RC ops
assert!(kinds.contains(&&InstrKind::GateRetain), "Missing GateRetain");
assert!(kinds.contains(&&InstrKind::GateRelease), "Missing GateRelease");
// 3. Emit Bytecode
let emit_result = emit_module(&vm_module).expect("Emission failed");
let bytecode = emit_result.rom;
// 4. Assert industrial PBS\0 format
use prometeu_bytecode::BytecodeLoader;
let module = BytecodeLoader::load(&bytecode).expect("Failed to parse industrial PBC");
assert_eq!(&bytecode[0..4], b"PBS\0");
// 5. Verify a few key instructions in the code section to ensure ABI stability
// We don't do a full byte-for-byte check of the entire file here as the section
// table offsets vary, but we check the instruction stream.
let instrs = module.code;
// Alloc { tid: 10, slots: 2 } -> 0x60 0x00, 0x0a 0x00 0x00 0x00, 0x02 0x00 0x00 0x00
assert!(instrs.windows(10).any(|w| w == &[0x60, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00]));
// PushConst 1 (42) -> 0x10 0x00, 0x01 0x00, 0x00, 0x00
assert!(instrs.windows(6).any(|w| w == &[0x10, 0x00, 0x01, 0x00, 0x00, 0x00]));
}