Co-authored-by: Nilton Constantino <nilton.constantino@visma.com> Reviewed-on: #8
104 lines
4.8 KiB
Rust
104 lines
4.8 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, InstrKind as CoreInstrKind, 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::from(CoreInstrKind::Alloc { ty: type_id, slots: 2 }),
|
|
Instr::from(CoreInstrKind::SetLocal(0)), // x = alloc
|
|
|
|
// mutates a field
|
|
Instr::from(CoreInstrKind::BeginMutate { gate: ValueId(0) }),
|
|
Instr::from(CoreInstrKind::PushConst(CoreConstId(0))),
|
|
Instr::from(CoreInstrKind::SetLocal(1)), // v = 42
|
|
Instr::from(CoreInstrKind::GateStoreField { gate: ValueId(0), field: field_id, value: ValueId(1) }),
|
|
Instr::from(CoreInstrKind::EndMutate),
|
|
|
|
// peeks value
|
|
Instr::from(CoreInstrKind::BeginPeek { gate: ValueId(0) }),
|
|
Instr::from(CoreInstrKind::GateLoadField { gate: ValueId(0), field: field_id }),
|
|
Instr::from(CoreInstrKind::EndPeek),
|
|
|
|
Instr::from(CoreInstrKind::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]));
|
|
}
|