Skip to content

Commit 3feae08

Browse files
committed
pd-vm: debugger filtering of locals based on liveless and visibility
1 parent c451f5a commit 3feae08

6 files changed

Lines changed: 480 additions & 43 deletions

File tree

pd-vm/src/assembler.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,20 @@ impl Assembler {
7777
self.debug.add_function(name, args);
7878
}
7979

80-
pub fn add_local(&mut self, name: String, index: u8) {
81-
self.debug.add_local(name, index);
82-
}
80+
pub fn add_local(&mut self, name: String, index: u8) {
81+
self.debug.add_local(name, index);
82+
}
83+
84+
pub fn add_local_with_range(
85+
&mut self,
86+
name: String,
87+
index: u8,
88+
declared_line: Option<u32>,
89+
last_line: Option<u32>,
90+
) {
91+
self.debug
92+
.add_local_with_range(name, index, declared_line, last_line);
93+
}
8394

8495
pub fn add_constant(&mut self, value: Value) -> u32 {
8596
match value {

pd-vm/src/compiler/mod.rs

Lines changed: 228 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,12 @@ pub struct CompiledProgram {
227227
pub functions: Vec<FunctionDecl>,
228228
}
229229

230+
#[derive(Clone, Copy, Debug, Default)]
231+
struct LocalDebugRange {
232+
declared_line: Option<u32>,
233+
last_line: Option<u32>,
234+
}
235+
230236
impl CompiledProgram {
231237
#[cfg(feature = "runtime")]
232238
pub fn into_vm(self) -> Vm {
@@ -308,6 +314,7 @@ fn compile_parsed_output(
308314
.filter(|func| !is_compiler_primitive_import(&func.name))
309315
.cloned()
310316
.collect::<Vec<_>>();
317+
let local_debug_ranges = collect_local_debug_ranges(&stmts, &function_impls);
311318

312319
let mut compiler = Compiler::new();
313320
compiler.set_source(source);
@@ -317,8 +324,9 @@ fn compile_parsed_output(
317324
compiler.add_function_debug(func);
318325
}
319326
for (name, index) in local_bindings {
327+
let range = local_debug_ranges.get(&index).copied().unwrap_or_default();
320328
compiler
321-
.add_local_debug(name, index)
329+
.add_local_debug(name, index, range.declared_line, range.last_line)
322330
.map_err(SourceError::Compile)?;
323331
}
324332
let mut program = compiler
@@ -469,8 +477,19 @@ impl Compiler {
469477
.add_function(func.name.clone(), func.args.clone());
470478
}
471479

472-
pub fn add_local_debug(&mut self, name: String, index: LocalSlot) -> Result<(), CompileError> {
473-
self.assembler.add_local(name, local_slot_operand(index)?);
480+
pub fn add_local_debug(
481+
&mut self,
482+
name: String,
483+
index: LocalSlot,
484+
declared_line: Option<u32>,
485+
last_line: Option<u32>,
486+
) -> Result<(), CompileError> {
487+
self.assembler.add_local_with_range(
488+
name,
489+
local_slot_operand(index)?,
490+
declared_line,
491+
last_line,
492+
);
474493
Ok(())
475494
}
476495

@@ -1066,6 +1085,212 @@ fn local_slot_operand(index: LocalSlot) -> Result<u8, CompileError> {
10661085
u8::try_from(index).map_err(|_| CompileError::LocalSlotOverflow(index))
10671086
}
10681087

1088+
fn collect_local_debug_ranges(
1089+
stmts: &[Stmt],
1090+
function_impls: &HashMap<u16, FunctionImpl>,
1091+
) -> HashMap<LocalSlot, LocalDebugRange> {
1092+
let mut ranges = HashMap::<LocalSlot, LocalDebugRange>::new();
1093+
for stmt in stmts {
1094+
record_stmt_local_debug_ranges(stmt, &mut ranges);
1095+
}
1096+
for function_impl in function_impls.values() {
1097+
for stmt in &function_impl.body_stmts {
1098+
record_stmt_local_debug_ranges(stmt, &mut ranges);
1099+
}
1100+
let fallback_line = function_impl
1101+
.body_stmts
1102+
.last()
1103+
.map(stmt_source_line)
1104+
.unwrap_or(1);
1105+
record_expr_local_debug_ranges(&function_impl.body_expr, fallback_line, &mut ranges);
1106+
}
1107+
ranges
1108+
}
1109+
1110+
fn record_stmt_local_debug_ranges(
1111+
stmt: &Stmt,
1112+
ranges: &mut HashMap<LocalSlot, LocalDebugRange>,
1113+
) {
1114+
match stmt {
1115+
Stmt::Noop { .. } | Stmt::FuncDecl { .. } | Stmt::Break { .. } | Stmt::Continue { .. } => {}
1116+
Stmt::Let { index, expr, line } => {
1117+
note_local_decl(ranges, *index, *line);
1118+
record_expr_local_debug_ranges(expr, *line, ranges);
1119+
}
1120+
Stmt::Assign { index, expr, line } => {
1121+
note_local_use(ranges, *index, *line);
1122+
record_expr_local_debug_ranges(expr, *line, ranges);
1123+
}
1124+
Stmt::ClosureLet { line, closure } => {
1125+
for (source_slot, captured_slot) in &closure.capture_copies {
1126+
note_local_use(ranges, *source_slot, *line);
1127+
note_local_use(ranges, *captured_slot, *line);
1128+
}
1129+
record_expr_local_debug_ranges(&closure.body, *line, ranges);
1130+
}
1131+
Stmt::Expr { expr, line } => {
1132+
record_expr_local_debug_ranges(expr, *line, ranges);
1133+
}
1134+
Stmt::IfElse {
1135+
condition,
1136+
then_branch,
1137+
else_branch,
1138+
line,
1139+
} => {
1140+
record_expr_local_debug_ranges(condition, *line, ranges);
1141+
for nested in then_branch {
1142+
record_stmt_local_debug_ranges(nested, ranges);
1143+
}
1144+
for nested in else_branch {
1145+
record_stmt_local_debug_ranges(nested, ranges);
1146+
}
1147+
}
1148+
Stmt::For {
1149+
init,
1150+
condition,
1151+
post,
1152+
body,
1153+
line,
1154+
} => {
1155+
record_stmt_local_debug_ranges(init, ranges);
1156+
record_expr_local_debug_ranges(condition, *line, ranges);
1157+
record_stmt_local_debug_ranges(post, ranges);
1158+
for nested in body {
1159+
record_stmt_local_debug_ranges(nested, ranges);
1160+
}
1161+
}
1162+
Stmt::While {
1163+
condition,
1164+
body,
1165+
line,
1166+
} => {
1167+
record_expr_local_debug_ranges(condition, *line, ranges);
1168+
for nested in body {
1169+
record_stmt_local_debug_ranges(nested, ranges);
1170+
}
1171+
}
1172+
}
1173+
}
1174+
1175+
fn record_expr_local_debug_ranges(
1176+
expr: &Expr,
1177+
line: u32,
1178+
ranges: &mut HashMap<LocalSlot, LocalDebugRange>,
1179+
) {
1180+
match expr {
1181+
Expr::Null
1182+
| Expr::Int(_)
1183+
| Expr::Float(_)
1184+
| Expr::Bool(_)
1185+
| Expr::String(_)
1186+
| Expr::FunctionRef(_) => {}
1187+
Expr::Var(index) => {
1188+
note_local_use(ranges, *index, line);
1189+
}
1190+
Expr::Call(_, args) => {
1191+
for arg in args {
1192+
record_expr_local_debug_ranges(arg, line, ranges);
1193+
}
1194+
}
1195+
Expr::LocalCall(index, args) => {
1196+
note_local_use(ranges, *index, line);
1197+
for arg in args {
1198+
record_expr_local_debug_ranges(arg, line, ranges);
1199+
}
1200+
}
1201+
Expr::Closure(closure) => {
1202+
for (source_slot, captured_slot) in &closure.capture_copies {
1203+
note_local_use(ranges, *source_slot, line);
1204+
note_local_use(ranges, *captured_slot, line);
1205+
}
1206+
record_expr_local_debug_ranges(&closure.body, line, ranges);
1207+
}
1208+
Expr::ClosureCall(closure, args) => {
1209+
for arg in args {
1210+
record_expr_local_debug_ranges(arg, line, ranges);
1211+
}
1212+
for (source_slot, captured_slot) in &closure.capture_copies {
1213+
note_local_use(ranges, *source_slot, line);
1214+
note_local_use(ranges, *captured_slot, line);
1215+
}
1216+
record_expr_local_debug_ranges(&closure.body, line, ranges);
1217+
}
1218+
Expr::Add(lhs, rhs)
1219+
| Expr::Sub(lhs, rhs)
1220+
| Expr::Mul(lhs, rhs)
1221+
| Expr::Div(lhs, rhs)
1222+
| Expr::Mod(lhs, rhs)
1223+
| Expr::And(lhs, rhs)
1224+
| Expr::Or(lhs, rhs)
1225+
| Expr::Eq(lhs, rhs)
1226+
| Expr::Lt(lhs, rhs)
1227+
| Expr::Gt(lhs, rhs) => {
1228+
record_expr_local_debug_ranges(lhs, line, ranges);
1229+
record_expr_local_debug_ranges(rhs, line, ranges);
1230+
}
1231+
Expr::Neg(inner) | Expr::Not(inner) => {
1232+
record_expr_local_debug_ranges(inner, line, ranges);
1233+
}
1234+
Expr::IfElse {
1235+
condition,
1236+
then_expr,
1237+
else_expr,
1238+
} => {
1239+
record_expr_local_debug_ranges(condition, line, ranges);
1240+
record_expr_local_debug_ranges(then_expr, line, ranges);
1241+
record_expr_local_debug_ranges(else_expr, line, ranges);
1242+
}
1243+
Expr::Match {
1244+
value_slot,
1245+
result_slot,
1246+
value,
1247+
arms,
1248+
default,
1249+
} => {
1250+
note_local_use(ranges, *value_slot, line);
1251+
note_local_use(ranges, *result_slot, line);
1252+
record_expr_local_debug_ranges(value, line, ranges);
1253+
for (_, arm_expr) in arms {
1254+
record_expr_local_debug_ranges(arm_expr, line, ranges);
1255+
}
1256+
record_expr_local_debug_ranges(default, line, ranges);
1257+
}
1258+
Expr::Block { stmts, expr } => {
1259+
for stmt in stmts {
1260+
record_stmt_local_debug_ranges(stmt, ranges);
1261+
}
1262+
record_expr_local_debug_ranges(expr, line, ranges);
1263+
}
1264+
}
1265+
}
1266+
1267+
fn note_local_decl(ranges: &mut HashMap<LocalSlot, LocalDebugRange>, slot: LocalSlot, line: u32) {
1268+
let entry = ranges.entry(slot).or_default();
1269+
entry.declared_line = Some(entry.declared_line.map_or(line, |current| current.min(line)));
1270+
entry.last_line = Some(entry.last_line.map_or(line, |current| current.max(line)));
1271+
}
1272+
1273+
fn note_local_use(ranges: &mut HashMap<LocalSlot, LocalDebugRange>, slot: LocalSlot, line: u32) {
1274+
let entry = ranges.entry(slot).or_default();
1275+
entry.last_line = Some(entry.last_line.map_or(line, |current| current.max(line)));
1276+
}
1277+
1278+
fn stmt_source_line(stmt: &Stmt) -> u32 {
1279+
match stmt {
1280+
Stmt::Noop { line }
1281+
| Stmt::Let { line, .. }
1282+
| Stmt::Assign { line, .. }
1283+
| Stmt::ClosureLet { line, .. }
1284+
| Stmt::FuncDecl { line, .. }
1285+
| Stmt::Expr { line, .. }
1286+
| Stmt::IfElse { line, .. }
1287+
| Stmt::For { line, .. }
1288+
| Stmt::While { line, .. }
1289+
| Stmt::Break { line }
1290+
| Stmt::Continue { line } => *line,
1291+
}
1292+
}
1293+
10691294
fn shift_amount_for_power_of_two(value: i64) -> Option<u32> {
10701295
if value <= 0 {
10711296
return None;

pd-vm/src/debug_info.rs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ pub struct DebugFunction {
1414
pub struct LocalInfo {
1515
pub name: String,
1616
pub index: u8,
17+
pub declared_line: Option<u32>,
18+
pub last_line: Option<u32>,
1719
}
1820

1921
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -106,14 +108,31 @@ impl DebugInfoBuilder {
106108
}
107109

108110
pub fn add_local(&mut self, name: String, index: u8) {
109-
if self
111+
self.add_local_with_range(name, index, None, None);
112+
}
113+
114+
pub fn add_local_with_range(
115+
&mut self,
116+
name: String,
117+
index: u8,
118+
declared_line: Option<u32>,
119+
last_line: Option<u32>,
120+
) {
121+
if let Some(existing) = self
110122
.locals
111-
.iter()
112-
.any(|local| local.name == name || local.index == index)
123+
.iter_mut()
124+
.find(|local| local.name == name || local.index == index)
113125
{
126+
existing.declared_line = merge_min_line(existing.declared_line, declared_line);
127+
existing.last_line = merge_max_line(existing.last_line, last_line);
114128
return;
115129
}
116-
self.locals.push(LocalInfo { name, index });
130+
self.locals.push(LocalInfo {
131+
name,
132+
index,
133+
declared_line,
134+
last_line,
135+
});
117136
}
118137

119138
pub fn mark_line(&mut self, offset: u32, line: u32) {
@@ -147,3 +166,21 @@ impl DebugInfoBuilder {
147166
})
148167
}
149168
}
169+
170+
fn merge_min_line(current: Option<u32>, incoming: Option<u32>) -> Option<u32> {
171+
match (current, incoming) {
172+
(Some(lhs), Some(rhs)) => Some(lhs.min(rhs)),
173+
(Some(lhs), None) => Some(lhs),
174+
(None, Some(rhs)) => Some(rhs),
175+
(None, None) => None,
176+
}
177+
}
178+
179+
fn merge_max_line(current: Option<u32>, incoming: Option<u32>) -> Option<u32> {
180+
match (current, incoming) {
181+
(Some(lhs), Some(rhs)) => Some(lhs.max(rhs)),
182+
(Some(lhs), None) => Some(lhs),
183+
(None, Some(rhs)) => Some(rhs),
184+
(None, None) => None,
185+
}
186+
}

0 commit comments

Comments
 (0)