Skip to content

Commit b1cab1b

Browse files
committed
refactor: simplify vm creation API
1 parent 191f8b9 commit b1cab1b

26 files changed

Lines changed: 222 additions & 210 deletions

pd-edge/src/bin/pd-edge-console.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ async fn run_loaded_program_once(state: &SharedState) -> Result<(), Box<dyn std:
337337
state.rate_limiter.clone(),
338338
)));
339339
let async_ops = new_shared_vm_async_ops();
340-
let mut vm = Vm::with_locals_shared(loaded.program.clone(), loaded.local_count);
340+
let mut vm = Vm::new_shared(loaded.program.clone());
341341
vm.set_async_bridge(Box::new(VmAsyncOpBridge::new(async_ops.clone())));
342342

343343
register_host_module(&mut vm, context, async_ops.clone())?;

pd-edge/src/runtime.rs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use std::{
1010
use serde::{Deserialize, Serialize};
1111
use tokio::sync::RwLock;
1212
use tracing::{info, warn};
13-
use vm::{Program, decode_program, infer_local_count, validate_program};
13+
use vm::{Program, decode_program, validate_program};
1414

1515
use crate::{
1616
HOST_FUNCTION_COUNT,
@@ -40,7 +40,6 @@ pub struct SharedState {
4040
#[derive(Clone)]
4141
pub struct LoadedProgram {
4242
pub program: Arc<Program>,
43-
pub local_count: usize,
4443
}
4544

4645
#[derive(Clone, Debug, Serialize, Deserialize)]
@@ -466,28 +465,12 @@ pub async fn apply_program_from_bytes(state: &SharedState, bytes: &[u8]) -> Prog
466465
};
467466
}
468467

469-
let local_count = match infer_local_count(&program) {
470-
Ok(local_count) => local_count,
471-
Err(err) => {
472-
state.record_program_apply_failure();
473-
let message = format!("invalid bytecode: {err}");
474-
warn!("{} local inference error: {err}", category_program());
475-
return ProgramApplyReport {
476-
applied: false,
477-
constants: None,
478-
code_bytes: None,
479-
local_count: None,
480-
message: Some(message),
481-
};
482-
}
483-
};
484-
468+
let local_count = program.local_count;
485469
let const_count = program.constants.len();
486470
let code_len = program.code.len();
487471
let mut guard = state.active_program.write().await;
488472
*guard = Some(Arc::new(LoadedProgram {
489473
program: Arc::new(program),
490-
local_count,
491474
}));
492475
state.record_program_apply_success();
493476
info!(

pd-edge/src/runtime/vm_runner.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,12 @@ pub async fn execute_vm_with_context(
3434
debug: VmDebugInvocation,
3535
register_host_modules: HostModuleRegistrar,
3636
) -> Result<VmExecutionOutcome, VmExecutionError> {
37-
let local_count = program.local_count;
3837
let program = program.program.clone();
3938
let async_ops = new_shared_vm_async_ops();
4039

4140
let task = tokio::task::spawn_blocking(move || {
4241
run_vm_blocking(
4342
program,
44-
local_count,
4543
vm_context,
4644
debug_session,
4745
debug,
@@ -59,14 +57,13 @@ pub async fn execute_vm_with_context(
5957

6058
fn run_vm_blocking(
6159
program: std::sync::Arc<vm::Program>,
62-
local_count: usize,
6360
vm_context: SharedProxyVmContext,
6461
debug_session: SharedDebugSession,
6562
debug: VmDebugInvocation,
6663
async_ops: SharedVmAsyncOps,
6764
register_host_modules: HostModuleRegistrar,
6865
) -> Result<VmExecutionOutcome, VmExecutionError> {
69-
let mut vm = Vm::with_locals_shared(program, local_count);
66+
let mut vm = Vm::new_shared(program);
7067
vm.set_async_bridge(Box::new(VmAsyncOpBridge::new(async_ops.clone())));
7168
register_host_modules(&mut vm, vm_context.clone(), async_ops)
7269
.map_err(VmExecutionError::HostRegistration)?;

pd-edge/tests/compile_tests.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ fn compile_edge_source_file_prefers_local_module_over_host_namespace_fallback()
100100
"local runtime module should win over host namespace fallback"
101101
);
102102

103-
let mut vm = Vm::with_locals(compiled.program, compiled.locals);
103+
let mut vm = Vm::new(compiled.program);
104104
let status = vm.run().expect("vm should run");
105105
assert_eq!(status, VmStatus::Halted);
106106
assert_eq!(vm.stack(), &[Value::Int(42)]);
@@ -144,7 +144,7 @@ fn compile_edge_source_file_with_options_can_override_runtime_module() {
144144
"runtime module override should replace host import fallback"
145145
);
146146

147-
let mut vm = Vm::with_locals(compiled.program, compiled.locals);
147+
let mut vm = Vm::new(compiled.program);
148148
let status = vm.run().expect("vm should run");
149149
assert_eq!(status, VmStatus::Halted);
150150
assert_eq!(vm.stack(), &[Value::Int(42)]);

pd-vm/src/bin/pd-vm-run.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ fn run_main() -> Result<(), Box<dyn std::error::Error>> {
9797
return Ok(());
9898
}
9999
let recording_program = cli.record_path.as_ref().map(|_| compiled.program.clone());
100-
let mut vm = Vm::with_locals(compiled.program, compiled.locals);
100+
let mut vm = Vm::new(compiled.program);
101101
if let Some(fuel) = cli.fuel {
102102
vm.set_fuel(fuel);
103103
}
@@ -525,7 +525,7 @@ fn run_repl() -> Result<(), Box<dyn std::error::Error>> {
525525
continue;
526526
}
527527
};
528-
let mut vm = Vm::with_locals(compiled.program, compiled.locals);
528+
let mut vm = Vm::new(compiled.program);
529529
if let Err(err) = register_functions(&mut vm, &compiled.functions) {
530530
println!("{err}");
531531
continue;

pd-vm/src/bytecode.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@ pub struct HostImport {
1919
pub struct Program {
2020
pub constants: Vec<Value>,
2121
pub code: Vec<u8>,
22+
pub local_count: usize,
2223
pub imports: Vec<HostImport>,
2324
pub debug: Option<crate::debug_info::DebugInfo>,
2425
}
2526

2627
impl Program {
2728
pub fn new(constants: Vec<Value>, code: Vec<u8>) -> Self {
29+
let local_count = infer_local_count_from_code(&code);
2830
Self {
2931
constants,
3032
code,
33+
local_count,
3134
imports: Vec::new(),
3235
debug: None,
3336
}
@@ -38,9 +41,11 @@ impl Program {
3841
code: Vec<u8>,
3942
debug: Option<crate::debug_info::DebugInfo>,
4043
) -> Self {
44+
let local_count = infer_local_count_from_code(&code);
4145
Self {
4246
constants,
4347
code,
48+
local_count,
4449
imports: Vec::new(),
4550
debug,
4651
}
@@ -52,13 +57,72 @@ impl Program {
5257
imports: Vec<HostImport>,
5358
debug: Option<crate::debug_info::DebugInfo>,
5459
) -> Self {
60+
let local_count = infer_local_count_from_code(&code);
5561
Self {
5662
constants,
5763
code,
64+
local_count,
5865
imports,
5966
debug,
6067
}
6168
}
69+
70+
pub fn with_local_count(mut self, local_count: usize) -> Self {
71+
self.local_count = local_count;
72+
self
73+
}
74+
}
75+
76+
fn infer_local_count_from_code(code: &[u8]) -> usize {
77+
let mut ip = 0usize;
78+
let mut max_local_index: Option<u8> = None;
79+
80+
while let Some(&opcode) = code.get(ip) {
81+
ip += 1;
82+
match opcode {
83+
x if x == OpCode::Nop as u8
84+
|| x == OpCode::Ret as u8
85+
|| x == OpCode::Add as u8
86+
|| x == OpCode::Sub as u8
87+
|| x == OpCode::Mul as u8
88+
|| x == OpCode::Div as u8
89+
|| x == OpCode::Neg as u8
90+
|| x == OpCode::Ceq as u8
91+
|| x == OpCode::Clt as u8
92+
|| x == OpCode::Cgt as u8
93+
|| x == OpCode::Pop as u8
94+
|| x == OpCode::Dup as u8
95+
|| x == OpCode::Shl as u8
96+
|| x == OpCode::Shr as u8
97+
|| x == OpCode::Mod as u8
98+
|| x == OpCode::And as u8
99+
|| x == OpCode::Or as u8 => {}
100+
x if x == OpCode::Ldc as u8
101+
|| x == OpCode::Br as u8
102+
|| x == OpCode::Brfalse as u8 => {
103+
if ip + 4 > code.len() {
104+
break;
105+
}
106+
ip += 4;
107+
}
108+
x if x == OpCode::Ldloc as u8 || x == OpCode::Stloc as u8 => {
109+
let Some(&index) = code.get(ip) else {
110+
break;
111+
};
112+
ip += 1;
113+
max_local_index = Some(max_local_index.map_or(index, |prev| prev.max(index)));
114+
}
115+
x if x == OpCode::Call as u8 => {
116+
if ip + 3 > code.len() {
117+
break;
118+
}
119+
ip += 3;
120+
}
121+
_ => break,
122+
}
123+
}
124+
125+
max_local_index.map_or(0, |index| index as usize + 1)
62126
}
63127

64128
#[derive(Clone, Copy, Debug, PartialEq, Eq)]

pd-vm/src/compiler/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ struct LocalDebugRange {
236236
impl CompiledProgram {
237237
#[cfg(feature = "runtime")]
238238
pub fn into_vm(self) -> Vm {
239-
Vm::with_locals(self.program, self.locals)
239+
Vm::new(self.program)
240240
}
241241
}
242242

@@ -332,6 +332,7 @@ fn compile_parsed_output(
332332
let mut program = compiler
333333
.compile_program(&stmts)
334334
.map_err(SourceError::Compile)?;
335+
program.local_count = locals;
335336
program.imports = runtime_import_functions
336337
.iter()
337338
.map(|func| HostImport {

pd-vm/src/debugger.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,8 @@ impl DebugCommandBridge {
583583
state.attached = true;
584584
state.current_line = current_line(vm);
585585
state.pending_request = None;
586-
state.pending_response = None;
586+
// Keep any in-flight response visible to execute() to avoid
587+
// races when the debugger resumes and immediately re-attaches.
587588
self.inner.changed.notify_all();
588589
}
589590

@@ -1867,7 +1868,7 @@ mod tests {
18671868
}],
18681869
}),
18691870
);
1870-
let mut vm = Vm::with_locals(program, 1);
1871+
let mut vm = Vm::new(program.with_local_count(1));
18711872
let status = vm.run().expect("vm should run");
18721873
assert_eq!(status, crate::vm::VmStatus::Halted);
18731874
vm
@@ -1889,7 +1890,7 @@ mod tests {
18891890
}],
18901891
}),
18911892
);
1892-
Vm::with_locals(program, 1)
1893+
Vm::new(program.with_local_count(1))
18931894
}
18941895

18951896
fn vm_with_scoped_named_locals() -> Vm {
@@ -1925,7 +1926,7 @@ mod tests {
19251926
],
19261927
}),
19271928
);
1928-
let mut vm = Vm::with_locals(program, 1);
1929+
let mut vm = Vm::new(program.with_local_count(1));
19291930
let status = vm.run().expect("vm should run");
19301931
assert_eq!(status, crate::vm::VmStatus::Halted);
19311932
vm
@@ -2560,7 +2561,7 @@ mod tests {
25602561
debugger.stop_on_entry();
25612562

25622563
let join = std::thread::spawn(move || {
2563-
let mut vm = Vm::with_locals(program, 3);
2564+
let mut vm = Vm::new(program.with_local_count(3));
25642565
vm.run_with_debugger(&mut debugger)
25652566
.expect("debugged vm run should succeed")
25662567
});

pd-vm/src/vm/jit/native/aarch64.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,7 +2775,7 @@ mod tests {
27752775

27762776
#[test]
27772777
fn stloc_step_bridge_moves_owned_values_safely() {
2778-
let mut vm = Vm::with_locals(Program::new(Vec::new(), Vec::new()), 1);
2778+
let mut vm = Vm::new(Program::new(Vec::new(), Vec::new()).with_local_count(1));
27792779
vm.locals[0] = Value::String("old".to_string());
27802780
vm.stack.push(Value::String("new".to_string()));
27812781

@@ -2812,7 +2812,7 @@ mod tests {
28122812

28132813
#[test]
28142814
fn ldloc_step_bridge_clones_owned_values() {
2815-
let mut vm = Vm::with_locals(Program::new(Vec::new(), Vec::new()), 1);
2815+
let mut vm = Vm::new(Program::new(Vec::new(), Vec::new()).with_local_count(1));
28162816
vm.locals[0] = Value::Map(vec![(Value::Int(1), Value::Int(2))]);
28172817

28182818
let status =
@@ -2959,9 +2959,8 @@ mod tests {
29592959
executions: 0,
29602960
};
29612961

2962-
let mut vm = Vm::with_locals(
2963-
Program::new(vec![Value::Int(200), Value::Int(1)], vec![0; 128]),
2964-
2,
2962+
let mut vm = Vm::new(
2963+
Program::new(vec![Value::Int(200), Value::Int(1)], vec![0; 128]).with_local_count(2),
29652964
);
29662965
vm.locals[0] = Value::Int(0);
29672966
vm.locals[1] = Value::Int(0);

pd-vm/src/vm/jit/native/x86_64.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3616,7 +3616,7 @@ mod tests {
36163616

36173617
#[test]
36183618
fn stloc_step_bridge_moves_owned_values_safely() {
3619-
let mut vm = Vm::with_locals(Program::new(Vec::new(), Vec::new()), 1);
3619+
let mut vm = Vm::new(Program::new(Vec::new(), Vec::new()).with_local_count(1));
36203620
vm.locals[0] = Value::String("old".to_string());
36213621
vm.stack.push(Value::String("new".to_string()));
36223622

0 commit comments

Comments
 (0)