custom_insts: make SetDebugSrcLoc have a range instead of just the starting location.

This commit is contained in:
Eduard-Mihai Burtescu 2023-05-26 07:56:40 +03:00 committed by Eduard-Mihai Burtescu
parent c5dcd035b1
commit 344605fde8
10 changed files with 211 additions and 129 deletions

View File

@ -680,13 +680,18 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
if span.is_dummy() {
self.custom_inst(void_ty, CustomInst::ClearDebugSrcLoc);
} else {
let (file, line, col) = self.builder.file_line_col_for_op_line(span);
let (file, line_col_range) = self.builder.file_line_col_range_for_debuginfo(span);
let ((line_start, col_start), (line_end, col_end)) =
(line_col_range.start, line_col_range.end);
self.custom_inst(
void_ty,
CustomInst::SetDebugSrcLoc {
file: Operand::IdRef(file.file_name_op_string_id),
line: Operand::IdRef(self.const_u32(line).def(self)),
col: Operand::IdRef(self.const_u32(col).def(self)),
line_start: Operand::IdRef(self.const_u32(line_start).def(self)),
line_end: Operand::IdRef(self.const_u32(line_end).def(self)),
col_start: Operand::IdRef(self.const_u32(col_start).def(self)),
col_end: Operand::IdRef(self.const_u32(col_end).def(self)),
},
);
}
@ -696,7 +701,9 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
if span.is_dummy() {
self.emit().no_line();
} else {
let (file, line, col) = self.builder.file_line_col_for_op_line(span);
let (file, line_col_range) = self.builder.file_line_col_range_for_debuginfo(span);
let (line, col) = line_col_range.start;
self.emit().line(file.file_name_op_string_id, line, col);
}
}

View File

@ -21,6 +21,7 @@ use std::assert_matches::assert_matches;
use std::cell::{RefCell, RefMut};
use std::hash::{Hash, Hasher};
use std::iter;
use std::ops::Range;
use std::str;
use std::{fs::File, io::Write, path::Path};
@ -678,13 +679,28 @@ impl<'tcx> BuilderSpirv<'tcx> {
}
}
pub fn file_line_col_for_op_line(&self, span: Span) -> (DebugFileSpirv<'tcx>, u32, u32) {
let loc = self.source_map.lookup_char_pos(span.lo());
(
self.def_debug_file(loc.file),
loc.line as u32,
loc.col_display as u32,
)
pub fn file_line_col_range_for_debuginfo(
&self,
span: Span,
) -> (DebugFileSpirv<'tcx>, Range<(u32, u32)>) {
let (lo, hi) = (span.lo(), span.hi());
let lo_loc = self.source_map.lookup_char_pos(lo);
let lo_line_col = (lo_loc.line as u32, lo_loc.col_display as u32);
// Only use `hi` if the span is actually a range within a file.
let hi_line_col = if lo <= hi {
let hi_loc = self.source_map.lookup_char_pos(hi);
if lo_loc.file.start_pos == hi_loc.file.start_pos {
(hi_loc.line as u32, hi_loc.col_display as u32)
} else {
lo_line_col
}
} else {
lo_line_col
};
(self.def_debug_file(lo_loc.file), lo_line_col..hi_line_col)
}
fn def_debug_file(&self, sf: Lrc<SourceFile>) -> DebugFileSpirv<'tcx> {

View File

@ -124,18 +124,20 @@ impl<'a> CustomDecoration<'a> for ZombieDecoration<'a> {
}
}
/// Equivalent of `OpLine`, for the places where `rspirv` currently doesn't let
/// us actually emit a real `OpLine`, the way generating SPIR-T directly might.
/// Equivalent of `CustomInst::SetDebugSrcLoc` (see `crate::custom_insts`),
/// for global definitions (i.e. outside functions), where limitations of
/// `rspirv`/`spirt` prevent us from using anything other than decorations.
//
// NOTE(eddyb) by keeping `line`+`col`, we mimick `OpLine` limitations
// (which could be lifted in the future using custom SPIR-T debuginfo).
// NOTE(eddyb) `NonSemantic.Shader.DebugInfo`'s `DebugLine` has both start & end,
// might be good to invest in SPIR-T being able to use NonSemantic debuginfo.
// NOTE(eddyb) `CustomInst::SetDebugSrcLoc` is modelled after `DebugLine` from
// `NonSemantic.Shader.DebugInfo`, might be good to invest in SPIR-T being able
// to use `NonSemantic.Shader.DebugInfo` directly, in all situations.
#[derive(Copy, Clone)]
pub struct SrcLocDecoration<'a> {
pub file_name: &'a str,
pub line: u32,
pub col: u32,
pub line_start: u32,
pub line_end: u32,
pub col_start: u32,
pub col_end: u32,
}
impl<'a> CustomDecoration<'a> for SrcLocDecoration<'a> {
@ -144,24 +146,33 @@ impl<'a> CustomDecoration<'a> for SrcLocDecoration<'a> {
fn encode(self, w: &mut impl fmt::Write) -> fmt::Result {
let Self {
file_name,
line,
col,
line_start,
line_end,
col_start,
col_end,
} = self;
write!(w, "{file_name}:{line}:{col}")
write!(
w,
"{file_name}:{line_start}:{col_start}-{line_end}:{col_end}"
)
}
fn decode(s: &'a str) -> Self {
#[derive(Copy, Clone, Debug)]
struct InvalidSrcLoc<'a>(&'a str);
let err = InvalidSrcLoc(s);
let (s, col) = s.rsplit_once(':').ok_or(err).unwrap();
let (s, line) = s.rsplit_once(':').ok_or(err).unwrap();
let (s, col_end) = s.rsplit_once(':').ok_or(err).unwrap();
let (s, line_end) = s.rsplit_once('-').ok_or(err).unwrap();
let (s, col_start) = s.rsplit_once(':').ok_or(err).unwrap();
let (s, line_start) = s.rsplit_once(':').ok_or(err).unwrap();
let file_name = s;
Self {
file_name,
line: line.parse().unwrap(),
col: col.parse().unwrap(),
line_start: line_start.parse().unwrap(),
line_end: line_end.parse().unwrap(),
col_start: col_start.parse().unwrap(),
col_end: col_end.parse().unwrap(),
}
}
}
@ -174,12 +185,16 @@ impl<'tcx> SrcLocDecoration<'tcx> {
return None;
}
let (file, line, col) = builder.file_line_col_for_op_line(span);
let (file, line_col_range) = builder.file_line_col_range_for_debuginfo(span);
let ((line_start, col_start), (line_end, col_end)) =
(line_col_range.start, line_col_range.end);
Some(Self {
file_name: file.file_name,
line,
col,
line_start,
line_end,
col_start,
col_end,
})
}
}
@ -361,24 +376,37 @@ impl<'a> SpanRegenerator<'a> {
.spv_debug_info
.get_or_insert_with(|| SpvDebugInfo::collect(self.module));
let (file_id, line, col) = match inst.class.opcode {
Op::Line => (
inst.operands[0].unwrap_id_ref(),
inst.operands[1].unwrap_literal_int32(),
inst.operands[2].unwrap_literal_int32(),
),
let (file_id, line_start, line_end, col_start, col_end) = match inst.class.opcode {
Op::Line => {
let file = inst.operands[0].unwrap_id_ref();
let line = inst.operands[1].unwrap_literal_int32();
let col = inst.operands[2].unwrap_literal_int32();
(file, line, line, col, col)
}
Op::ExtInst
if Some(inst.operands[0].unwrap_id_ref())
== spv_debug_info.custom_ext_inst_set_import =>
{
match CustomInst::decode(inst) {
CustomInst::SetDebugSrcLoc { file, line, col } => (
file.unwrap_id_ref(),
spv_debug_info.id_to_op_constant_operand[&line.unwrap_id_ref()]
.unwrap_literal_int32(),
spv_debug_info.id_to_op_constant_operand[&col.unwrap_id_ref()]
.unwrap_literal_int32(),
),
CustomInst::SetDebugSrcLoc {
file,
line_start,
line_end,
col_start,
col_end,
} => {
let const_u32 = |operand: Operand| {
spv_debug_info.id_to_op_constant_operand[&operand.unwrap_id_ref()]
.unwrap_literal_int32()
};
(
file.unwrap_id_ref(),
const_u32(line_start),
const_u32(line_end),
const_u32(col_start),
const_u32(col_end),
)
}
custom_inst @ CustomInst::ClearDebugSrcLoc => {
unreachable!("src_loc_from_debug_inst({inst:?} => {custom_inst:?})")
}
@ -392,8 +420,10 @@ impl<'a> SpanRegenerator<'a> {
.get(&file_id)
.map(|&file_name| SrcLocDecoration {
file_name,
line,
col,
line_start,
line_end,
col_start,
col_end,
})
}
@ -455,66 +485,77 @@ impl<'a> SpanRegenerator<'a> {
pub fn src_loc_to_rustc(&mut self, src_loc: SrcLocDecoration<'_>) -> Option<Span> {
let SrcLocDecoration {
file_name,
line,
col,
line_start,
line_end,
col_start,
col_end,
} = src_loc;
let file = self.regenerate_rustc_source_file(file_name)?;
let line_bpos_range = file.line_bounds(line.checked_sub(1)? as usize);
// FIXME(eddyb) avoid some of the duplicated work when this closure is
// called with `line`/`col` values that are near eachother - thankfully,
// this code should only be hit on the error reporting path anyway.
let line_col_to_bpos = |line: u32, col: u32| {
let line_bpos_range = file.line_bounds(line.checked_sub(1)? as usize);
// Find the special cases (`MultiByteChar`s/`NonNarrowChar`s) in the line.
let multibyte_chars = {
let find = |bpos| {
file.multibyte_chars
.binary_search_by_key(&bpos, |mbc| mbc.pos)
.unwrap_or_else(|x| x)
// Find the special cases (`MultiByteChar`s/`NonNarrowChar`s) in the line.
let multibyte_chars = {
let find = |bpos| {
file.multibyte_chars
.binary_search_by_key(&bpos, |mbc| mbc.pos)
.unwrap_or_else(|x| x)
};
let Range { start, end } = line_bpos_range;
file.multibyte_chars[find(start)..find(end)].iter()
};
let Range { start, end } = line_bpos_range;
file.multibyte_chars[find(start)..find(end)].iter()
};
let non_narrow_chars = {
let find = |bpos| {
file.non_narrow_chars
.binary_search_by_key(&bpos, |nnc| nnc.pos())
.unwrap_or_else(|x| x)
let non_narrow_chars = {
let find = |bpos| {
file.non_narrow_chars
.binary_search_by_key(&bpos, |nnc| nnc.pos())
.unwrap_or_else(|x| x)
};
let Range { start, end } = line_bpos_range;
file.non_narrow_chars[find(start)..find(end)].iter()
};
let Range { start, end } = line_bpos_range;
file.non_narrow_chars[find(start)..find(end)].iter()
};
let mut special_chars = multibyte_chars
.merge_join_by(non_narrow_chars, |mbc, nnc| mbc.pos.cmp(&nnc.pos()))
.peekable();
let mut special_chars = multibyte_chars
.merge_join_by(non_narrow_chars, |mbc, nnc| mbc.pos.cmp(&nnc.pos()))
.peekable();
// Increment the `BytePos` until we reach the right `col_display`, using
// `MultiByteChar`s/`NonNarrowChar`s to track non-trivial contributions
// (this may look inefficient, but lines tend to be short, and `rustc`
// itself is even worse than this, when it comes to `BytePos` lookups).
let (mut cur_bpos, mut cur_col_display) = (line_bpos_range.start, 0);
while cur_bpos < line_bpos_range.end && cur_col_display < col {
let next_special_bpos = special_chars.peek().map(|special| {
special
.as_ref()
.map_any(|mbc| mbc.pos, |nnc| nnc.pos())
.reduce(|x, _| x)
});
// Increment the `BytePos` until we reach the right `col_display`, using
// `MultiByteChar`s/`NonNarrowChar`s to track non-trivial contributions
// (this may look inefficient, but lines tend to be short, and `rustc`
// itself is even worse than this, when it comes to `BytePos` lookups).
let (mut cur_bpos, mut cur_col_display) = (line_bpos_range.start, 0);
while cur_bpos < line_bpos_range.end && cur_col_display < col {
let next_special_bpos = special_chars.peek().map(|special| {
special
.as_ref()
.map_any(|mbc| mbc.pos, |nnc| nnc.pos())
.reduce(|x, _| x)
});
// Batch trivial chars (i.e. chars 1:1 wrt `BytePos` vs `col_display`).
let following_trivial_chars =
next_special_bpos.unwrap_or(line_bpos_range.end).0 - cur_bpos.0;
if following_trivial_chars > 0 {
let wanted_trivial_chars = following_trivial_chars.min(col - cur_col_display);
cur_bpos.0 += wanted_trivial_chars;
cur_col_display += wanted_trivial_chars;
continue;
// Batch trivial chars (i.e. chars 1:1 wrt `BytePos` vs `col_display`).
let following_trivial_chars =
next_special_bpos.unwrap_or(line_bpos_range.end).0 - cur_bpos.0;
if following_trivial_chars > 0 {
let wanted_trivial_chars = following_trivial_chars.min(col - cur_col_display);
cur_bpos.0 += wanted_trivial_chars;
cur_col_display += wanted_trivial_chars;
continue;
}
// Add a special char's `BytePos` and `col_display` contributions.
let mbc_nnc = special_chars.next().unwrap();
cur_bpos.0 += mbc_nnc.as_ref().left().map_or(1, |mbc| mbc.bytes as u32);
cur_col_display += mbc_nnc.as_ref().right().map_or(1, |nnc| nnc.width() as u32);
}
Some(cur_bpos)
};
// Add a special char's `BytePos` and `col_display` contributions.
let mbc_nnc = special_chars.next().unwrap();
cur_bpos.0 += mbc_nnc.as_ref().left().map_or(1, |mbc| mbc.bytes as u32);
cur_col_display += mbc_nnc.as_ref().right().map_or(1, |nnc| nnc.width() as u32);
}
Some(Span::with_root_ctxt(cur_bpos, cur_bpos))
Some(Span::with_root_ctxt(
line_col_to_bpos(line_start, col_start)?,
line_col_to_bpos(line_end, col_end)?,
))
}
}

View File

@ -109,10 +109,11 @@ macro_rules! def_custom_insts {
}
}
// NOTE(eddyb) several of these are similar to `NonSemantic.Shader.DebugInfo.100`
// instructions, but simpler (to aid implementation, for now).
def_custom_insts! {
// `OpLine` equivalent.
// FIXME(eddyb) improve on this, by adding more information.
0 => SetDebugSrcLoc { file, line, col },
// `OpNoLine` equivalent.
// Like `DebugLine` (from `NonSemantic.Shader.DebugInfo.100`) or `OpLine`.
0 => SetDebugSrcLoc { file, line_start, line_end, col_start, col_end },
// Like `DebugNoLine` (from `NonSemantic.Shader.DebugInfo.100`) or `OpNoLine`.
1 => ClearDebugSrcLoc,
}

View File

@ -86,7 +86,13 @@ impl Transformer for CustomDebuginfoToSpv<'_> {
{
if ext_set == self.custom_ext_inst_set {
match CustomOp::decode(ext_inst).with_operands(&data_inst_def.inputs) {
CustomInst::SetDebugSrcLoc { file, line, col } => {
CustomInst::SetDebugSrcLoc {
file,
line_start: line,
line_end: _,
col_start: col,
col_end: _,
} => {
let const_ctor = |v: Value| match v {
Value::Const(ct) => &self.cx[ct].ctor,
_ => unreachable!(),

View File

@ -213,8 +213,10 @@ impl UseOrigin<'_> {
col,
} => span_regen.src_loc_to_rustc(SrcLocDecoration {
file_name: &cx[file_path.0],
line,
col,
line_start: line,
line_end: line,
col_start: col,
col_end: col,
}),
_ => None,
})
@ -251,10 +253,17 @@ impl UseOrigin<'_> {
}
_ => unreachable!(),
};
let (file, line, col) = match custom_op.with_operands(&debug_inst_def.inputs) {
CustomInst::SetDebugSrcLoc { file, line, col } => (file, line, col),
CustomInst::ClearDebugSrcLoc => unreachable!(),
};
let (file, line_start, line_end, col_start, col_end) =
match custom_op.with_operands(&debug_inst_def.inputs) {
CustomInst::SetDebugSrcLoc {
file,
line_start,
line_end,
col_start,
col_end,
} => (file, line_start, line_end, col_start, col_end),
CustomInst::ClearDebugSrcLoc => unreachable!(),
};
let const_ctor = |v: Value| match v {
Value::Const(ct) => &cx[ct].ctor,
_ => unreachable!(),
@ -276,8 +285,10 @@ impl UseOrigin<'_> {
span_regen.src_loc_to_rustc(SrcLocDecoration {
file_name: &cx[const_str(file)],
line: const_u32(line),
col: const_u32(col),
line_start: const_u32(line_start),
line_end: const_u32(line_end),
col_start: const_u32(col_start),
col_end: const_u32(col_end),
})
})
.or_else(|| from_attrs(func_attrs, span_regen)),

View File

@ -2,28 +2,28 @@ error: cannot memcpy dynamically sized data
--> $CORE_SRC/intrinsics.rs:2460:9
|
2460 | copy(src, dst, count)
| ^
| ^^^^^^^^^^^^^^^^^^^^^
|
note: used from within `core::intrinsics::copy::<f32>`
--> $CORE_SRC/intrinsics.rs:2447:21
|
2447 | pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) {
| ^
| ^^^^
note: called by `ptr_copy::copy_via_raw_ptr`
--> $DIR/ptr_copy.rs:28:18
|
28 | unsafe { core::ptr::copy(src, dst, 1) }
| ^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: called by `ptr_copy::main`
--> $DIR/ptr_copy.rs:33:5
|
33 | copy_via_raw_ptr(&i, o);
| ^
| ^^^^^^^^^^^^^^^^^^^^^^^
note: called by `main`
--> $DIR/ptr_copy.rs:31:1
|
31 | #[spirv(fragment)]
| ^
| ^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -2,35 +2,35 @@ error: constant arrays/structs cannot contain pointers to other constants
--> $DIR/nested-ref-in-composite.rs:20:17
|
20 | *pair_out = pair_deep_load(&(&123, &3.14));
| ^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: used from within `nested_ref_in_composite::main_pair`
--> $DIR/nested-ref-in-composite.rs:20:17
|
20 | *pair_out = pair_deep_load(&(&123, &3.14));
| ^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: called by `main_pair`
--> $DIR/nested-ref-in-composite.rs:18:1
|
18 | #[spirv(fragment)]
| ^
| ^^^^^^^^^^^^^^^^^^
error: constant arrays/structs cannot contain pointers to other constants
--> $DIR/nested-ref-in-composite.rs:25:19
|
25 | *array3_out = array3_deep_load(&[&0, &1, &2]);
| ^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: used from within `nested_ref_in_composite::main_array3`
--> $DIR/nested-ref-in-composite.rs:25:19
|
25 | *array3_out = array3_deep_load(&[&0, &1, &2]);
| ^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: called by `main_array3`
--> $DIR/nested-ref-in-composite.rs:23:1
|
23 | #[spirv(fragment)]
| ^
| ^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View File

@ -4,12 +4,12 @@ note: used from within `allocate_const_scalar::main`
--> $DIR/allocate_const_scalar.rs:15:20
|
15 | let _pointer = POINTER;
| ^
| ^^^^^^^
note: called by `main`
--> $DIR/allocate_const_scalar.rs:13:1
|
13 | #[spirv(fragment)]
| ^
| ^^^^^^^^^^^^^^^^^^
error: aborting due to previous error

View File

@ -4,18 +4,18 @@ error: cannot cast between pointer types
--> $DIR/zst_member_ref_arg-broken.rs:23:5
|
23 | f(&s.y);
| ^
| ^^^^^^^
|
note: used from within `zst_member_ref_arg_broken::main_scalar`
--> $DIR/zst_member_ref_arg-broken.rs:23:5
|
23 | f(&s.y);
| ^
| ^^^^^^^
note: called by `main_scalar`
--> $DIR/zst_member_ref_arg-broken.rs:21:1
|
21 | #[spirv(fragment)]
| ^
| ^^^^^^^^^^^^^^^^^^
error: cannot cast between pointer types
from `*struct S<usize, usize> { u32, u32 }`
@ -23,18 +23,18 @@ error: cannot cast between pointer types
--> $DIR/zst_member_ref_arg-broken.rs:28:5
|
28 | f(&s.y);
| ^
| ^^^^^^^
|
note: used from within `zst_member_ref_arg_broken::main_scalar_pair`
--> $DIR/zst_member_ref_arg-broken.rs:28:5
|
28 | f(&s.y);
| ^
| ^^^^^^^
note: called by `main_scalar_pair`
--> $DIR/zst_member_ref_arg-broken.rs:26:1
|
26 | #[spirv(fragment)]
| ^
| ^^^^^^^^^^^^^^^^^^
error: cannot cast between pointer types
from `*struct (usize, usize) { u32, u32 }`
@ -42,18 +42,18 @@ error: cannot cast between pointer types
--> $DIR/zst_member_ref_arg-broken.rs:33:5
|
33 | f(&s.y);
| ^
| ^^^^^^^
|
note: used from within `zst_member_ref_arg_broken::main_scalar_scalar_pair_nested`
--> $DIR/zst_member_ref_arg-broken.rs:33:5
|
33 | f(&s.y);
| ^
| ^^^^^^^
note: called by `main_scalar_scalar_pair_nested`
--> $DIR/zst_member_ref_arg-broken.rs:31:1
|
31 | #[spirv(fragment)]
| ^
| ^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors