Skip to content

Commit

Permalink
Synthetic type centralization (#9700)
Browse files Browse the repository at this point in the history
  • Loading branch information
SingleAccretion authored Dec 2, 2024
1 parent 100e90b commit af476a5
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 225 deletions.
20 changes: 16 additions & 4 deletions crates/cranelift/src/debug/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use anyhow::Error;
use cranelift_codegen::isa::TargetIsa;
use gimli::{write, Dwarf, DwarfPackage, LittleEndian, Section, Unit, UnitSectionOffset};
use std::{collections::HashSet, fmt::Debug};
use synthetic::ModuleSyntheticUnit;
use thiserror::Error;
use wasmtime_environ::{
DefinedFuncIndex, ModuleTranslation, PrimaryMap, StaticModuleIndex, Tunables,
Expand All @@ -23,6 +24,7 @@ mod line_program;
mod range_info_builder;
mod refs;
mod simulate;
mod synthetic;
mod unit;
mod utils;

Expand Down Expand Up @@ -165,17 +167,16 @@ pub fn transform_dwarf(

let out_encoding = gimli::Encoding {
format: gimli::Format::Dwarf32,
// TODO: this should be configurable
version: 4,
version: 4, // TODO: this should be configurable
address_size: isa.pointer_bytes(),
};

let mut out_strings = write::StringTable::default();
let mut out_units = write::UnitTable::default();

let out_line_strings = write::LineStringTable::default();
let mut pending_di_refs = Vec::new();
let mut di_ref_map = DebugInfoRefsMap::new();
let mut vmctx_ptr_die_refs = PrimaryMap::new();

let mut translated = HashSet::new();

Expand All @@ -187,8 +188,17 @@ pub fn transform_dwarf(
let context = DebugInputContext {
reachable: &reachable,
};
let mut iter = di.dwarf.debug_info.units();
let out_module_synthetic_unit = ModuleSyntheticUnit::new(
module,
compilation,
out_encoding,
&mut out_units,
&mut out_strings,
);
// TODO-LLVM-DI-Cleanup: move the simulation code to be per-module and delete this map.
vmctx_ptr_die_refs.push(out_module_synthetic_unit.vmctx_ptr_die_ref());

let mut iter = di.dwarf.debug_info.units();
while let Some(header) = iter.next().unwrap_or(None) {
let unit = di.dwarf.unit(header)?;

Expand All @@ -215,6 +225,7 @@ pub fn transform_dwarf(
&context,
&addr_tr,
out_encoding,
&out_module_synthetic_unit,
&mut out_units,
&mut out_strings,
&mut translated,
Expand All @@ -232,6 +243,7 @@ pub fn transform_dwarf(
&transforms,
&translated,
out_encoding,
&vmctx_ptr_die_refs,
&mut out_units,
&mut out_strings,
isa,
Expand Down
25 changes: 9 additions & 16 deletions crates/cranelift/src/debug/transform/simulate.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::expression::{CompiledExpression, FunctionFrameInfo};
use super::utils::{add_internal_types, append_vmctx_info};
use super::utils::append_vmctx_info;
use super::AddressTransform;
use crate::debug::{Compilation, ModuleMemoryOffset};
use crate::debug::Compilation;
use crate::translate::get_vmctx_value_label;
use anyhow::{Context, Error};
use cranelift_codegen::isa::TargetIsa;
Expand Down Expand Up @@ -114,7 +114,6 @@ fn autogenerate_dwarf_wasm_path(di: &DebugInfoData) -> PathBuf {
}

struct WasmTypesDieRefs {
vmctx: write::UnitEntryId,
i32: write::UnitEntryId,
i64: write::UnitEntryId,
f32: write::UnitEntryId,
Expand All @@ -125,10 +124,7 @@ fn add_wasm_types(
unit: &mut write::Unit,
root_id: write::UnitEntryId,
out_strings: &mut write::StringTable,
memory_offset: &ModuleMemoryOffset,
) -> WasmTypesDieRefs {
let (_wp_die_id, vmctx_die_id) = add_internal_types(unit, root_id, out_strings, memory_offset);

macro_rules! def_type {
($id:literal, $size:literal, $enc:path) => {{
let die_id = unit.add(root_id, gimli::DW_TAG_base_type);
Expand All @@ -149,7 +145,6 @@ fn add_wasm_types(
let f64_die_id = def_type!("f64", 8, gimli::DW_ATE_float);

WasmTypesDieRefs {
vmctx: vmctx_die_id,
i32: i32_die_id,
i64: i64_die_id,
f32: f32_die_id,
Expand Down Expand Up @@ -196,6 +191,7 @@ fn generate_vars(
addr_tr: &AddressTransform,
frame_info: &FunctionFrameInfo,
scope_ranges: &[(u64, u64)],
vmctx_ptr_die_ref: write::Reference,
wasm_types: &WasmTypesDieRefs,
func_meta: &FunctionMetadata,
locals_names: Option<&HashMap<u32, &str>>,
Expand All @@ -213,7 +209,7 @@ fn generate_vars(
append_vmctx_info(
unit,
die_id,
wasm_types.vmctx,
vmctx_ptr_die_ref,
addr_tr,
Some(frame_info),
scope_ranges,
Expand Down Expand Up @@ -282,11 +278,13 @@ fn check_invalid_chars_in_path(path: PathBuf) -> Option<PathBuf> {
.and_then(move |s| if s.contains('\x00') { None } else { Some(path) })
}

/// Generate "simulated" native DWARF for functions lacking WASM-level DWARF.
pub fn generate_simulated_dwarf(
compilation: &mut Compilation<'_>,
addr_tr: &PrimaryMap<StaticModuleIndex, AddressTransform>,
translated: &HashSet<usize>,
out_encoding: gimli::Encoding,
vmctx_ptr_die_refs: &PrimaryMap<StaticModuleIndex, write::Reference>,
out_units: &mut write::UnitTable,
out_strings: &mut write::StringTable,
isa: &dyn TargetIsa,
Expand Down Expand Up @@ -345,13 +343,7 @@ pub fn generate_simulated_dwarf(
(unit, root_id, file_id)
};

let mut module_wasm_types = PrimaryMap::new();
for (module, memory_offset) in compilation.module_memory_offsets.iter() {
let wasm_types = add_wasm_types(unit, root_id, out_strings, memory_offset);
let i = module_wasm_types.push(wasm_types);
assert_eq!(i, module);
}

let wasm_types = add_wasm_types(unit, root_id, out_strings);
for (module, index) in compilation.indexes().collect::<Vec<_>>() {
let (symbol, _) = compilation.function(module, index);
if translated.contains(&symbol) {
Expand Down Expand Up @@ -411,7 +403,8 @@ pub fn generate_simulated_dwarf(
addr_tr,
&frame_info,
&[(source_range.0, source_range.1)],
&module_wasm_types[module],
vmctx_ptr_die_refs[module],
&wasm_types,
&di.wasm_file.funcs[index.as_u32() as usize],
di.name_section.locals_names.get(&func_index),
out_strings,
Expand Down
179 changes: 179 additions & 0 deletions crates/cranelift/src/debug/transform/synthetic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
use gimli::write::{
AttributeValue, LineProgram, Reference, StringTable, Unit, UnitEntryId, UnitId, UnitTable,
};
use wasmtime_environ::StaticModuleIndex;
use wasmtime_versioned_export_macros::versioned_stringify_ident;

use crate::debug::{Compilation, ModuleMemoryOffset};

/// Internal Wasm utility types DIEs such as WebAssemblyPtr and WasmtimeVMContext.
///
/// For unwrapping Wasm pointer, the WasmtimeVMContext has the `set()` method
/// that allows to control current Wasm memory to inspect.
/// Notice that "set_vmctx_memory" is an external/builtin subprogram that
/// is not part of Wasm code.
///
/// This CU is currently per-module since VMContext memory structure is per-module;
/// some of the contained types could be made global (per-Compilation).
pub struct ModuleSyntheticUnit {
unit_id: UnitId,
vmctx_ptr_die_id: UnitEntryId,
wasm_ptr_die_id: UnitEntryId,
}

macro_rules! add_tag {
($unit:ident, $parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
let $die_id = $unit.add($parent_id, $tag);
let $die = $unit.get_mut($die_id);
$( $die.set($a, $v); )*
};
}

impl ModuleSyntheticUnit {
pub fn new(
module: StaticModuleIndex,
compilation: &Compilation<'_>,
encoding: gimli::Encoding,
out_units: &mut UnitTable,
out_strings: &mut StringTable,
) -> Self {
let unit_id = Self::create_unit(encoding, out_units, out_strings);
let unit = out_units.get_mut(unit_id);
let vmctx_ptr_die_id = Self::create_vmctx_ptr_die(module, compilation, unit, out_strings);
let wasm_ptr_die_id = Self::create_wasm_ptr_die(unit, out_strings);

Self {
unit_id,
vmctx_ptr_die_id,
wasm_ptr_die_id,
}
}

pub fn vmctx_ptr_die_ref(&self) -> Reference {
Reference::Entry(self.unit_id, self.vmctx_ptr_die_id)
}

pub fn wasm_ptr_die_ref(&self) -> Reference {
Reference::Entry(self.unit_id, self.wasm_ptr_die_id)
}

fn create_unit(
encoding: gimli::Encoding,
out_units: &mut UnitTable,
out_strings: &mut StringTable,
) -> UnitId {
let unit_id = out_units.add(Unit::new(encoding, LineProgram::none()));
let unit = out_units.get_mut(unit_id);
let unit_die = unit.get_mut(unit.root());
unit_die.set(
gimli::DW_AT_name,
AttributeValue::StringRef(out_strings.add("WasmtimeModuleSyntheticUnit")),
);
unit_id
}

fn create_vmctx_ptr_die(
module: StaticModuleIndex,
compilation: &Compilation<'_>,
unit: &mut Unit,
out_strings: &mut StringTable,
) -> UnitEntryId {
// Build DW_TAG_base_type for Wasm byte:
// .. DW_AT_name = u8
// .. DW_AT_encoding = DW_ATE_unsigned
// .. DW_AT_byte_size = 1
let root_id = unit.root();
add_tag!(unit, root_id, gimli::DW_TAG_base_type => memory_byte_die as memory_byte_die_id {
gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("u8")),
gimli::DW_AT_encoding = AttributeValue::Encoding(gimli::DW_ATE_unsigned),
gimli::DW_AT_byte_size = AttributeValue::Data1(1)
});

// Build DW_TAG_pointer_type that references Wasm bytes:
// .. DW_AT_name = "u8*"
// .. DW_AT_type = <memory_byte_die>
add_tag!(unit, root_id, gimli::DW_TAG_pointer_type => memory_bytes_die as memory_bytes_die_id {
gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("u8*")),
gimli::DW_AT_type = AttributeValue::UnitRef(memory_byte_die_id)
});

// Create artificial VMContext type and its reference for convenience viewing
// its fields (such as memory ref) in a debugger. Build DW_TAG_structure_type:
// .. DW_AT_name = "WasmtimeVMContext"
let vmctx_die_id = unit.add(root_id, gimli::DW_TAG_structure_type);
let vmctx_die = unit.get_mut(vmctx_die_id);
vmctx_die.set(
gimli::DW_AT_name,
AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
);

// TODO multiple memories
match compilation.module_memory_offsets[module] {
ModuleMemoryOffset::Defined(memory_offset) => {
// The context has defined memory: extend the WasmtimeVMContext size
// past the "memory" field.
const MEMORY_FIELD_SIZE_PLUS_PADDING: u32 = 8;
vmctx_die.set(
gimli::DW_AT_byte_size,
AttributeValue::Data4(memory_offset + MEMORY_FIELD_SIZE_PLUS_PADDING),
);

// Define the "memory" field which is a direct pointer to allocated Wasm memory.
// Build DW_TAG_member:
// .. DW_AT_name = "memory"
// .. DW_AT_type = <memory_bytes_die>
// .. DW_AT_data_member_location = `memory_offset`
add_tag!(unit, vmctx_die_id, gimli::DW_TAG_member => m_die as m_die_id {
gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("memory")),
gimli::DW_AT_type = AttributeValue::UnitRef(memory_bytes_die_id),
gimli::DW_AT_data_member_location = AttributeValue::Udata(memory_offset as u64)
});
}
ModuleMemoryOffset::Imported { .. } => {
// TODO implement convenience pointer to and additional types for VMMemoryImport.
}
ModuleMemoryOffset::None => (),
}

// Build DW_TAG_pointer_type for `WasmtimeVMContext*`:
// .. DW_AT_name = "WasmtimeVMContext*"
// .. DW_AT_type = <vmctx_die>
add_tag!(unit, root_id, gimli::DW_TAG_pointer_type => vmctx_ptr_die as vmctx_ptr_die_id {
gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")),
gimli::DW_AT_type = AttributeValue::UnitRef(vmctx_die_id)
});

// Build vmctx_die's DW_TAG_subprogram for `set` method:
// .. DW_AT_linkage_name = "set_vmctx_memory"
// .. DW_AT_name = "set"
// .. DW_TAG_formal_parameter
// .. .. DW_AT_type = <vmctx_ptr_die>
// .. .. DW_AT_artificial = 1
add_tag!(unit, vmctx_die_id, gimli::DW_TAG_subprogram => vmctx_set as vmctx_set_id {
gimli::DW_AT_linkage_name = AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(set_vmctx_memory))),
gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("set"))
});
add_tag!(unit, vmctx_set_id, gimli::DW_TAG_formal_parameter => vmctx_set_this_param as vmctx_set_this_param_id {
gimli::DW_AT_type = AttributeValue::UnitRef(vmctx_ptr_die_id),
gimli::DW_AT_artificial = AttributeValue::Flag(true)
});

vmctx_ptr_die_id
}

fn create_wasm_ptr_die(unit: &mut Unit, out_strings: &mut StringTable) -> UnitEntryId {
// Build DW_TAG_base_type for generic `WebAssemblyPtr`.
// .. DW_AT_name = "WebAssemblyPtr"
// .. DW_AT_byte_size = 4
// .. DW_AT_encoding = DW_ATE_unsigned
const WASM_PTR_LEN: u8 = 4;
let root_id = unit.root();
add_tag!(unit, root_id, gimli::DW_TAG_base_type => wp_die as wp_die_id {
gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")),
gimli::DW_AT_byte_size = AttributeValue::Data1(WASM_PTR_LEN),
gimli::DW_AT_encoding = AttributeValue::Encoding(gimli::DW_ATE_unsigned)
});

wp_die_id
}
}
Loading

0 comments on commit af476a5

Please sign in to comment.