fix compiler issues
This commit is contained in:
parent
926ad2a807
commit
e2a5a08bf2
@ -259,7 +259,8 @@ mod tests {
|
|||||||
00BE GateRelease
|
00BE GateRelease
|
||||||
00C0 GetLocal U32(2)
|
00C0 GetLocal U32(2)
|
||||||
00C6 GateRelease
|
00C6 GateRelease
|
||||||
00C8 Ret
|
00C8 PushConst U32(0)
|
||||||
|
00CE Ret
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
assert_eq!(disasm_text, expected_disasm);
|
assert_eq!(disasm_text, expected_disasm);
|
||||||
|
|||||||
@ -146,7 +146,7 @@ mod tests {
|
|||||||
assert_eq!(func.name, "start");
|
assert_eq!(func.name, "start");
|
||||||
assert_eq!(func.id, FunctionId(10));
|
assert_eq!(func.id, FunctionId(10));
|
||||||
|
|
||||||
assert_eq!(func.body.len(), 3);
|
assert_eq!(func.body.len(), 4);
|
||||||
match &func.body[0].kind {
|
match &func.body[0].kind {
|
||||||
InstrKind::Label(Label(l)) => assert!(l.contains("block_0")),
|
InstrKind::Label(Label(l)) => assert!(l.contains("block_0")),
|
||||||
_ => panic!("Expected label"),
|
_ => panic!("Expected label"),
|
||||||
@ -156,6 +156,10 @@ mod tests {
|
|||||||
_ => panic!("Expected PushConst"),
|
_ => panic!("Expected PushConst"),
|
||||||
}
|
}
|
||||||
match &func.body[2].kind {
|
match &func.body[2].kind {
|
||||||
|
InstrKind::PushNull => (),
|
||||||
|
_ => panic!("Expected PushNull"),
|
||||||
|
}
|
||||||
|
match &func.body[3].kind {
|
||||||
InstrKind::Ret => (),
|
InstrKind::Ret => (),
|
||||||
_ => panic!("Expected Ret"),
|
_ => panic!("Expected Ret"),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -313,6 +313,13 @@ pub fn lower_function(
|
|||||||
vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::GateRelease, None));
|
vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::GateRelease, None));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the function is Void, we must push a Null value to satisfy the VM's RET instruction.
|
||||||
|
// The VM always pops one value from the stack to be the return value.
|
||||||
|
if vm_func.return_type == ir_vm::Type::Void {
|
||||||
|
vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::PushNull, None));
|
||||||
|
}
|
||||||
|
|
||||||
vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::Ret, None));
|
vm_func.body.push(ir_vm::Instruction::new(ir_vm::InstrKind::Ret, None));
|
||||||
}
|
}
|
||||||
ir_core::Terminator::Jump(target) => {
|
ir_core::Terminator::Jump(target) => {
|
||||||
@ -422,7 +429,7 @@ mod tests {
|
|||||||
let func = &vm_module.functions[0];
|
let func = &vm_module.functions[0];
|
||||||
assert_eq!(func.name, "main");
|
assert_eq!(func.name, "main");
|
||||||
|
|
||||||
assert_eq!(func.body.len(), 7);
|
assert_eq!(func.body.len(), 8);
|
||||||
|
|
||||||
match &func.body[0].kind {
|
match &func.body[0].kind {
|
||||||
InstrKind::Label(Label(l)) => assert_eq!(l, "block_0"),
|
InstrKind::Label(Label(l)) => assert_eq!(l, "block_0"),
|
||||||
@ -452,6 +459,10 @@ mod tests {
|
|||||||
_ => panic!("Expected HostCall 42"),
|
_ => panic!("Expected HostCall 42"),
|
||||||
}
|
}
|
||||||
match &func.body[6].kind {
|
match &func.body[6].kind {
|
||||||
|
InstrKind::PushNull => (),
|
||||||
|
_ => panic!("Expected PushNull"),
|
||||||
|
}
|
||||||
|
match &func.body[7].kind {
|
||||||
InstrKind::Ret => (),
|
InstrKind::Ret => (),
|
||||||
_ => panic!("Expected Ret"),
|
_ => panic!("Expected Ret"),
|
||||||
}
|
}
|
||||||
@ -500,7 +511,7 @@ mod tests {
|
|||||||
// GateStore 100 (offset)
|
// GateStore 100 (offset)
|
||||||
// Ret
|
// Ret
|
||||||
|
|
||||||
assert_eq!(func.body.len(), 9);
|
assert_eq!(func.body.len(), 10);
|
||||||
match &func.body[1].kind {
|
match &func.body[1].kind {
|
||||||
ir_vm::InstrKind::LocalLoad { slot } => assert_eq!(*slot, 0),
|
ir_vm::InstrKind::LocalLoad { slot } => assert_eq!(*slot, 0),
|
||||||
_ => panic!("Expected LocalLoad 0"),
|
_ => panic!("Expected LocalLoad 0"),
|
||||||
@ -517,6 +528,14 @@ mod tests {
|
|||||||
ir_vm::InstrKind::GateStore { offset } => assert_eq!(*offset, 100),
|
ir_vm::InstrKind::GateStore { offset } => assert_eq!(*offset, 100),
|
||||||
_ => panic!("Expected GateStore 100"),
|
_ => panic!("Expected GateStore 100"),
|
||||||
}
|
}
|
||||||
|
match &func.body[8].kind {
|
||||||
|
ir_vm::InstrKind::PushNull => (),
|
||||||
|
_ => panic!("Expected PushNull"),
|
||||||
|
}
|
||||||
|
match &func.body[9].kind {
|
||||||
|
ir_vm::InstrKind::Ret => (),
|
||||||
|
_ => panic!("Expected Ret"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -609,10 +628,10 @@ mod tests {
|
|||||||
assert!(found_overwrite, "Should have emitted release-then-store sequence for overwrite");
|
assert!(found_overwrite, "Should have emitted release-then-store sequence for overwrite");
|
||||||
|
|
||||||
// Check Ret cleanup:
|
// Check Ret cleanup:
|
||||||
// LocalLoad 1, GateRelease, Ret
|
// LocalLoad 1, GateRelease, PushNull, Ret
|
||||||
let mut found_cleanup = false;
|
let mut found_cleanup = false;
|
||||||
for i in 0..kinds.len() - 2 {
|
for i in 0..kinds.len() - 3 {
|
||||||
if let (InstrKind::LocalLoad { slot: 1 }, InstrKind::GateRelease, InstrKind::Ret) = (kinds[i], kinds[i+1], kinds[i+2]) {
|
if let (InstrKind::LocalLoad { slot: 1 }, InstrKind::GateRelease, InstrKind::PushNull, InstrKind::Ret) = (kinds[i], kinds[i+1], kinds[i+2], kinds[i+3]) {
|
||||||
found_cleanup = true;
|
found_cleanup = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -92,7 +92,7 @@ fn test_hip_conformance_core_to_vm_to_bytecode() {
|
|||||||
0x02, 0x00, 0x00, 0x00, // CP Count: 2
|
0x02, 0x00, 0x00, 0x00, // CP Count: 2
|
||||||
0x00, // CP[0]: Null
|
0x00, // CP[0]: Null
|
||||||
0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CP[1]: Int64(42)
|
0x01, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // CP[1]: Int64(42)
|
||||||
0x66, 0x00, 0x00, 0x00, // ROM Size: 102
|
0x6c, 0x00, 0x00, 0x00, // ROM Size: 108
|
||||||
// Instructions:
|
// Instructions:
|
||||||
0x60, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // Alloc { tid: 10, slots: 2 }
|
0x60, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // Alloc { tid: 10, slots: 2 }
|
||||||
0x43, 0x00, 0x00, 0x00, 0x00, 0x00, // SetLocal 0
|
0x43, 0x00, 0x00, 0x00, 0x00, 0x00, // SetLocal 0
|
||||||
@ -118,6 +118,7 @@ fn test_hip_conformance_core_to_vm_to_bytecode() {
|
|||||||
0x11, 0x00, // Pop
|
0x11, 0x00, // Pop
|
||||||
0x42, 0x00, 0x00, 0x00, 0x00, 0x00, // GetLocal 0 (cleanup)
|
0x42, 0x00, 0x00, 0x00, 0x00, 0x00, // GetLocal 0 (cleanup)
|
||||||
0x6a, 0x00, // GateRelease
|
0x6a, 0x00, // GateRelease
|
||||||
|
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // PushConst 0 (Null return)
|
||||||
0x51, 0x00, // Ret
|
0x51, 0x00, // Ret
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,7 @@ pub struct PrometeuOS {
|
|||||||
pub current_cartridge_title: String,
|
pub current_cartridge_title: String,
|
||||||
pub current_cartridge_app_version: String,
|
pub current_cartridge_app_version: String,
|
||||||
pub current_cartridge_app_mode: crate::model::AppMode,
|
pub current_cartridge_app_mode: crate::model::AppMode,
|
||||||
|
pub current_entrypoint: String,
|
||||||
/// Rate-limiter to prevent apps from flooding the log buffer and killing performance.
|
/// Rate-limiter to prevent apps from flooding the log buffer and killing performance.
|
||||||
pub logs_written_this_frame: HashMap<u32, u32>,
|
pub logs_written_this_frame: HashMap<u32, u32>,
|
||||||
|
|
||||||
@ -100,6 +101,7 @@ impl PrometeuOS {
|
|||||||
current_cartridge_title: String::new(),
|
current_cartridge_title: String::new(),
|
||||||
current_cartridge_app_version: String::new(),
|
current_cartridge_app_version: String::new(),
|
||||||
current_cartridge_app_mode: crate::model::AppMode::Game,
|
current_cartridge_app_mode: crate::model::AppMode::Game,
|
||||||
|
current_entrypoint: String::new(),
|
||||||
logs_written_this_frame: HashMap::new(),
|
logs_written_this_frame: HashMap::new(),
|
||||||
telemetry_current: TelemetryFrame::default(),
|
telemetry_current: TelemetryFrame::default(),
|
||||||
telemetry_last: TelemetryFrame::default(),
|
telemetry_last: TelemetryFrame::default(),
|
||||||
@ -167,6 +169,7 @@ impl PrometeuOS {
|
|||||||
self.current_cartridge_title = cartridge.title.clone();
|
self.current_cartridge_title = cartridge.title.clone();
|
||||||
self.current_cartridge_app_version = cartridge.app_version.clone();
|
self.current_cartridge_app_version = cartridge.app_version.clone();
|
||||||
self.current_cartridge_app_mode = cartridge.app_mode;
|
self.current_cartridge_app_mode = cartridge.app_mode;
|
||||||
|
self.current_entrypoint = cartridge.entrypoint.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes a single VM instruction (Debug).
|
/// Executes a single VM instruction (Debug).
|
||||||
@ -204,6 +207,12 @@ impl PrometeuOS {
|
|||||||
self.logical_frame_remaining_cycles = Self::CYCLES_PER_LOGICAL_FRAME;
|
self.logical_frame_remaining_cycles = Self::CYCLES_PER_LOGICAL_FRAME;
|
||||||
self.begin_logical_frame(signals, hw);
|
self.begin_logical_frame(signals, hw);
|
||||||
|
|
||||||
|
// If the VM is not currently executing a function (e.g. at the start of the app
|
||||||
|
// or after the entrypoint function returned), we prepare a new call to the entrypoint.
|
||||||
|
if vm.call_stack.is_empty() {
|
||||||
|
vm.prepare_call(&self.current_entrypoint);
|
||||||
|
}
|
||||||
|
|
||||||
// Reset telemetry for the new logical frame
|
// Reset telemetry for the new logical frame
|
||||||
self.telemetry_current = TelemetryFrame {
|
self.telemetry_current = TelemetryFrame {
|
||||||
frame_index: self.logical_frame_index,
|
frame_index: self.logical_frame_index,
|
||||||
@ -236,8 +245,16 @@ impl PrometeuOS {
|
|||||||
self.log(LogLevel::Info, LogSource::Vm, 0xDEB1, format!("Breakpoint hit at PC 0x{:X}", vm.pc));
|
self.log(LogLevel::Info, LogSource::Vm, 0xDEB1, format!("Breakpoint hit at PC 0x{:X}", vm.pc));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Frame Finalization (FRAME_SYNC reached)
|
// Handle Panics
|
||||||
if run.reason == crate::virtual_machine::LogicalFrameEndingReason::FrameSync {
|
if let crate::virtual_machine::LogicalFrameEndingReason::Panic(err) = run.reason {
|
||||||
|
let err_msg = format!("PVM Fault: \"{}\"", err);
|
||||||
|
self.log(LogLevel::Error, LogSource::Vm, 0, err_msg.clone());
|
||||||
|
return Some(err_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Frame Finalization (FRAME_SYNC reached or Entrypoint returned)
|
||||||
|
if run.reason == crate::virtual_machine::LogicalFrameEndingReason::FrameSync ||
|
||||||
|
run.reason == crate::virtual_machine::LogicalFrameEndingReason::EndOfRom {
|
||||||
// All drawing commands for this frame are now complete.
|
// All drawing commands for this frame are now complete.
|
||||||
// Finalize the framebuffer.
|
// Finalize the framebuffer.
|
||||||
hw.gfx_mut().render_all();
|
hw.gfx_mut().render_all();
|
||||||
@ -626,6 +643,45 @@ mod tests {
|
|||||||
os.syscall(0x1001, &mut vm, &mut hw).unwrap(); // gfx.clear
|
os.syscall(0x1001, &mut vm, &mut hw).unwrap(); // gfx.clear
|
||||||
assert_eq!(vm.pop().unwrap(), Value::Null);
|
assert_eq!(vm.pop().unwrap(), Value::Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_entrypoint_called_every_frame() {
|
||||||
|
let mut os = PrometeuOS::new(None);
|
||||||
|
let mut vm = VirtualMachine::default();
|
||||||
|
let mut hw = Hardware::new();
|
||||||
|
let signals = InputSignals::default();
|
||||||
|
|
||||||
|
// PushI32 0 (0x17), then Ret (0x51)
|
||||||
|
let rom = vec![
|
||||||
|
0x17, 0x00, // PushI32
|
||||||
|
0x00, 0x00, 0x00, 0x00, // value 0
|
||||||
|
0x51, 0x00 // Ret
|
||||||
|
];
|
||||||
|
let cartridge = Cartridge {
|
||||||
|
app_id: 1234,
|
||||||
|
title: "test".to_string(),
|
||||||
|
app_version: "1.0.0".to_string(),
|
||||||
|
app_mode: AppMode::Game,
|
||||||
|
entrypoint: "0".to_string(),
|
||||||
|
program: rom,
|
||||||
|
assets: vec![],
|
||||||
|
asset_table: vec![],
|
||||||
|
preload: vec![],
|
||||||
|
};
|
||||||
|
os.initialize_vm(&mut vm, &cartridge);
|
||||||
|
|
||||||
|
// First frame
|
||||||
|
os.tick(&mut vm, &signals, &mut hw);
|
||||||
|
assert_eq!(os.logical_frame_index, 1);
|
||||||
|
assert!(!os.logical_frame_active);
|
||||||
|
assert!(vm.call_stack.is_empty());
|
||||||
|
|
||||||
|
// Second frame - Should call entrypoint again
|
||||||
|
os.tick(&mut vm, &signals, &mut hw);
|
||||||
|
assert_eq!(os.logical_frame_index, 2);
|
||||||
|
assert!(!os.logical_frame_active);
|
||||||
|
assert!(vm.call_stack.is_empty());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NativeInterface for PrometeuOS {
|
impl NativeInterface for PrometeuOS {
|
||||||
|
|||||||
@ -137,6 +137,30 @@ impl VirtualMachine {
|
|||||||
self.cycles = 0;
|
self.cycles = 0;
|
||||||
self.halted = false;
|
self.halted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepares the VM to execute a specific entrypoint by setting the PC and
|
||||||
|
/// pushing an initial call frame.
|
||||||
|
pub fn prepare_call(&mut self, entrypoint: &str) {
|
||||||
|
let addr = if let Ok(addr) = entrypoint.parse::<usize>() {
|
||||||
|
addr
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
self.pc = addr;
|
||||||
|
self.halted = false;
|
||||||
|
|
||||||
|
// Pushing a sentinel frame so RET works at the top level.
|
||||||
|
// The return address is set to the end of ROM, which will naturally
|
||||||
|
// cause the VM to stop after returning from the entrypoint.
|
||||||
|
self.operand_stack.clear();
|
||||||
|
self.call_stack.clear();
|
||||||
|
self.scope_stack.clear();
|
||||||
|
self.call_stack.push(CallFrame {
|
||||||
|
return_pc: self.program.rom.len() as u32,
|
||||||
|
stack_base: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for VirtualMachine {
|
impl Default for VirtualMachine {
|
||||||
@ -1277,4 +1301,25 @@ mod tests {
|
|||||||
_ => panic!("Expected Trap, got {:?}", report.reason),
|
_ => panic!("Expected Trap, got {:?}", report.reason),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_entry_point_ret_with_prepare_call() {
|
||||||
|
// PushI32 0 (0x17), then Ret (0x51)
|
||||||
|
let rom = vec![
|
||||||
|
0x17, 0x00, // PushI32
|
||||||
|
0x00, 0x00, 0x00, 0x00, // value 0
|
||||||
|
0x51, 0x00 // Ret
|
||||||
|
];
|
||||||
|
let mut vm = VirtualMachine::new(rom, vec![]);
|
||||||
|
let mut hw = crate::Hardware::new();
|
||||||
|
struct TestNative;
|
||||||
|
impl NativeInterface for TestNative {
|
||||||
|
fn syscall(&mut self, _id: u32, _vm: &mut VirtualMachine, _hw: &mut dyn HardwareBridge) -> Result<u64, String> { Ok(0) }
|
||||||
|
}
|
||||||
|
let mut native = TestNative;
|
||||||
|
|
||||||
|
vm.prepare_call("0");
|
||||||
|
let result = vm.run_budget(100, &mut native, &mut hw).expect("VM run failed");
|
||||||
|
assert_eq!(result.reason, LogicalFrameEndingReason::EndOfRom);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
00000000 PushConst U32(1)
|
|
||||||
00000006 SetLocal U32(0)
|
|
||||||
0000000C GetLocal U32(0)
|
|
||||||
00000012 PushConst U32(1)
|
|
||||||
00000018 Eq
|
|
||||||
0000001A JmpIfFalse U32(56)
|
|
||||||
00000020 Jmp U32(38)
|
|
||||||
00000026 PushConst U32(2)
|
|
||||||
0000002C Syscall U32(4097)
|
|
||||||
00000032 Jmp U32(62)
|
|
||||||
00000038 Jmp U32(62)
|
|
||||||
0000003E Ret
|
|
||||||
Binary file not shown.
@ -1 +1 @@
|
|||||||
../../target/debug
|
../../dist-staging/stable/prometeu-aarch64-apple-darwin
|
||||||
@ -1,6 +1,2 @@
|
|||||||
declare contract Touch host {
|
|
||||||
fn f();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn frame(): void {
|
fn frame(): void {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
pub declare contract Gfx host {}
|
|
||||||
pub declare contract Input host {}
|
|
||||||
pub declare contract Touch host {}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import Gfx from "./sdk.pbs"
|
|
||||||
|
|
||||||
fn frame(): void {
|
|
||||||
Gfx.clear(0);
|
|
||||||
Gfx.fillRect(110, 110, 20, 20, 2016);
|
|
||||||
Gfx.drawText(10, 10, "Test01 PBS - Minimal", 65535);
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user