mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-25 00:04:11 +00:00
decorations: allow zero-copy deserialization of strings.
This commit is contained in:
parent
b3067494e8
commit
e7921fbf20
@ -656,13 +656,8 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
|
||||
|
||||
fn set_span(&mut self, span: Span) {
|
||||
self.current_span = Some(span);
|
||||
let loc = self.cx.tcx.sess.source_map().lookup_char_pos(span.lo());
|
||||
let file = self.builder.def_debug_file(loc.file);
|
||||
self.emit().line(
|
||||
file.file_name_op_string_id,
|
||||
loc.line as u32,
|
||||
loc.col_display as u32,
|
||||
);
|
||||
let (file, line, col) = self.builder.file_line_col_for_op_line(span);
|
||||
self.emit().line(file.file_name_op_string_id, line, col);
|
||||
}
|
||||
|
||||
// FIXME(eddyb) change `Self::Function` to be more like a function index.
|
||||
|
@ -9,16 +9,19 @@ use rspirv::spirv::{
|
||||
AddressingModel, Capability, MemoryModel, Op, SourceLanguage, StorageClass, Word,
|
||||
};
|
||||
use rspirv::{binary::Assemble, binary::Disassemble};
|
||||
use rustc_arena::DroplessArena;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{SourceFile, Span, DUMMY_SP};
|
||||
use rustc_span::{FileName, FileNameDisplayPreference, SourceFile, Span, DUMMY_SP};
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter;
|
||||
use std::str;
|
||||
use std::{fs::File, io::Write, path::Path};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
@ -354,9 +357,11 @@ impl Hash for DebugFileKey {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DebugFileSpirv {
|
||||
pub struct DebugFileSpirv<'tcx> {
|
||||
pub file_name: &'tcx str,
|
||||
|
||||
/// The SPIR-V ID for the result of the `OpString` instruction containing
|
||||
/// the file name - this is what e.g. `OpLine` uses to identify the file.
|
||||
/// `file_name` - this is what e.g. `OpLine` uses to identify the file.
|
||||
///
|
||||
/// All other details about the file are also attached to this ID, using
|
||||
/// other instructions that don't produce their own IDs (e.g. `OpSource`).
|
||||
@ -395,6 +400,7 @@ pub struct BuilderCursor {
|
||||
pub struct BuilderSpirv<'tcx> {
|
||||
// HACK(eddyb) public only for `decorations`.
|
||||
pub(crate) source_map: &'tcx SourceMap,
|
||||
dropless_arena: &'tcx DroplessArena,
|
||||
|
||||
builder: RefCell<Builder>,
|
||||
|
||||
@ -405,7 +411,7 @@ pub struct BuilderSpirv<'tcx> {
|
||||
const_to_id: RefCell<FxHashMap<WithType<SpirvConst<'tcx>>, WithConstLegality<Word>>>,
|
||||
id_to_const: RefCell<FxHashMap<Word, WithConstLegality<SpirvConst<'tcx>>>>,
|
||||
|
||||
debug_file_cache: RefCell<FxHashMap<DebugFileKey, DebugFileSpirv>>,
|
||||
debug_file_cache: RefCell<FxHashMap<DebugFileKey, DebugFileSpirv<'tcx>>>,
|
||||
|
||||
enabled_capabilities: FxHashSet<Capability>,
|
||||
enabled_extensions: FxHashSet<Symbol>,
|
||||
@ -413,7 +419,7 @@ pub struct BuilderSpirv<'tcx> {
|
||||
|
||||
impl<'tcx> BuilderSpirv<'tcx> {
|
||||
pub fn new(
|
||||
source_map: &'tcx SourceMap,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
sym: &Symbols,
|
||||
target: &SpirvTarget,
|
||||
features: &[TargetFeature],
|
||||
@ -478,7 +484,8 @@ impl<'tcx> BuilderSpirv<'tcx> {
|
||||
builder.memory_model(AddressingModel::Logical, memory_model);
|
||||
|
||||
Self {
|
||||
source_map,
|
||||
source_map: tcx.sess.source_map(),
|
||||
dropless_arena: &tcx.arena.dropless,
|
||||
builder: RefCell::new(builder),
|
||||
const_to_id: Default::default(),
|
||||
id_to_const: Default::default(),
|
||||
@ -689,7 +696,17 @@ impl<'tcx> BuilderSpirv<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn def_debug_file(&self, sf: Lrc<SourceFile>) -> DebugFileSpirv {
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
||||
// HACK(eddyb) public only for `decorations`.
|
||||
pub(crate) fn def_debug_file(&self, sf: Lrc<SourceFile>) -> DebugFileSpirv<'tcx> {
|
||||
*self
|
||||
.debug_file_cache
|
||||
.borrow_mut()
|
||||
@ -697,7 +714,34 @@ impl<'tcx> BuilderSpirv<'tcx> {
|
||||
.or_insert_with_key(|DebugFileKey(sf)| {
|
||||
let mut builder = self.builder(Default::default());
|
||||
|
||||
let file_name_op_string_id = builder.string(sf.name.prefer_remapped().to_string());
|
||||
// FIXME(eddyb) it would be nicer if we could just rely on
|
||||
// `RealFileName::to_string_lossy` returning `Cow<'_, str>`,
|
||||
// but sadly that `'_` is the lifetime of the temporary `Lrc`,
|
||||
// not `'tcx`, so we have to arena-allocate to get `&'tcx str`.
|
||||
let file_name = match &sf.name {
|
||||
FileName::Real(name) => {
|
||||
name.to_string_lossy(FileNameDisplayPreference::Remapped)
|
||||
}
|
||||
_ => sf.name.prefer_remapped().to_string().into(),
|
||||
};
|
||||
let file_name = {
|
||||
// FIXME(eddyb) it should be possible to arena-allocate a
|
||||
// `&str` directly, but it would require upstream changes,
|
||||
// and strings are handled by string interning in `rustc`.
|
||||
fn arena_alloc_slice<'tcx, T: Copy>(
|
||||
dropless_arena: &'tcx DroplessArena,
|
||||
xs: &[T],
|
||||
) -> &'tcx [T] {
|
||||
if xs.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
dropless_arena.alloc_slice(xs)
|
||||
}
|
||||
}
|
||||
str::from_utf8(arena_alloc_slice(self.dropless_arena, file_name.as_bytes()))
|
||||
.unwrap()
|
||||
};
|
||||
let file_name_op_string_id = builder.string(file_name.to_owned());
|
||||
|
||||
let file_contents = self
|
||||
.source_map
|
||||
@ -752,6 +796,7 @@ impl<'tcx> BuilderSpirv<'tcx> {
|
||||
}
|
||||
|
||||
DebugFileSpirv {
|
||||
file_name,
|
||||
file_name_op_string_id,
|
||||
}
|
||||
})
|
||||
|
@ -53,7 +53,7 @@ pub struct CodegenCx<'tcx> {
|
||||
/// Invalid spir-v IDs that should be stripped from the final binary,
|
||||
/// each with its own reason and span that should be used for reporting
|
||||
/// (in the event that the value is actually needed)
|
||||
zombie_decorations: RefCell<FxHashMap<Word, ZombieDecoration>>,
|
||||
zombie_decorations: RefCell<FxHashMap<Word, ZombieDecoration<'tcx>>>,
|
||||
/// Cache of all the builtin symbols we need
|
||||
pub sym: Rc<Symbols>,
|
||||
pub instruction_table: InstructionTable,
|
||||
@ -117,7 +117,7 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
Self {
|
||||
tcx,
|
||||
codegen_unit,
|
||||
builder: BuilderSpirv::new(tcx.sess.source_map(), &sym, &target, &features),
|
||||
builder: BuilderSpirv::new(tcx, &sym, &target, &features),
|
||||
instances: Default::default(),
|
||||
function_parameter_values: Default::default(),
|
||||
type_cache: Default::default(),
|
||||
@ -188,7 +188,9 @@ impl<'tcx> CodegenCx<'tcx> {
|
||||
self.zombie_decorations.borrow_mut().insert(
|
||||
word,
|
||||
ZombieDecoration {
|
||||
reason: reason.to_string(),
|
||||
// FIXME(eddyb) this could take advantage of `Cow` and use
|
||||
// either `&'static str` or `String`, on a case-by-case basis.
|
||||
reason: reason.to_string().into(),
|
||||
span: SerializedSpan::from_rustc(span, &self.builder),
|
||||
},
|
||||
);
|
||||
|
@ -108,8 +108,11 @@ impl<D> Clone for LazilyDeserialized<'_, D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: for<'a> Deserialize<'a>> LazilyDeserialized<'_, D> {
|
||||
pub fn deserialize(&self) -> D {
|
||||
impl<D> LazilyDeserialized<'_, D> {
|
||||
pub fn deserialize<'a>(&'a self) -> D
|
||||
where
|
||||
D: Deserialize<'a>,
|
||||
{
|
||||
serde_json::from_str(&self.json).unwrap()
|
||||
}
|
||||
|
||||
@ -123,29 +126,29 @@ impl<D: for<'a> Deserialize<'a>> LazilyDeserialized<'_, D> {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct ZombieDecoration {
|
||||
pub reason: String,
|
||||
pub struct ZombieDecoration<'a> {
|
||||
pub reason: Cow<'a, str>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub span: Option<SerializedSpan>,
|
||||
pub span: Option<SerializedSpan<'a>>,
|
||||
}
|
||||
|
||||
impl CustomDecoration for ZombieDecoration {
|
||||
impl CustomDecoration for ZombieDecoration<'_> {
|
||||
const ENCODING_PREFIX: &'static str = "Z";
|
||||
}
|
||||
|
||||
/// Representation of a `rustc` `Span` that can be turned into a `Span` again
|
||||
/// in another compilation, by regenerating the `rustc` `SourceFile`.
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct SerializedSpan {
|
||||
file_name: String,
|
||||
pub struct SerializedSpan<'a> {
|
||||
file_name: Cow<'a, str>,
|
||||
// NOTE(eddyb) by keeping `lo` but not `hi`, we mimick `OpLine` limitations
|
||||
// (which could be lifted in the future using custom SPIR-T debuginfo).
|
||||
lo: u32,
|
||||
}
|
||||
|
||||
impl SerializedSpan {
|
||||
pub fn from_rustc(span: Span, builder: &BuilderSpirv<'_>) -> Option<Self> {
|
||||
impl<'tcx> SerializedSpan<'tcx> {
|
||||
pub fn from_rustc(span: Span, builder: &BuilderSpirv<'tcx>) -> Option<Self> {
|
||||
// Decorations may not always have valid spans.
|
||||
// FIXME(eddyb) reduce the sources of this as much as possible.
|
||||
if span.is_dummy() {
|
||||
@ -154,18 +157,18 @@ impl SerializedSpan {
|
||||
|
||||
let lo = span.lo();
|
||||
|
||||
let file = builder.source_map.lookup_source_file(lo);
|
||||
if !(file.start_pos..=file.end_pos).contains(&lo) {
|
||||
let sf = builder.source_map.lookup_source_file(lo);
|
||||
if !(sf.start_pos..=sf.end_pos).contains(&lo) {
|
||||
// FIXME(eddyb) broken `Span` - potentially turn this into an assert?
|
||||
return None;
|
||||
}
|
||||
|
||||
// NOTE(eddyb) this emits necessary `OpString`/`OpSource` instructions.
|
||||
builder.def_debug_file(file.clone());
|
||||
let file = builder.def_debug_file(sf.clone());
|
||||
|
||||
Some(Self {
|
||||
file_name: file.name.prefer_remapped().to_string(),
|
||||
lo: (lo - file.start_pos).to_u32(),
|
||||
file_name: file.file_name.into(),
|
||||
lo: (lo - sf.start_pos).to_u32(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -287,7 +290,7 @@ impl<'a> SpanRegenerator<'a> {
|
||||
file.as_deref()
|
||||
}
|
||||
|
||||
pub fn serialized_span_to_rustc(&mut self, span: &SerializedSpan) -> Option<Span> {
|
||||
pub fn serialized_span_to_rustc(&mut self, span: &SerializedSpan<'_>) -> Option<Span> {
|
||||
let file = self.regenerate_rustc_source_file(&span.file_name[..])?;
|
||||
|
||||
// Sanity check - assuming `SerializedSpan` isn't corrupted, this assert
|
||||
|
@ -40,6 +40,7 @@ compile_error!(
|
||||
);
|
||||
|
||||
extern crate rustc_apfloat;
|
||||
extern crate rustc_arena;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_attr;
|
||||
extern crate rustc_codegen_ssa;
|
||||
|
@ -12,7 +12,7 @@ use std::iter::once;
|
||||
// FIXME(eddyb) change this to chain through IDs instead of wasting allocations.
|
||||
#[derive(Clone)]
|
||||
struct ZombieInfo<'a> {
|
||||
serialized: &'a LazilyDeserialized<'static, ZombieDecoration>,
|
||||
serialized: &'a LazilyDeserialized<'static, ZombieDecoration<'a>>,
|
||||
stack: Vec<Word>,
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user