Skip to content

Commit 5b63c87

Browse files
[DI] Fix live range tracking off-by-one confusions (#10570)
* Dump blocks in the VL table * Add a test * Work around #10572 in tests * [DI] Fix live range tracking off-by-one confusions How things used to work w.r.t. instruction indices (IIs): 1) In lowering: - Reversed order: IIs represented "before IP"s. - Block args were defined one instruction too late, but this issue was masked due to how RA allocates, at least in simple examples. - Execution order: IIs represented "after IP"s. 2) In RA: - IIs represented "before IP"s. - Notice the mismatch. 3) In emit: - RA directions w.r.t. the explicit ProgPoint positions were not respected and always treated as "after". How things work after this change: 1) In lowering: - Reversed order: IIs represent "after IP"s. - Execution order: IIs represent "before IP"s. 2) In RA: - No change; mismatch fixed. 3) In emit: - ProgPoint positions now respected. This fixes various "silent bad debug info" issues.
1 parent 67494c8 commit 5b63c87

File tree

6 files changed

+180
-65
lines changed

6 files changed

+180
-65
lines changed

cranelift/codegen/src/machinst/lower.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,10 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
710710
continue;
711711
}
712712

713+
// Value defined by "inst" becomes live after it in normal
714+
// order, and therefore **before** in reversed order.
715+
self.emit_value_label_live_range_start_for_inst(inst);
716+
713717
// Normal instruction: codegen if the instruction is side-effecting
714718
// or any of its outputs is used.
715719
if has_side_effect || value_needed {
@@ -801,11 +805,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
801805
}
802806
}
803807
}
804-
805-
// Emit value-label markers if needed, to later recover
806-
// debug mappings. This must happen before the instruction
807-
// (so after we emit, in bottom-to-top pass).
808-
self.emit_value_label_markers_for_inst(inst);
809808
}
810809

811810
// Add the block params to this block.
@@ -869,7 +868,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
869868
}
870869
}
871870

872-
fn emit_value_label_markers_for_inst(&mut self, inst: Inst) {
871+
fn emit_value_label_live_range_start_for_inst(&mut self, inst: Inst) {
873872
if self.f.dfg.values_labels.is_none() {
874873
return;
875874
}
@@ -884,7 +883,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
884883
}
885884
}
886885

887-
fn emit_value_label_markers_for_block_args(&mut self, block: Block) {
886+
fn emit_value_label_live_range_start_for_block_args(&mut self, block: Block) {
888887
if self.f.dfg.values_labels.is_none() {
889888
return;
890889
}
@@ -1088,7 +1087,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
10881087
// Original block body.
10891088
if let Some(bb) = lb.orig_block() {
10901089
self.lower_clif_block(backend, bb, ctrl_plane)?;
1091-
self.emit_value_label_markers_for_block_args(bb);
1090+
self.emit_value_label_live_range_start_for_block_args(bb);
10921091
}
10931092

10941093
if bindex.index() == 0 {

cranelift/codegen/src/machinst/vcode.rs

Lines changed: 81 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ use crate::CodegenError;
2626
use crate::{machinst::*, trace_log_enabled};
2727
use crate::{LabelValueLoc, ValueLocRange};
2828
use regalloc2::{
29-
Edit, Function as RegallocFunction, InstOrEdit, InstRange, MachineEnv, Operand,
30-
OperandConstraint, OperandKind, PRegSet, RegClass,
29+
Edit, Function as RegallocFunction, InstOrEdit, InstPosition, InstRange, MachineEnv, Operand,
30+
OperandConstraint, OperandKind, PRegSet, ProgPoint, RegClass,
3131
};
3232
use rustc_hash::FxHashMap;
3333

@@ -381,18 +381,37 @@ impl<I: VCodeInst> VCodeBuilder<I> {
381381

382382
/// Add a debug value label to a register.
383383
pub fn add_value_label(&mut self, reg: Reg, label: ValueLabel) {
384-
// We'll fix up labels in reverse(). Because we're generating
385-
// code bottom-to-top, the liverange of the label goes *from*
386-
// the last index at which was defined (or 0, which is the end
387-
// of the eventual function) *to* just this instruction, and
388-
// no further.
389-
let inst = InsnIndex::new(self.vcode.insts.len());
384+
// 1) In the reversed order, we consider the instructions
385+
// that define ranges in the "debug_info" array to refer
386+
// to the IP **after** them (when reversed):
387+
// IP[2]__| Inst 3 |
388+
// IP[1]__| Inst 2 |
389+
// IP[0]__| Inst 1 |
390+
// | Inst 0 |
391+
// This is so that we can represent IP[<function start>],
392+
// done at the cost of not being to represent IP[<function end>],
393+
// which is OK since no values will be live at that point.
394+
// 2) The live range for "reg" begins at the current IP
395+
// and continues until the next, in execution order,
396+
// VReg that defines "label". Since the ranges are open
397+
// at the end, the subtraction of 1 cancels out:
398+
// [last..current IP] <=>
399+
// [last..last emitted inst index] <=>
400+
// [last..next_inst_index - 1] <=>
401+
// [last..next_inst_index)
402+
//
403+
let next_inst_index = self.vcode.insts.len();
404+
if next_inst_index == 0 {
405+
// This would produce a defective [0..0) range.
406+
return;
407+
}
408+
let next_inst = InsnIndex::new(next_inst_index);
390409
let labels = self.debug_info.entry(label).or_insert_with(|| vec![]);
391410
let last = labels
392411
.last()
393412
.map(|(_start, end, _vreg)| *end)
394413
.unwrap_or(InsnIndex::new(0));
395-
labels.push((last, inst, reg.into()));
414+
labels.push((last, next_inst, reg.into()));
396415
}
397416

398417
/// Access the constants.
@@ -1163,11 +1182,20 @@ impl<I: VCodeInst> VCode<I> {
11631182
for &(label, from, to, alloc) in &regalloc.debug_locations {
11641183
let label = ValueLabel::from_u32(label);
11651184
let ranges = value_labels_ranges.entry(label).or_insert_with(|| vec![]);
1166-
let from_offset = inst_offsets[from.inst().index()];
1167-
let to_offset = if to.inst().index() == inst_offsets.len() {
1185+
let prog_point_to_inst = |prog_point: ProgPoint| {
1186+
let mut inst = prog_point.inst();
1187+
if prog_point.pos() == InstPosition::After {
1188+
inst = inst.next();
1189+
}
1190+
inst.index()
1191+
};
1192+
let from_inst_index = prog_point_to_inst(from);
1193+
let to_inst_index = prog_point_to_inst(to);
1194+
let from_offset = inst_offsets[from_inst_index];
1195+
let to_offset = if to_inst_index == inst_offsets.len() {
11681196
func_body_len
11691197
} else {
1170-
inst_offsets[to.inst().index()]
1198+
inst_offsets[to_inst_index]
11711199
};
11721200

11731201
// Empty ranges or unavailable offsets can happen
@@ -1193,34 +1221,18 @@ impl<I: VCodeInst> VCode<I> {
11931221
LabelValueLoc::CFAOffset(cfa_to_sp_offset + slot_offset)
11941222
};
11951223

1196-
// ValueLocRanges are recorded by *instruction-end
1197-
// offset*. `from_offset` is the *start* of the
1198-
// instruction; that is the same as the end of another
1199-
// instruction, so we only want to begin coverage once
1200-
// we are past the previous instruction's end.
1201-
let start = from_offset + 1;
1202-
1203-
// Likewise, `end` is exclusive, but we want to
1204-
// *include* the end of the last
1205-
// instruction. `to_offset` is the start of the
1206-
// `to`-instruction, which is the exclusive end, i.e.,
1207-
// the first instruction not covered. That
1208-
// instruction's start is the same as the end of the
1209-
// last instruction that is included, so we go one
1210-
// byte further to be sure to include it.
1211-
let end = to_offset + 1;
1212-
12131224
// Coalesce adjacent ranges that for the same location
12141225
// to minimize output size here and for the consumers.
12151226
if let Some(last_loc_range) = ranges.last_mut() {
1216-
if last_loc_range.loc == loc && last_loc_range.end == start {
1227+
if last_loc_range.loc == loc && last_loc_range.end == from_offset {
12171228
trace!(
1218-
"Extending debug range for {:?} in {:?} to {}",
1229+
"Extending debug range for {:?} in {:?} to Inst {} ({})",
12191230
label,
12201231
loc,
1221-
end
1232+
to_inst_index,
1233+
to_offset
12221234
);
1223-
last_loc_range.end = end;
1235+
last_loc_range.end = to_offset;
12241236
continue;
12251237
}
12261238
}
@@ -1229,13 +1241,17 @@ impl<I: VCodeInst> VCode<I> {
12291241
"Recording debug range for {:?} in {:?}: [Inst {}..Inst {}) [{}..{})",
12301242
label,
12311243
loc,
1232-
from.inst().index(),
1233-
to.inst().index(),
1234-
start,
1235-
end
1244+
from_inst_index,
1245+
to_inst_index,
1246+
from_offset,
1247+
to_offset
12361248
);
12371249

1238-
ranges.push(ValueLocRange { loc, start, end });
1250+
ranges.push(ValueLocRange {
1251+
loc,
1252+
start: from_offset,
1253+
end: to_offset,
1254+
});
12391255
}
12401256

12411257
value_labels_ranges
@@ -1279,10 +1295,10 @@ impl<I: VCodeInst> VCode<I> {
12791295
enum Row {
12801296
Head,
12811297
Line,
1282-
Inst(usize),
1298+
Inst(usize, usize),
12831299
}
12841300

1285-
let mut widths = vec![0; 2 + 2 * labels.len()];
1301+
let mut widths = vec![0; 3 + 2 * labels.len()];
12861302
let mut row = String::new();
12871303
let mut output_row = |row_kind: Row, mode: Mode| {
12881304
let mut column_index = 0;
@@ -1320,6 +1336,7 @@ impl<I: VCodeInst> VCode<I> {
13201336

13211337
match row_kind {
13221338
Row::Head => {
1339+
output_cell!("BB");
13231340
output_cell!("Inst");
13241341
output_cell!("IP");
13251342
for label in &labels {
@@ -1328,21 +1345,23 @@ impl<I: VCodeInst> VCode<I> {
13281345
}
13291346
Row::Line => {
13301347
debug_assert!(mode == Mode::Emit);
1331-
output_cell_impl!('-', 1, "");
1332-
output_cell_impl!('-', 1, "");
1348+
for _ in 0..3 {
1349+
output_cell_impl!('-', 1, "");
1350+
}
13331351
for _ in &labels {
13341352
output_cell_impl!('-', 2, "");
13351353
}
13361354
}
1337-
Row::Inst(inst_index) => {
1355+
Row::Inst(block_index, inst_index) => {
1356+
debug_assert!(inst_index < self.num_insts());
1357+
if self.block_ranges.get(block_index).start == inst_index {
1358+
output_cell!("B{}", block_index);
1359+
} else {
1360+
output_cell!("");
1361+
}
13381362
output_cell!("Inst {inst_index} ");
13391363
output_cell!("{} ", inst_offsets[inst_index]);
13401364

1341-
// The ranges which we query below operate on the logic of
1342-
// "IP(inst) == IP after inst", while the rows of our table
1343-
// represent IPs 'before' "inst", so we need to convert "inst"
1344-
// into these "IP after" coordinates.
1345-
let inst_ip_index = inst_index.wrapping_sub(1);
13461365
for label in &labels {
13471366
// First, the VReg.
13481367
use regalloc2::Inst;
@@ -1364,12 +1383,12 @@ impl<I: VCodeInst> VCode<I> {
13641383
}
13651384
};
13661385
let vreg_index =
1367-
vregs.binary_search_by(|(l, s, e, _)| vreg_cmp(inst_ip_index, l, s, e));
1386+
vregs.binary_search_by(|(l, s, e, _)| vreg_cmp(inst_index, l, s, e));
13681387
if let Ok(vreg_index) = vreg_index {
13691388
let mut prev_vreg = None;
1370-
if inst_ip_index > 0 {
1389+
if inst_index > 0 {
13711390
let prev_vreg_index = vregs.binary_search_by(|(l, s, e, _)| {
1372-
vreg_cmp(inst_ip_index - 1, l, s, e)
1391+
vreg_cmp(inst_index - 1, l, s, e)
13731392
});
13741393
if let Ok(prev_vreg_index) = prev_vreg_index {
13751394
prev_vreg = Some(vregs[prev_vreg_index].3);
@@ -1387,13 +1406,14 @@ impl<I: VCodeInst> VCode<I> {
13871406
}
13881407

13891408
// Second, the allocated location.
1409+
let inst_prog_point = ProgPoint::before(Inst::new(inst_index));
13901410
let range_index = regalloc.debug_locations.binary_search_by(
13911411
|(range_label, range_start, range_end, _)| match range_label.cmp(label)
13921412
{
13931413
Ordering::Equal => {
1394-
if range_end.inst().index() <= inst_ip_index {
1414+
if *range_end <= inst_prog_point {
13951415
Ordering::Less
1396-
} else if range_start.inst().index() > inst_ip_index {
1416+
} else if *range_start > inst_prog_point {
13971417
Ordering::Greater
13981418
} else {
13991419
Ordering::Equal
@@ -1423,15 +1443,19 @@ impl<I: VCodeInst> VCode<I> {
14231443
}
14241444
};
14251445

1426-
for inst_index in 0..inst_offsets.len() {
1427-
output_row(Row::Inst(inst_index), Mode::Measure);
1446+
for block_index in 0..self.num_blocks() {
1447+
for inst_index in self.block_ranges.get(block_index) {
1448+
output_row(Row::Inst(block_index, inst_index), Mode::Measure);
1449+
}
14281450
}
14291451
output_row(Row::Head, Mode::Measure);
14301452

14311453
output_row(Row::Head, Mode::Emit);
14321454
output_row(Row::Line, Mode::Emit);
1433-
for inst_index in 0..inst_offsets.len() {
1434-
output_row(Row::Inst(inst_index), Mode::Emit);
1455+
for block_index in 0..self.num_blocks() {
1456+
for inst_index in self.block_ranges.get(block_index) {
1457+
output_row(Row::Inst(block_index, inst_index), Mode::Emit);
1458+
}
14351459
}
14361460
}
14371461

crates/test-programs/artifacts/build.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,18 @@ fn build_debug_info_assets(paths_code: &mut String) {
234234
]
235235
.as_slice(),
236236
),
237+
(
238+
"clang",
239+
"codegen-optimized-wasm-optimized.wasm",
240+
[
241+
"-target",
242+
"wasm32-unknown-wasip1",
243+
"-g",
244+
"-O2",
245+
"codegen-optimized-wasm-optimized.cpp",
246+
]
247+
.as_slice(),
248+
),
237249
];
238250

239251
// The debug tests relying on these assets are ignored by default,

tests/all/debug/gdb.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub fn test_debug_dwarf_gdb() -> Result<()> {
5959
&[
6060
"-Ccache=n",
6161
"-Ddebug-info",
62+
"-Oopt-level=0",
6263
"--invoke",
6364
"fib",
6465
"tests/all/debug/testsuite/fib-wasm.wasm",

tests/all/debug/lldb.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ pub fn test_debug_dwarf_lldb() -> Result<()> {
6969
&[
7070
"-Ccache=n",
7171
"-Ddebug-info",
72+
"-Oopt-level=0",
7273
"--invoke",
7374
"fib",
7475
assets::FIB_WASM_WASM_PATH,
@@ -106,6 +107,7 @@ pub fn test_debug_dwarf5_lldb() -> Result<()> {
106107
&[
107108
"-Ccache=n",
108109
"-Ddebug-info",
110+
"-Oopt-level=0",
109111
"--invoke",
110112
"fib",
111113
assets::FIB_WASM_DWARF5_WASM_PATH,
@@ -143,6 +145,7 @@ pub fn test_debug_split_dwarf4_lldb() -> Result<()> {
143145
&[
144146
"-Ccache=n",
145147
"-Ddebug-info",
148+
"-Oopt-level=0",
146149
"--invoke",
147150
"fib",
148151
assets::FIB_WASM_SPLIT4_WASM_PATH,
@@ -261,6 +264,41 @@ check: exited with status = 0
261264
Ok(())
262265
}
263266

267+
#[test]
268+
#[ignore]
269+
pub fn test_debug_codegen_optimized_wasm_optimized_lldb() -> Result<()> {
270+
let output = lldb_with_script(
271+
&[
272+
"-Ccache=n",
273+
"-Ddebug-info",
274+
"-Oopt-level=2",
275+
assets::CODEGEN_OPTIMIZED_WASM_OPTIMIZED_WASM_PATH,
276+
],
277+
r#"b InitializeTest
278+
r
279+
b codegen-optimized-wasm-optimized.cpp:21
280+
b codegen-optimized-wasm-optimized.cpp:27
281+
c
282+
v b
283+
c
284+
v b
285+
c"#,
286+
)?;
287+
288+
check_lldb_output(
289+
&output,
290+
r#"
291+
check: stop reason = breakpoint 1.1
292+
check: stop reason = breakpoint 2.1
293+
check: b = 42
294+
check: stop reason = breakpoint 3.1
295+
check: b = 43
296+
check: exited with status = 0
297+
"#,
298+
)?;
299+
Ok(())
300+
}
301+
264302
#[test]
265303
#[ignore]
266304
pub fn test_debug_dwarf_ref() -> Result<()> {

0 commit comments

Comments
 (0)