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])); }