6 use cranelift_codegen::ir::{StackSlots, ValueLabel, ValueLoc};
7 use cranelift_codegen::isa::{RegUnit, TargetIsa};
8 use cranelift_codegen::ValueLocRange;
11 self, Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location,
12 LocationList, Range, RangeList, UnitEntryId, Writer,
14 use gimli::{Encoding, Format, LineEncoding, Register, RunTimeEndian, X86_64};
16 pub use emit::{DebugReloc, DebugRelocName};
18 fn target_endian(tcx: TyCtxt) -> RunTimeEndian {
19 use rustc::ty::layout::Endian;
21 match tcx.data_layout.endian {
22 Endian::Big => RunTimeEndian::Big,
23 Endian::Little => RunTimeEndian::Little,
27 pub struct DebugContext<'tcx> {
30 endian: RunTimeEndian,
31 symbols: indexmap::IndexMap<FuncId, String>,
34 unit_range_list: RangeList,
36 types: HashMap<Ty<'tcx>, UnitEntryId>,
39 impl<'tcx> DebugContext<'tcx> {
40 pub fn new(tcx: TyCtxt<'tcx>, address_size: u8) -> Self {
41 let encoding = Encoding {
42 format: Format::Dwarf32,
43 // TODO: this should be configurable
44 // macOS doesn't seem to support DWARF > 3
49 let mut dwarf = DwarfUnit::new(encoding);
51 // FIXME: how to get version when building out of tree?
52 // Normally this would use option_env!("CFG_VERSION").
53 let producer = format!("cranelift fn (rustc version {})", "unknown version");
54 let comp_dir = tcx.sess.working_dir.0.to_string_lossy().into_owned();
55 let name = match tcx.sess.local_crate_source_file {
56 Some(ref path) => path.to_string_lossy().into_owned(),
57 None => tcx.crate_name(LOCAL_CRATE).to_string(),
60 let line_program = LineProgram::new(
62 LineEncoding::default(),
63 LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings),
64 LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings),
67 dwarf.unit.line_program = line_program;
70 let name = dwarf.strings.add(name);
71 let comp_dir = dwarf.strings.add(comp_dir);
73 let root = dwarf.unit.root();
74 let root = dwarf.unit.get_mut(root);
76 gimli::DW_AT_producer,
77 AttributeValue::StringRef(dwarf.strings.add(producer)),
80 gimli::DW_AT_language,
81 AttributeValue::Language(gimli::DW_LANG_Rust),
83 root.set(gimli::DW_AT_name, AttributeValue::StringRef(name));
84 root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
87 AttributeValue::Address(Address::Constant(0)),
94 endian: target_endian(tcx),
95 symbols: indexmap::IndexMap::new(),
98 unit_range_list: RangeList(Vec::new()),
100 types: HashMap::new(),
104 fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
105 if let Some(type_id) = self.types.get(ty) {
109 let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag);
111 let primitive = |dwarf: &mut DwarfUnit, ate| {
112 let type_id = new_entry(dwarf, gimli::DW_TAG_base_type);
113 let type_entry = dwarf.unit.get_mut(type_id);
114 type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate));
118 let name = format!("{}", ty);
119 let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();
121 let type_id = match ty.kind {
122 ty::Bool => primitive(&mut self.dwarf, gimli::DW_ATE_boolean),
123 ty::Char => primitive(&mut self.dwarf, gimli::DW_ATE_UTF),
124 ty::Uint(_) => primitive(&mut self.dwarf, gimli::DW_ATE_unsigned),
125 ty::Int(_) => primitive(&mut self.dwarf, gimli::DW_ATE_signed),
126 ty::Float(_) => primitive(&mut self.dwarf, gimli::DW_ATE_float),
127 ty::Ref(_, pointee_ty, _mutbl)
128 | ty::RawPtr(ty::TypeAndMut {
132 let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type);
134 // Ensure that type is inserted before recursing to avoid duplicates
135 self.types.insert(ty, type_id);
137 let pointee = self.dwarf_ty(pointee_ty);
139 let type_entry = self.dwarf.unit.get_mut(type_id);
141 //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc_hir::Mutability::Mut));
142 type_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(pointee));
146 ty::Adt(adt_def, _substs) if adt_def.is_struct() && !layout.is_unsized() => {
147 let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type);
149 // Ensure that type is inserted before recursing to avoid duplicates
150 self.types.insert(ty, type_id);
152 let variant = adt_def.non_enum_variant();
154 for (field_idx, field_def) in variant.fields.iter().enumerate() {
155 let field_offset = layout.fields.offset(field_idx);
156 let field_layout = layout.field(&layout::LayoutCx {
158 param_env: ParamEnv::reveal_all(),
159 }, field_idx).unwrap();
161 let field_type = self.dwarf_ty(field_layout.ty);
163 let field_id = self.dwarf.unit.add(type_id, gimli::DW_TAG_member);
164 let field_entry = self.dwarf.unit.get_mut(field_id);
166 field_entry.set(gimli::DW_AT_name, AttributeValue::String(field_def.ident.as_str().to_string().into_bytes()));
167 field_entry.set(gimli::DW_AT_data_member_location, AttributeValue::Udata(field_offset.bytes()));
168 field_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(field_type));
173 _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type),
176 let type_entry = self.dwarf.unit.get_mut(type_id);
178 type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
180 gimli::DW_AT_byte_size,
181 AttributeValue::Udata(layout.size.bytes()),
184 self.types.insert(ty, type_id);
190 pub struct FunctionDebugContext<'a, 'tcx> {
191 debug_context: &'a mut DebugContext<'tcx>,
192 entry_id: UnitEntryId,
194 instance: Instance<'tcx>,
195 mir: &'tcx mir::Body<'tcx>,
198 impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
200 debug_context: &'a mut DebugContext<'tcx>,
201 instance: Instance<'tcx>,
205 let mir = *debug_context.tcx.instance_mir(instance.def);
207 let (symbol, _) = debug_context.symbols.insert_full(func_id, name.to_string());
209 // FIXME: add to appropriate scope intead of root
210 let scope = debug_context.dwarf.unit.root();
212 let entry_id = debug_context
215 .add(scope, gimli::DW_TAG_subprogram);
216 let entry = debug_context.dwarf.unit.get_mut(entry_id);
217 let name_id = debug_context.dwarf.strings.add(name);
219 gimli::DW_AT_linkage_name,
220 AttributeValue::StringRef(name_id),
223 FunctionDebugContext {
232 fn define_local(&mut self, name: String, ty: Ty<'tcx>) -> UnitEntryId {
233 let ty = self.debug_context.tcx.subst_and_normalize_erasing_regions(
234 self.instance.substs,
235 ty::ParamEnv::reveal_all(),
238 let dw_ty = self.debug_context.dwarf_ty(ty);
244 .add(self.entry_id, gimli::DW_TAG_variable);
245 let var_entry = self.debug_context.dwarf.unit.get_mut(var_id);
247 var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
248 var_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(dw_ty));
257 source_info_set: &indexmap::IndexSet<SourceInfo>,
258 local_map: HashMap<mir::Local, CPlace<'tcx>>,
260 let end = self.create_debug_lines(context, isa, source_info_set);
265 .push(Range::StartLength {
266 begin: Address::Symbol {
273 // FIXME make it more reliable and implement scopes before re-enabling this.
275 let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap();
277 for (local, _local_decl) in self.mir.local_decls.iter_enumerated() {
278 let var_id = self.define_local(format!("{:?}", local), &self.mir.local_decls[local].ty);
280 let location = place_location(
285 &value_labels_ranges,
288 projection: ty::List::empty(),
292 let var_entry = self.debug_context.dwarf.unit.get_mut(var_id);
293 var_entry.set(gimli::DW_AT_location, location);
297 // FIXME create locals for all entries in mir.var_debug_info
301 fn place_location<'a, 'tcx>(
302 func_debug_ctx: &mut FunctionDebugContext<'a, 'tcx>,
305 local_map: &HashMap<mir::Local, CPlace<'tcx>>,
306 value_labels_ranges: &HashMap<ValueLabel, Vec<ValueLocRange>>,
308 ) -> AttributeValue {
309 assert!(place.projection.is_empty()); // FIXME implement them
311 match local_map[&place.local].inner() {
312 CPlaceInner::Var(local) => {
313 let value_label = cranelift_codegen::ir::ValueLabel::from_u32(local.as_u32());
314 if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) {
315 let loc_list = LocationList(
318 .map(|value_loc_range| Location::StartEnd {
319 begin: Address::Symbol {
320 symbol: func_debug_ctx.symbol,
321 addend: i64::from(value_loc_range.start),
323 end: Address::Symbol {
324 symbol: func_debug_ctx.symbol,
325 addend: i64::from(value_loc_range.end),
328 translate_loc(isa, value_loc_range.loc, &context.func.stack_slots).unwrap(),
333 let loc_list_id = func_debug_ctx.debug_context.dwarf.unit.locations.add(loc_list);
335 AttributeValue::LocationListRef(loc_list_id)
337 // FIXME set value labels for unused locals
339 AttributeValue::Exprloc(Expression(vec![]))
342 CPlaceInner::Addr(_, _) => {
343 // FIXME implement this (used by arguments and returns)
345 AttributeValue::Exprloc(Expression(vec![]))
347 // For PointerBase::Stack:
348 //AttributeValue::Exprloc(Expression(translate_loc(ValueLoc::Stack(*stack_slot), &context.func.stack_slots).unwrap()))
350 CPlaceInner::NoPlace => AttributeValue::Exprloc(Expression(vec![])),
358 // Adapted from https://github.com/bytecodealliance/wasmtime/blob/50496efb6bac32aaf469c6d9186b322de83549bf/crates/debug/src/transform/map_reg.rs
359 pub(crate) fn map_reg(isa: &dyn TargetIsa, reg: RegUnit) -> Register {
360 // TODO avoid duplication with fde.rs
361 assert!(isa.name() == "x86" && isa.pointer_bits() == 64);
362 // Mapping from https://github.com/bytecodealliance/cranelift/pull/902 by @iximeow
363 const X86_GP_REG_MAP: [Register; 16] = [
381 const X86_XMM_REG_MAP: [Register; 16] = [
399 let reg_info = isa.register_info();
400 let bank = reg_info.bank_containing_regunit(reg).unwrap();
403 // x86 GP registers have a weird mapping to DWARF registers, so we use a
405 X86_GP_REG_MAP[(reg - bank.first_unit) as usize]
407 "FloatRegs" => X86_XMM_REG_MAP[(reg - bank.first_unit) as usize],
409 panic!("unsupported register bank: {}", bank_name);
414 // Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137
415 fn translate_loc(isa: &dyn TargetIsa, loc: ValueLoc, stack_slots: &StackSlots) -> Option<Vec<u8>> {
417 ValueLoc::Reg(reg) => {
418 let machine_reg = map_reg(isa, reg).0 as u8;
419 assert!(machine_reg <= 32); // FIXME
420 Some(vec![gimli::constants::DW_OP_reg0.0 + machine_reg])
422 ValueLoc::Stack(ss) => {
423 if let Some(ss_offset) = stack_slots[ss].offset {
424 let endian = gimli::RunTimeEndian::Little;
425 let mut writer = write::EndianVec::new(endian);
427 .write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)
429 writer.write_sleb128(ss_offset as i64 + 16).expect("ss wr");
430 let buf = writer.into_vec();