Emit OpSource for every source file referenced via OpLine.

This commit is contained in:
Eduard-Mihai Burtescu 2023-04-17 09:29:01 +03:00 committed by Eduard-Mihai Burtescu
parent b560a21453
commit 3fb4f9176a
14 changed files with 145 additions and 20 deletions

View File

@ -657,11 +657,12 @@ 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_string(format!("{}", loc.file.name.prefer_remapped()));
self.emit()
.line(file, loc.line as u32, loc.col_display as u32);
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,
);
}
// FIXME(eddyb) change `Self::Function` to be more like a function index.

View File

@ -5,14 +5,20 @@ use crate::symbols::Symbols;
use crate::target::SpirvTarget;
use crate::target_feature::TargetFeature;
use rspirv::dr::{Block, Builder, Module, Operand};
use rspirv::spirv::{AddressingModel, Capability, MemoryModel, Op, StorageClass, Word};
use rspirv::spirv::{
AddressingModel, Capability, MemoryModel, Op, SourceLanguage, StorageClass, Word,
};
use rspirv::{binary::Assemble, binary::Disassemble};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lrc;
use rustc_middle::bug;
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use rustc_span::{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::{fs::File, io::Write, path::Path};
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
@ -321,6 +327,42 @@ struct WithConstLegality<V> {
legal: Result<(), IllegalConst>,
}
/// `HashMap` key type (for `debug_file_cache` in `BuilderSpirv`), which is
/// equivalent to a whole `rustc` `SourceFile`, but has O(1) `Eq` and `Hash`
/// implementations (i.e. not involving the path or the contents of the file).
///
/// This is possible because we can compare `Lrc<SourceFile>`s by equality, as
/// `rustc`'s `SourceMap` already ensures that only one `SourceFile` will be
/// allocated for some given file. For hashing, we could hash the address, or
///
struct DebugFileKey(Lrc<SourceFile>);
impl PartialEq for DebugFileKey {
fn eq(&self, other: &Self) -> bool {
let (Self(self_sf), Self(other_sf)) = (self, other);
Lrc::ptr_eq(self_sf, other_sf)
}
}
impl Eq for DebugFileKey {}
impl Hash for DebugFileKey {
fn hash<H: Hasher>(&self, state: &mut H) {
let Self(sf) = self;
sf.name_hash.hash(state);
sf.src_hash.hash(state);
}
}
#[derive(Copy, Clone)]
pub struct DebugFileSpirv {
/// 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.
///
/// 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`).
pub file_name_op_string_id: Word,
}
/// Cursor system:
///
/// The LLVM module builder model (and therefore `codegen_ssa`) assumes that there is a central
@ -351,6 +393,8 @@ pub struct BuilderCursor {
}
pub struct BuilderSpirv<'tcx> {
source_map: &'tcx SourceMap,
builder: RefCell<Builder>,
// Bidirectional maps between `SpirvConst` and the ID of the defined global
@ -359,14 +403,20 @@ pub struct BuilderSpirv<'tcx> {
// allows getting that legality information without additional lookups.
const_to_id: RefCell<FxHashMap<WithType<SpirvConst<'tcx>>, WithConstLegality<Word>>>,
id_to_const: RefCell<FxHashMap<Word, WithConstLegality<SpirvConst<'tcx>>>>,
string_cache: RefCell<FxHashMap<String, Word>>,
debug_file_cache: RefCell<FxHashMap<DebugFileKey, DebugFileSpirv>>,
enabled_capabilities: FxHashSet<Capability>,
enabled_extensions: FxHashSet<Symbol>,
}
impl<'tcx> BuilderSpirv<'tcx> {
pub fn new(sym: &Symbols, target: &SpirvTarget, features: &[TargetFeature]) -> Self {
pub fn new(
source_map: &'tcx SourceMap,
sym: &Symbols,
target: &SpirvTarget,
features: &[TargetFeature],
) -> Self {
let version = target.spirv_version();
let memory_model = target.memory_model();
@ -427,10 +477,11 @@ impl<'tcx> BuilderSpirv<'tcx> {
builder.memory_model(AddressingModel::Logical, memory_model);
Self {
source_map,
builder: RefCell::new(builder),
const_to_id: Default::default(),
id_to_const: Default::default(),
string_cache: Default::default(),
debug_file_cache: Default::default(),
enabled_capabilities,
enabled_extensions,
}
@ -637,15 +688,75 @@ impl<'tcx> BuilderSpirv<'tcx> {
}
}
pub fn def_string(&self, s: String) -> Word {
use std::collections::hash_map::Entry;
match self.string_cache.borrow_mut().entry(s) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let key = entry.key().clone();
*entry.insert(self.builder(Default::default()).string(key))
}
}
pub fn def_debug_file(&self, sf: Lrc<SourceFile>) -> DebugFileSpirv {
*self
.debug_file_cache
.borrow_mut()
.entry(DebugFileKey(sf))
.or_insert_with_key(|DebugFileKey(sf)| {
let mut builder = self.builder(Default::default());
// FIXME(eddyb) remapping might be really bad for being able to
// load the sources the later, maybe it should be done at the
// very end? (just before linking outputting the final SPIR-V)
let file_name_op_string_id = builder.string(sf.name.prefer_remapped().to_string());
let file_contents = self
.source_map
.span_to_snippet(Span::with_root_ctxt(sf.start_pos, sf.end_pos))
.ok();
// HACK(eddyb) this logic is duplicated from `spirt::spv::lift`.
let op_source_and_continued_chunks = file_contents.as_ref().map(|contents| {
// The maximum word count is `2**16 - 1`, the first word is
// taken up by the opcode & word count, and one extra byte is
// taken up by the nil byte at the end of the LiteralString.
const MAX_OP_SOURCE_CONT_CONTENTS_LEN: usize = (0xffff - 1) * 4 - 1;
// `OpSource` has 3 more operands than `OpSourceContinued`,
// and each of them take up exactly one word.
const MAX_OP_SOURCE_CONTENTS_LEN: usize =
MAX_OP_SOURCE_CONT_CONTENTS_LEN - 3 * 4;
let (op_source_str, mut all_op_source_continued_str) =
contents.split_at(contents.len().min(MAX_OP_SOURCE_CONTENTS_LEN));
// FIXME(eddyb) `spirt::spv::lift` should use this.
let all_op_source_continued_str_chunks = iter::from_fn(move || {
let contents_rest = &mut all_op_source_continued_str;
if contents_rest.is_empty() {
return None;
}
// FIXME(eddyb) test with UTF-8! this `split_at` should
// actually take *less* than the full possible size, to
// avoid cutting a UTF-8 sequence.
let (cont_chunk, rest) = contents_rest
.split_at(contents_rest.len().min(MAX_OP_SOURCE_CONT_CONTENTS_LEN));
*contents_rest = rest;
Some(cont_chunk)
});
(op_source_str, all_op_source_continued_str_chunks)
});
if let Some((op_source_str, all_op_source_continued_str_chunks)) =
op_source_and_continued_chunks
{
builder.source(
SourceLanguage::Unknown,
0,
Some(file_name_op_string_id),
Some(op_source_str),
);
for cont_chunk in all_op_source_continued_str_chunks {
builder.source_continued(cont_chunk);
}
}
DebugFileSpirv {
file_name_op_string_id,
}
})
}
pub fn set_global_initializer(&self, global: Word, initializer: Word) {

View File

@ -117,7 +117,7 @@ impl<'tcx> CodegenCx<'tcx> {
Self {
tcx,
codegen_unit,
builder: BuilderSpirv::new(&sym, &target, &features),
builder: BuilderSpirv::new(tcx.sess.source_map(), &sym, &target, &features),
instances: Default::default(),
function_parameter_values: Default::default(),
type_cache: Default::default(),

View File

@ -5,6 +5,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::bug;
use std::collections::hash_map;
// FIXME(eddyb) consider deduplicating the `OpString` and `OpSource` created for
// file-level debuginfo (but using SPIR-T for linking might be better?).
pub fn remove_duplicate_extensions(module: &mut Module) {
let mut set = FxHashSet::default();

View File

@ -6,6 +6,7 @@
// compile-flags: -C target-feature=+RuntimeDescriptorArray,+ext:SPV_EXT_descriptor_indexing
// compile-flags: -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"

View File

@ -6,6 +6,7 @@
// compile-flags: -C target-feature=+RuntimeDescriptorArray,+ext:SPV_EXT_descriptor_indexing
// compile-flags: -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"

View File

@ -5,6 +5,7 @@
// build-pass
// compile-flags: -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"

View File

@ -5,6 +5,7 @@
// build-pass
// compile-flags: -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"

View File

@ -7,6 +7,7 @@
// build-pass
// compile-flags: -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
#![feature(adt_const_params)]

View File

@ -7,6 +7,7 @@
// build-pass
// compile-flags: -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
#![feature(adt_const_params)]

View File

@ -17,6 +17,7 @@
// build-pass
// compile-flags: -C debuginfo=0 -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"

View File

@ -17,6 +17,7 @@
// build-pass
// compile-flags: -C debuginfo=0 -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"

View File

@ -7,6 +7,7 @@
// build-pass
// compile-flags: -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"

View File

@ -7,6 +7,7 @@
// build-pass
// compile-flags: -C llvm-args=--disassemble-globals
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
// normalize-stderr-test "OpSource .*\n" -> ""
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"