127 lines
6.4 KiB
Rust
127 lines
6.4 KiB
Rust
use prometeu_compiler::ir_core::{self, Program, Block, Instr, Terminator, ConstantValue, ConstPool};
|
|
use prometeu_compiler::ir_core::ids::{FunctionId, ConstId as CoreConstId, TypeId as CoreTypeId, FieldId, ValueId};
|
|
use prometeu_compiler::ir_vm::InstrKind;
|
|
use prometeu_compiler::lowering::lower_program;
|
|
use prometeu_compiler::backend::emit_bytecode::emit_module;
|
|
use prometeu_compiler::common::files::FileManager;
|
|
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(),
|
|
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 file_manager = FileManager::new();
|
|
let emit_result = emit_module(&vm_module, &file_manager).expect("Emission failed");
|
|
let bytecode = emit_result.rom;
|
|
|
|
// 4. Assert exact bytes match frozen ISA/ABI
|
|
let expected_bytecode = vec![
|
|
0x50, 0x50, 0x42, 0x43, // Magic: "PPBC"
|
|
0x00, 0x00, // Version: 0
|
|
0x00, 0x00, // Flags: 0
|
|
0x02, 0x00, 0x00, 0x00, // CP Count: 2
|
|
0x00, // CP[0]: Null
|
|
0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CP[1]: Int64(42)
|
|
0x6c, 0x00, 0x00, 0x00, // ROM Size: 108
|
|
// Instructions:
|
|
0x60, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // Alloc { tid: 10, slots: 2 }
|
|
0x43, 0x00, 0x00, 0x00, 0x00, 0x00, // SetLocal 0
|
|
0x42, 0x00, 0x00, 0x00, 0x00, 0x00, // GetLocal 0
|
|
0x69, 0x00, // GateRetain
|
|
0x67, 0x00, // GateBeginMutate
|
|
0x10, 0x00, 0x01, 0x00, 0x00, 0x00, // PushConst 1 (42)
|
|
0x43, 0x00, 0x01, 0x00, 0x00, 0x00, // SetLocal 1
|
|
0x42, 0x00, 0x00, 0x00, 0x00, 0x00, // GetLocal 0
|
|
0x69, 0x00, // GateRetain
|
|
0x42, 0x00, 0x01, 0x00, 0x00, 0x00, // GetLocal 1
|
|
0x62, 0x00, 0x00, 0x00, 0x00, 0x00, // GateStore 0
|
|
0x68, 0x00, // GateEndMutate
|
|
0x6a, 0x00, // GateRelease
|
|
0x42, 0x00, 0x00, 0x00, 0x00, 0x00, // GetLocal 0
|
|
0x69, 0x00, // GateRetain
|
|
0x63, 0x00, // GateBeginPeek
|
|
0x42, 0x00, 0x00, 0x00, 0x00, 0x00, // GetLocal 0
|
|
0x69, 0x00, // GateRetain
|
|
0x61, 0x00, 0x00, 0x00, 0x00, 0x00, // GateLoad 0
|
|
0x64, 0x00, // GateEndPeek
|
|
0x6a, 0x00, // GateRelease
|
|
0x11, 0x00, // Pop
|
|
0x42, 0x00, 0x00, 0x00, 0x00, 0x00, // GetLocal 0 (cleanup)
|
|
0x6a, 0x00, // GateRelease
|
|
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // PushConst 0 (Null return)
|
|
0x51, 0x00, // Ret
|
|
];
|
|
|
|
assert_eq!(bytecode, expected_bytecode, "Bytecode does not match golden ISA/ABI v0");
|
|
}
|