Skip to content

Commit 552dca9

Browse files
committed
feat: model live JIT entry stacks in SSA
1 parent b33db47 commit 552dca9

7 files changed

Lines changed: 456 additions & 93 deletions

File tree

src/vm/jit/ir.rs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ pub(crate) struct SsaBlock {
439439
#[derive(Clone, Debug, PartialEq)]
440440
pub(crate) struct SsaTrace {
441441
pub(crate) root_ip: usize,
442+
pub(crate) entry_stack_depth: usize,
442443
pub(crate) entry: SsaBlockId,
443444
pub(crate) blocks: Vec<SsaBlock>,
444445
pub(crate) exits: Vec<SsaExit>,
@@ -461,6 +462,12 @@ impl SsaTrace {
461462
if entry.id != self.entry {
462463
return Err(SsaVerifyError::UnknownEntry(self.entry));
463464
}
465+
if self.entry_stack_depth > entry.params.len() {
466+
return Err(SsaVerifyError::EntryStackDepthMismatch {
467+
depth: self.entry_stack_depth,
468+
params: entry.params.len(),
469+
});
470+
}
464471

465472
let block_param_reprs = self
466473
.blocks
@@ -531,7 +538,11 @@ impl SsaTrace {
531538
#[allow(dead_code)]
532539
pub(crate) fn render_text(&self) -> String {
533540
let mut out = String::new();
534-
let _ = writeln!(&mut out, "trace root_ip={}", self.root_ip);
541+
let _ = writeln!(
542+
&mut out,
543+
"trace root_ip={} entry_stack_depth={}",
544+
self.root_ip, self.entry_stack_depth
545+
);
535546
for block in &self.blocks {
536547
let _ = write!(&mut out, "{}(", block.id);
537548
for (index, param) in block.params.iter().enumerate() {
@@ -583,6 +594,10 @@ pub(crate) enum SsaVerifyError {
583594
DuplicateValue(SsaValueId),
584595
MissingTerminator(SsaBlockId),
585596
UnknownEntry(SsaBlockId),
597+
EntryStackDepthMismatch {
598+
depth: usize,
599+
params: usize,
600+
},
586601
UseBeforeDef {
587602
block: SsaBlockId,
588603
value: SsaValueId,
@@ -614,11 +629,12 @@ pub(crate) struct SsaTraceBuilder {
614629
}
615630

616631
impl SsaTraceBuilder {
617-
pub(crate) fn new(root_ip: usize) -> Self {
632+
pub(crate) fn new(root_ip: usize, entry_stack_depth: usize) -> Self {
618633
let entry = SsaBlockId::new(0);
619634
Self {
620635
trace: SsaTrace {
621636
root_ip,
637+
entry_stack_depth,
622638
entry,
623639
blocks: vec![SsaBlock {
624640
id: entry,
@@ -966,7 +982,7 @@ mod tests {
966982

967983
#[test]
968984
fn verifier_accepts_simple_loop_shape() {
969-
let mut builder = SsaTraceBuilder::new(12);
985+
let mut builder = SsaTraceBuilder::new(12, 0);
970986
let entry = builder.entry();
971987
let local = builder
972988
.append_param(entry, SsaValueRepr::I64, "local0")
@@ -1005,7 +1021,7 @@ mod tests {
10051021

10061022
#[test]
10071023
fn verifier_rejects_jump_arity_mismatch() {
1008-
let mut builder = SsaTraceBuilder::new(1);
1024+
let mut builder = SsaTraceBuilder::new(1, 0);
10091025
let entry = builder.entry();
10101026
let next = builder.create_block();
10111027
let value = builder
@@ -1037,4 +1053,16 @@ mod tests {
10371053
})
10381054
);
10391055
}
1056+
1057+
#[test]
1058+
fn verifier_rejects_entry_stack_depth_beyond_entry_params() {
1059+
let mut builder = SsaTraceBuilder::new(1, 1);
1060+
let entry = builder.entry();
1061+
let exit = builder.add_exit(2, Vec::new(), Vec::new());
1062+
builder
1063+
.set_terminator(entry, SsaTerminator::Return { exit })
1064+
.expect("return");
1065+
let trace = builder.finish();
1066+
assert!(trace.verify().is_err());
1067+
}
10401068
}

src/vm/jit/native/lower.rs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,14 @@ fn try_compile_ssa_trace(
227227
);
228228
b.ins()
229229
.store(MemFlags::new(), root_ip, vm_ptr, offsets.vm_ip);
230+
emit_entry_stack_depth_guard(
231+
&mut b,
232+
vm_ptr,
233+
exit_block,
234+
pointer_type,
235+
offsets,
236+
ssa.entry_stack_depth,
237+
)?;
230238

231239
let entry_ssa_block = ssa
232240
.blocks
@@ -241,7 +249,16 @@ fn try_compile_ssa_trace(
241249
pointer_type,
242250
layout,
243251
offsets,
244-
entry_ssa_block.params.len(),
252+
ssa.entry_stack_depth,
253+
entry_ssa_block
254+
.params
255+
.len()
256+
.checked_sub(ssa.entry_stack_depth)
257+
.ok_or_else(|| {
258+
VmError::JitNative(
259+
"SSA entry stack depth exceeds entry parameter count".to_string(),
260+
)
261+
})?,
245262
)?;
246263
init_owned_value_temps(
247264
&mut b,
@@ -743,12 +760,30 @@ fn build_entry_args(
743760
pointer_type: cranelift_codegen::ir::Type,
744761
layout: crate::vm::native::NativeStackLayout,
745762
offsets: ResolvedOffsets,
763+
stack_depth: usize,
746764
local_count: usize,
747765
) -> VmResult<Vec<cranelift_codegen::ir::Value>> {
766+
let stack_ptr = b
767+
.ins()
768+
.load(pointer_type, MemFlags::new(), vm_ptr, offsets.stack_ptr);
748769
let locals_ptr = b
749770
.ins()
750771
.load(pointer_type, MemFlags::new(), vm_ptr, offsets.locals_ptr);
751-
let mut args = Vec::with_capacity(local_count);
772+
let mut args = Vec::with_capacity(stack_depth + local_count);
773+
for stack_index in 0..stack_depth {
774+
let index = b.ins().iconst(
775+
pointer_type,
776+
i64::try_from(stack_index)
777+
.map_err(|_| VmError::JitNative("SSA stack index out of range".to_string()))?,
778+
);
779+
args.push(ssa_value_addr(
780+
b,
781+
pointer_type,
782+
stack_ptr,
783+
index,
784+
layout.value.size,
785+
));
786+
}
752787
for local in 0..local_count {
753788
let index = b.ins().iconst(
754789
pointer_type,
@@ -766,6 +801,35 @@ fn build_entry_args(
766801
Ok(args)
767802
}
768803

804+
fn emit_entry_stack_depth_guard(
805+
b: &mut FunctionBuilder,
806+
vm_ptr: cranelift_codegen::ir::Value,
807+
exit_block: Block,
808+
pointer_type: cranelift_codegen::ir::Type,
809+
offsets: ResolvedOffsets,
810+
expected_depth: usize,
811+
) -> VmResult<()> {
812+
let actual_depth = b
813+
.ins()
814+
.load(pointer_type, MemFlags::new(), vm_ptr, offsets.stack_len);
815+
let expected_depth = b.ins().iconst(
816+
pointer_type,
817+
i64::try_from(expected_depth)
818+
.map_err(|_| VmError::JitNative("SSA entry stack depth out of range".to_string()))?,
819+
);
820+
let matches = b.ins().icmp(IntCC::Equal, actual_depth, expected_depth);
821+
let matched = b.create_block();
822+
let mismatch = b.create_block();
823+
b.ins().brif(matches, matched, &[], mismatch, &[]);
824+
825+
b.switch_to_block(mismatch);
826+
let status = b.ins().iconst(types::I32, STATUS_CONTINUE as i64);
827+
jump_with_status(b, exit_block, status);
828+
829+
b.switch_to_block(matched);
830+
Ok(())
831+
}
832+
769833
fn ssa_block_args(values: impl IntoIterator<Item = cranelift_codegen::ir::Value>) -> Vec<BlockArg> {
770834
values.into_iter().map(BlockArg::Value).collect()
771835
}
@@ -2989,6 +3053,8 @@ fn ssa_emit_trace_exit_status(
29893053

29903054
#[derive(Clone, Copy)]
29913055
struct ResolvedOffsets {
3056+
stack_ptr: i32,
3057+
stack_len: i32,
29923058
locals_ptr: i32,
29933059
vm_ip: i32,
29943060
fuel_remaining: i32,
@@ -3186,13 +3252,25 @@ fn emit_epoch_tick_inline_core(
31863252
}
31873253

31883254
fn resolve_offsets(layout: NativeStackLayout) -> VmResult<ResolvedOffsets> {
3255+
let stack_ptr = checked_add_i32(
3256+
layout.vm_stack_offset,
3257+
layout.stack_vec.ptr_offset,
3258+
"stack ptr offset overflow",
3259+
)?;
3260+
let stack_len = checked_add_i32(
3261+
layout.vm_stack_offset,
3262+
layout.stack_vec.len_offset,
3263+
"stack len offset overflow",
3264+
)?;
31893265
let locals_ptr = checked_add_i32(
31903266
layout.vm_locals_offset,
31913267
layout.stack_vec.ptr_offset,
31923268
"locals ptr offset overflow",
31933269
)?;
31943270

31953271
Ok(ResolvedOffsets {
3272+
stack_ptr,
3273+
stack_len,
31963274
locals_ptr,
31973275
vm_ip: layout.vm_ip_offset,
31983276
fuel_remaining: layout.vm_fuel_remaining_offset,

0 commit comments

Comments
 (0)