6 use cranelift::codegen::ir::{StackSlots, ValueLoc};
7 use cranelift::codegen::isa::RegUnit;
10 self, Address, AttributeValue, DwarfUnit, Expression, LineProgram, LineString, Location,
11 LocationList, Range, RangeList, UnitEntryId, Writer,
13 use gimli::{Encoding, Format, LineEncoding, Register, RunTimeEndian, X86_64};
15 pub use emit::{DebugReloc, DebugRelocName};
17 fn target_endian(tcx: TyCtxt) -> RunTimeEndian {
18 use rustc::ty::layout::Endian;
20 match tcx.data_layout.endian {
21 Endian::Big => RunTimeEndian::Big,
22 Endian::Little => RunTimeEndian::Little,
26 pub struct DebugContext<'tcx> {
29 endian: RunTimeEndian,
30 symbols: indexmap::IndexMap<FuncId, String>,
33 unit_range_list: RangeList,
35 types: HashMap<Ty<'tcx>, UnitEntryId>,
38 impl<'tcx> DebugContext<'tcx> {
39 pub fn new(tcx: TyCtxt<'tcx>, address_size: u8) -> Self {
40 let encoding = Encoding {
41 format: Format::Dwarf32,
42 // TODO: this should be configurable
43 // macOS doesn't seem to support DWARF > 3
48 let mut dwarf = DwarfUnit::new(encoding);
50 // FIXME: how to get version when building out of tree?
51 // Normally this would use option_env!("CFG_VERSION").
52 let producer = format!("cranelift fn (rustc version {})", "unknown version");
53 let comp_dir = tcx.sess.working_dir.0.to_string_lossy().into_owned();
54 let name = match tcx.sess.local_crate_source_file {
55 Some(ref path) => path.to_string_lossy().into_owned(),
56 None => tcx.crate_name(LOCAL_CRATE).to_string(),
59 let line_program = LineProgram::new(
61 LineEncoding::default(),
62 LineString::new(comp_dir.as_bytes(), encoding, &mut dwarf.line_strings),
63 LineString::new(name.as_bytes(), encoding, &mut dwarf.line_strings),
66 dwarf.unit.line_program = line_program;
69 let name = dwarf.strings.add(&*name);
70 let comp_dir = dwarf.strings.add(&*comp_dir);
72 let root = dwarf.unit.root();
73 let root = dwarf.unit.get_mut(root);
75 gimli::DW_AT_producer,
76 AttributeValue::StringRef(dwarf.strings.add(producer)),
79 gimli::DW_AT_language,
80 AttributeValue::Language(gimli::DW_LANG_Rust),
82 root.set(gimli::DW_AT_name, AttributeValue::StringRef(name));
83 root.set(gimli::DW_AT_comp_dir, AttributeValue::StringRef(comp_dir));
86 AttributeValue::Address(Address::Constant(0)),
93 endian: target_endian(tcx),
94 symbols: indexmap::IndexMap::new(),
97 unit_range_list: RangeList(Vec::new()),
99 types: HashMap::new(),
103 fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
104 if let Some(type_id) = self.types.get(ty) {
108 let new_entry = |dwarf: &mut DwarfUnit, tag| dwarf.unit.add(dwarf.unit.root(), tag);
110 let primtive = |dwarf: &mut DwarfUnit, ate| {
111 let type_id = new_entry(dwarf, gimli::DW_TAG_base_type);
112 let type_entry = dwarf.unit.get_mut(type_id);
113 type_entry.set(gimli::DW_AT_encoding, AttributeValue::Encoding(ate));
117 let type_id = match ty.kind {
118 ty::Bool => primtive(&mut self.dwarf, gimli::DW_ATE_boolean),
119 ty::Char => primtive(&mut self.dwarf, gimli::DW_ATE_UTF),
120 ty::Uint(_) => primtive(&mut self.dwarf, gimli::DW_ATE_unsigned),
121 ty::Int(_) => primtive(&mut self.dwarf, gimli::DW_ATE_signed),
122 ty::Float(_) => primtive(&mut self.dwarf, gimli::DW_ATE_float),
123 ty::Ref(_, pointee_ty, mutbl)
124 | ty::RawPtr(ty::TypeAndMut {
128 let type_id = new_entry(&mut self.dwarf, gimli::DW_TAG_pointer_type);
130 // Ensure that type is inserted before recursing to avoid duplicates
131 self.types.insert(ty, type_id);
133 let pointee = self.dwarf_ty(pointee_ty);
135 let type_entry = self.dwarf.unit.get_mut(type_id);
137 //type_entry.set(gimli::DW_AT_mutable, AttributeValue::Flag(mutbl == rustc::hir::Mutability::MutMutable));
138 type_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(pointee));
142 _ => new_entry(&mut self.dwarf, gimli::DW_TAG_structure_type),
144 let name = format!("{}", ty);
145 let layout = self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();
147 let type_entry = self.dwarf.unit.get_mut(type_id);
149 type_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
151 gimli::DW_AT_byte_size,
152 AttributeValue::Udata(layout.size.bytes()),
155 self.types.insert(ty, type_id);
161 pub struct FunctionDebugContext<'a, 'tcx> {
162 debug_context: &'a mut DebugContext<'tcx>,
163 entry_id: UnitEntryId,
165 instance: Instance<'tcx>,
166 mir: &'tcx mir::Body<'tcx>,
169 impl<'a, 'tcx> FunctionDebugContext<'a, 'tcx> {
171 debug_context: &'a mut DebugContext<'tcx>,
172 instance: Instance<'tcx>,
176 let mir = debug_context.tcx.instance_mir(instance.def);
178 let (symbol, _) = debug_context.symbols.insert_full(func_id, name.to_string());
180 // FIXME: add to appropriate scope intead of root
181 let scope = debug_context.dwarf.unit.root();
183 let entry_id = debug_context
186 .add(scope, gimli::DW_TAG_subprogram);
187 let entry = debug_context.dwarf.unit.get_mut(entry_id);
188 let name_id = debug_context.dwarf.strings.add(name);
190 gimli::DW_AT_linkage_name,
191 AttributeValue::StringRef(name_id),
194 FunctionDebugContext {
203 fn define_local(&mut self, local: mir::Local) -> UnitEntryId {
204 let local_decl = &self.mir.local_decls[local];
206 let ty = self.debug_context.tcx.subst_and_normalize_erasing_regions(
207 self.instance.substs,
208 ty::ParamEnv::reveal_all(),
211 let dw_ty = self.debug_context.dwarf_ty(ty);
213 let name = if let Some(name) = local_decl.name {
214 format!("{}{:?}", name.as_str(), local)
216 format!("{:?}", local)
223 .add(self.entry_id, gimli::DW_TAG_variable);
224 let var_entry = self.debug_context.dwarf.unit.get_mut(var_id);
226 var_entry.set(gimli::DW_AT_name, AttributeValue::String(name.into_bytes()));
227 var_entry.set(gimli::DW_AT_type, AttributeValue::ThisUnitEntryRef(dw_ty));
235 isa: &dyn cranelift::codegen::isa::TargetIsa,
236 source_info_set: &indexmap::IndexSet<(Span, mir::SourceScope)>,
237 local_map: HashMap<mir::Local, CPlace<'tcx>>,
239 let end = self.create_debug_lines(context, isa, source_info_set);
244 .push(Range::StartLength {
245 begin: Address::Symbol {
252 let value_labels_ranges = context.build_value_labels_ranges(isa).unwrap();
254 for (local, _local_decl) in self.mir.local_decls.iter_enumerated() {
255 let var_id = self.define_local(local);
256 let value_label = cranelift::codegen::ir::ValueLabel::from_u32(local.as_u32());
258 let location = match local_map[&local].inner() {
259 CPlaceInner::Var(_) => {
260 if let Some(value_loc_ranges) = value_labels_ranges.get(&value_label) {
261 let loc_list = LocationList(
264 .map(|value_loc_range| Location::StartEnd {
265 begin: Address::Symbol {
267 addend: i64::from(value_loc_range.start),
269 end: Address::Symbol {
271 addend: i64::from(value_loc_range.end),
274 translate_loc(value_loc_range.loc, &context.func.stack_slots).unwrap(),
279 let loc_list_id = self.debug_context.dwarf.unit.locations.add(loc_list);
281 AttributeValue::LocationListRef(loc_list_id)
283 // FIXME set value labels for unused locals
285 AttributeValue::Exprloc(Expression(vec![]))
288 CPlaceInner::Addr(_, _) => {
289 // FIXME implement this (used by arguments and returns)
291 AttributeValue::Exprloc(Expression(vec![]))
293 CPlaceInner::Stack(stack_slot) => {
294 AttributeValue::Exprloc(Expression(translate_loc(ValueLoc::Stack(*stack_slot), &context.func.stack_slots).unwrap()))
296 CPlaceInner::NoPlace => AttributeValue::Exprloc(Expression(vec![])),
299 let var_entry = self.debug_context.dwarf.unit.get_mut(var_id);
300 var_entry.set(gimli::DW_AT_location, location);
311 // Adapted from https://github.com/CraneStation/wasmtime/blob/5a1845b4caf7a5dba8eda1fef05213a532ed4259/crates/debug/src/transform/expression.rs#L59-L137
313 fn map_reg(reg: RegUnit) -> Register {
314 static mut REG_X86_MAP: Option<HashMap<RegUnit, Register>> = None;
315 // FIXME lazy initialization?
317 if REG_X86_MAP.is_none() {
318 REG_X86_MAP = Some(HashMap::new());
320 if let Some(val) = REG_X86_MAP.as_mut().unwrap().get(®) {
323 let result = match reg {
356 _ => panic!("unknown x86_64 register {}", reg),
358 REG_X86_MAP.as_mut().unwrap().insert(reg, result);
363 fn translate_loc(loc: ValueLoc, stack_slots: &StackSlots) -> Option<Vec<u8>> {
365 ValueLoc::Reg(reg) => {
366 let machine_reg = map_reg(reg).0 as u8;
367 assert!(machine_reg <= 32); // FIXME
368 Some(vec![gimli::constants::DW_OP_reg0.0 + machine_reg])
370 ValueLoc::Stack(ss) => {
371 if let Some(ss_offset) = stack_slots[ss].offset {
372 let endian = gimli::RunTimeEndian::Little;
373 let mut writer = write::EndianVec::new(endian);
375 .write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)
377 writer.write_sleb128(ss_offset as i64 + 16).expect("ss wr");
379 .write_u8(gimli::constants::DW_OP_deref.0 as u8)
381 let buf = writer.into_vec();