diff --git a/cranelift/codegen/src/value_label.rs b/cranelift/codegen/src/value_label.rs index 94e5c58171..7f2fce7cce 100644 --- a/cranelift/codegen/src/value_label.rs +++ b/cranelift/codegen/src/value_label.rs @@ -18,9 +18,9 @@ use serde::{Deserialize, Serialize}; pub struct ValueLocRange { /// The ValueLoc containing a ValueLabel during this range. pub loc: ValueLoc, - /// The start of the range. + /// The start of the range. It is an offset in the generated code. pub start: u32, - /// The end of the range. + /// The end of the range. It is an offset in the generated code. pub end: u32, } diff --git a/crates/debug/src/transform/address_transform.rs b/crates/debug/src/transform/address_transform.rs index 3c2efe2863..71fa050172 100644 --- a/crates/debug/src/transform/address_transform.rs +++ b/crates/debug/src/transform/address_transform.rs @@ -597,20 +597,6 @@ impl AddressTransform { let map = &self.map[index]; (map.wasm_start, map.wasm_end) } - - pub fn convert_to_code_range( - &self, - addr: write::Address, - len: u64, - ) -> (GeneratedAddress, GeneratedAddress) { - let start = if let write::Address::Symbol { addend, .. } = addr { - // TODO subtract self.map[symbol].offset ? - addend as GeneratedAddress - } else { - unreachable!(); - }; - (start, start + len as GeneratedAddress) - } } #[cfg(test)] diff --git a/crates/debug/src/transform/attr.rs b/crates/debug/src/transform/attr.rs index 0820a82b17..1e74ca9a57 100644 --- a/crates/debug/src/transform/attr.rs +++ b/crates/debug/src/transform/attr.rs @@ -13,11 +13,11 @@ use wasmtime_environ::isa::TargetIsa; #[derive(Debug)] pub(crate) enum FileAttributeContext<'a> { Root(Option), - Children( - &'a Vec, - u64, - Option<&'a CompiledExpression<'a>>, - ), + Children { + file_map: &'a [write::FileId], + file_index_base: u64, + frame_base: Option<&'a CompiledExpression>, + }, } fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { @@ -55,8 +55,6 @@ pub(crate) fn clone_die_attributes<'a, R>( where R: Reader, { - let _tag = &entry.tag(); - let endian = gimli::RunTimeEndian::Little; let unit_encoding = unit.encoding(); let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { @@ -116,7 +114,12 @@ where } } AttributeValue::FileIndex(i) => { - if let FileAttributeContext::Children(file_map, file_index_base, _) = file_context { + if let FileAttributeContext::Children { + file_map, + file_index_base, + .. + } = file_context + { write::AttributeValue::FileIndex(Some(file_map[(i - file_index_base) as usize])) } else { return Err(TransformError("unexpected file index attribute").into()); @@ -150,34 +153,41 @@ where unit.addr_base, )?; let frame_base = - if let FileAttributeContext::Children(_, _, frame_base) = file_context { + if let FileAttributeContext::Children { frame_base, .. } = file_context { frame_base } else { None }; - let mut result = None; + + let mut result: Option> = None; while let Some(loc) = locs.next()? { - if let Some(expr) = - compile_expression(&loc.data, unit_encoding, frame_base, isa)? - { - if result.is_none() { - result = Some(Vec::new()); - } - for (start, len, expr) in expr.build_with_locals( - &[(loc.range.begin, loc.range.end)], - addr_tr, - frame_info, - endian, - )? { - if len == 0 { + if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { + let chunk = expr + .build_with_locals( + &[(loc.range.begin, loc.range.end)], + addr_tr, + frame_info, + isa, + ) + .filter(|i| { // Ignore empty range - continue; - } - result.as_mut().unwrap().push(write::Location::StartLength { - begin: start, - length: len, - data: expr, - }); + if let Ok((_, 0, _)) = i { + false + } else { + true + } + }) + .map(|i| { + i.map(|(start, len, expr)| write::Location::StartLength { + begin: start, + length: len, + data: expr, + }) + }) + .collect::, _>>()?; + match &mut result { + Some(r) => r.extend(chunk), + x @ None => *x = Some(chunk), } } else { // FIXME _expr contains invalid expression @@ -192,12 +202,12 @@ where } AttributeValue::Exprloc(ref expr) => { let frame_base = - if let FileAttributeContext::Children(_, _, frame_base) = file_context { + if let FileAttributeContext::Children { frame_base, .. } = file_context { frame_base } else { None }; - if let Some(expr) = compile_expression(expr, unit_encoding, frame_base, isa)? { + if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { if expr.is_simple() { if let Some(expr) = expr.build() { write::AttributeValue::Exprloc(expr) @@ -207,8 +217,9 @@ where } else { // Conversion to loclist is required. if let Some(scope_ranges) = scope_ranges { - let exprs = - expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian)?; + let exprs = expr + .build_with_locals(scope_ranges, addr_tr, frame_info, isa) + .collect::, _>>()?; if exprs.is_empty() { continue; } diff --git a/crates/debug/src/transform/expression.rs b/crates/debug/src/transform/expression.rs index 24948a78d1..c7b71c0030 100644 --- a/crates/debug/src/transform/expression.rs +++ b/crates/debug/src/transform/expression.rs @@ -29,76 +29,121 @@ impl<'a> FunctionFrameInfo<'a> { } } -#[derive(Debug)] +struct ExpressionWriter(write::EndianVec); + +impl ExpressionWriter { + pub fn new() -> Self { + let endian = gimli::RunTimeEndian::Little; + let writer = write::EndianVec::new(endian); + ExpressionWriter(writer) + } + + pub fn write_op(&mut self, op: gimli::DwOp) -> write::Result<()> { + self.write_u8(op.0 as u8) + } + + pub fn write_op_reg(&mut self, reg: u16) -> write::Result<()> { + if reg < 32 { + self.write_u8(gimli::constants::DW_OP_reg0.0 as u8 + reg as u8) + } else { + self.write_op(gimli::constants::DW_OP_regx)?; + self.write_uleb128(reg.into()) + } + } + + pub fn write_op_breg(&mut self, reg: u16) -> write::Result<()> { + if reg < 32 { + self.write_u8(gimli::constants::DW_OP_breg0.0 as u8 + reg as u8) + } else { + self.write_op(gimli::constants::DW_OP_bregx)?; + self.write_uleb128(reg.into()) + } + } + + pub fn write_u8(&mut self, b: u8) -> write::Result<()> { + write::Writer::write_u8(&mut self.0, b) + } + + pub fn write_uleb128(&mut self, i: u64) -> write::Result<()> { + write::Writer::write_uleb128(&mut self.0, i) + } + + pub fn write_sleb128(&mut self, i: i64) -> write::Result<()> { + write::Writer::write_sleb128(&mut self.0, i) + } + + pub fn into_vec(self) -> Vec { + self.0.into_vec() + } +} + +#[derive(Debug, Clone, PartialEq)] enum CompiledExpressionPart { + // Untranslated DWARF expression. Code(Vec), - Local(ValueLabel), + // The wasm-local DWARF operator. The label points to `ValueLabel`. + // The trailing field denotes that the operator was last in sequence, + // and it is the DWARF location (not a pointer). + Local { label: ValueLabel, trailing: bool }, + // Dereference is needed. Deref, } -#[derive(Debug)] -pub struct CompiledExpression<'a> { +#[derive(Debug, Clone, PartialEq)] +pub struct CompiledExpression { parts: Vec, need_deref: bool, - isa: &'a dyn TargetIsa, } -impl Clone for CompiledExpressionPart { - fn clone(&self) -> Self { - match self { - CompiledExpressionPart::Code(c) => CompiledExpressionPart::Code(c.clone()), - CompiledExpressionPart::Local(i) => CompiledExpressionPart::Local(*i), - CompiledExpressionPart::Deref => CompiledExpressionPart::Deref, - } - } -} - -impl<'a> CompiledExpression<'a> { - pub fn vmctx(isa: &'a dyn TargetIsa) -> CompiledExpression { - CompiledExpression::from_label(get_vmctx_value_label(), isa) +impl CompiledExpression { + pub fn vmctx() -> CompiledExpression { + CompiledExpression::from_label(get_vmctx_value_label()) } - pub fn from_label(label: ValueLabel, isa: &'a dyn TargetIsa) -> CompiledExpression<'a> { + pub fn from_label(label: ValueLabel) -> CompiledExpression { CompiledExpression { - parts: vec![ - CompiledExpressionPart::Local(label), - CompiledExpressionPart::Code(vec![gimli::constants::DW_OP_stack_value.0 as u8]), - ], + parts: vec![CompiledExpressionPart::Local { + label, + trailing: true, + }], need_deref: false, - isa, } } } +const X86_64_STACK_OFFSET: i64 = 16; + fn translate_loc( loc: ValueLoc, frame_info: Option<&FunctionFrameInfo>, isa: &dyn TargetIsa, + add_stack_value: bool, ) -> Result>> { - use gimli::write::Writer; Ok(match loc { + ValueLoc::Reg(reg) if add_stack_value => { + let machine_reg = isa.map_dwarf_register(reg)?; + let mut writer = ExpressionWriter::new(); + writer.write_op_reg(machine_reg)?; + Some(writer.into_vec()) + } ValueLoc::Reg(reg) => { - let machine_reg = isa.map_dwarf_register(reg)? as u8; - Some(if machine_reg < 32 { - vec![gimli::constants::DW_OP_reg0.0 + machine_reg] - } else { - let endian = gimli::RunTimeEndian::Little; - let mut writer = write::EndianVec::new(endian); - writer.write_u8(gimli::constants::DW_OP_regx.0 as u8)?; - writer.write_uleb128(machine_reg.into())?; - writer.into_vec() - }) + assert!(!add_stack_value); + let machine_reg = isa.map_dwarf_register(reg)?; + let mut writer = ExpressionWriter::new(); + writer.write_op_breg(machine_reg)?; + writer.write_sleb128(0)?; + Some(writer.into_vec()) } ValueLoc::Stack(ss) => { if let Some(frame_info) = frame_info { if let Some(ss_offset) = frame_info.stack_slots[ss].offset { - let endian = gimli::RunTimeEndian::Little; - let mut writer = write::EndianVec::new(endian); - writer.write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)?; - writer.write_sleb128(ss_offset as i64 + 16)?; - writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?; - let buf = writer.into_vec(); - return Ok(Some(buf)); + let mut writer = ExpressionWriter::new(); + writer.write_op_breg(X86_64::RBP.0)?; + writer.write_sleb128(ss_offset as i64 + X86_64_STACK_OFFSET)?; + if !add_stack_value { + writer.write_op(gimli::constants::DW_OP_deref)?; + } + return Ok(Some(writer.into_vec())); } } None @@ -111,11 +156,9 @@ fn append_memory_deref( buf: &mut Vec, frame_info: &FunctionFrameInfo, vmctx_loc: ValueLoc, - endian: gimli::RunTimeEndian, isa: &dyn TargetIsa, ) -> Result { - use gimli::write::Writer; - let mut writer = write::EndianVec::new(endian); + let mut writer = ExpressionWriter::new(); // FIXME for imported memory match vmctx_loc { ValueLoc::Reg(vmctx_reg) => { @@ -131,10 +174,10 @@ fn append_memory_deref( } ValueLoc::Stack(ss) => { if let Some(ss_offset) = frame_info.stack_slots[ss].offset { - writer.write_u8(gimli::constants::DW_OP_breg0.0 + X86_64::RBP.0 as u8)?; - writer.write_sleb128(ss_offset as i64 + 16)?; - writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_consts.0 as u8)?; + writer.write_op_breg(X86_64::RBP.0)?; + writer.write_sleb128(ss_offset as i64 + X86_64_STACK_OFFSET)?; + writer.write_op(gimli::constants::DW_OP_deref)?; + writer.write_op(gimli::constants::DW_OP_consts)?; let memory_offset = match frame_info.vmctx_memory_offset() { Some(offset) => offset, None => { @@ -142,7 +185,7 @@ fn append_memory_deref( } }; writer.write_sleb128(memory_offset)?; - writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?; + writer.write_op(gimli::constants::DW_OP_plus)?; } else { return Ok(false); } @@ -151,18 +194,17 @@ fn append_memory_deref( return Ok(false); } } - writer.write_u8(gimli::constants::DW_OP_deref.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_swap.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_stack_value.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_constu.0 as u8)?; + writer.write_op(gimli::constants::DW_OP_deref)?; + writer.write_op(gimli::constants::DW_OP_swap)?; + writer.write_op(gimli::constants::DW_OP_constu)?; writer.write_uleb128(0xffff_ffff)?; - writer.write_u8(gimli::constants::DW_OP_and.0 as u8)?; - writer.write_u8(gimli::constants::DW_OP_plus.0 as u8)?; - buf.extend_from_slice(writer.slice()); + writer.write_op(gimli::constants::DW_OP_and)?; + writer.write_op(gimli::constants::DW_OP_plus)?; + buf.extend(writer.into_vec()); Ok(true) } -impl<'a> CompiledExpression<'a> { +impl CompiledExpression { pub fn is_simple(&self) -> bool { if let [CompiledExpressionPart::Code(_)] = self.parts.as_slice() { true @@ -179,25 +221,60 @@ impl<'a> CompiledExpression<'a> { None } - pub fn build_with_locals( - &self, - scope: &[(u64, u64)], // wasm ranges - addr_tr: &AddressTransform, - frame_info: Option<&FunctionFrameInfo>, - endian: gimli::RunTimeEndian, - ) -> Result> { - if scope.is_empty() { - return Ok(vec![]); + pub fn build_with_locals<'a>( + &'a self, + scope: &'a [(u64, u64)], // wasm ranges + addr_tr: &'a AddressTransform, + frame_info: Option<&'a FunctionFrameInfo>, + isa: &'a dyn TargetIsa, + ) -> impl Iterator> + 'a { + enum BuildWithLocalsResult<'a> { + Empty, + Simple( + Box + 'a>, + Vec, + ), + Ranges( + Box)>> + 'a>, + ), } - - if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() { - let mut result_scope = Vec::new(); - for s in scope { - for (addr, len) in addr_tr.translate_ranges(s.0, s.1) { - result_scope.push((addr, len, write::Expression(code.to_vec()))); + impl Iterator for BuildWithLocalsResult<'_> { + type Item = Result<(write::Address, u64, write::Expression)>; + fn next(&mut self) -> Option { + match self { + BuildWithLocalsResult::Empty => None, + BuildWithLocalsResult::Simple(it, code) => it + .next() + .map(|(addr, len)| Ok((addr, len, write::Expression(code.to_vec())))), + BuildWithLocalsResult::Ranges(it) => it.next().map(|r| { + r.map(|(func_index, start, end, code_buf)| { + ( + write::Address::Symbol { + symbol: func_index.index(), + addend: start as i64, + }, + (end - start) as u64, + write::Expression(code_buf), + ) + }) + }), } } - return Ok(result_scope); + } + + if scope.is_empty() { + return BuildWithLocalsResult::Empty; + } + + // If it a simple DWARF code, no need in locals processing. Just translate + // the scope ranges. + if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() { + return BuildWithLocalsResult::Simple( + Box::new(scope.iter().flat_map(move |(wasm_start, wasm_end)| { + addr_tr.translate_ranges(*wasm_start, *wasm_end) + })), + code.clone(), + ); } let vmctx_label = get_vmctx_value_label(); @@ -205,87 +282,75 @@ impl<'a> CompiledExpression<'a> { // Some locals are present, preparing and divided ranges based on the scope // and frame_info data. let mut ranges_builder = ValueLabelRangesBuilder::new(scope, addr_tr, frame_info); - for p in &self.parts { + for p in self.parts.iter() { match p { CompiledExpressionPart::Code(_) => (), - CompiledExpressionPart::Local(label) => ranges_builder.process_label(*label), + CompiledExpressionPart::Local { label, .. } => ranges_builder.process_label(*label), CompiledExpressionPart::Deref => ranges_builder.process_label(vmctx_label), } } if self.need_deref { ranges_builder.process_label(vmctx_label); } - ranges_builder.remove_incomplete_ranges(); - let ranges = ranges_builder.ranges; + let ranges = ranges_builder.into_ranges(); - let mut result = Vec::new(); - 'range: for CachedValueLabelRange { - func_index, - start, - end, - label_location, - } in ranges - { - // build expression - let mut code_buf = Vec::new(); - for part in &self.parts { - match part { - CompiledExpressionPart::Code(c) => code_buf.extend_from_slice(c.as_slice()), - CompiledExpressionPart::Local(label) => { - let loc = *label_location.get(&label).context("label_location")?; - if let Some(expr) = translate_loc(loc, frame_info, self.isa)? { - code_buf.extend_from_slice(&expr) - } else { - continue 'range; + return BuildWithLocalsResult::Ranges(Box::new( + ranges + .into_iter() + .map( + move |CachedValueLabelRange { + func_index, + start, + end, + label_location, + }| { + // build expression + let mut code_buf = Vec::new(); + macro_rules! deref { + () => { + if let (Some(vmctx_loc), Some(frame_info)) = + (label_location.get(&vmctx_label), frame_info) + { + if !append_memory_deref( + &mut code_buf, + frame_info, + *vmctx_loc, + isa, + )? { + return Ok(None); + } + } else { + return Ok(None); + }; + }; } - } - CompiledExpressionPart::Deref => { - if let (Some(vmctx_loc), Some(frame_info)) = - (label_location.get(&vmctx_label), frame_info) - { - if !append_memory_deref( - &mut code_buf, - frame_info, - *vmctx_loc, - endian, - self.isa, - )? { - continue 'range; + for part in &self.parts { + match part { + CompiledExpressionPart::Code(c) => { + code_buf.extend_from_slice(c.as_slice()) + } + CompiledExpressionPart::Local { label, trailing } => { + let loc = + *label_location.get(&label).context("label_location")?; + if let Some(expr) = + translate_loc(loc, frame_info, isa, *trailing)? + { + code_buf.extend_from_slice(&expr) + } else { + return Ok(None); + } + } + CompiledExpressionPart::Deref => deref!(), } - } else { - continue 'range; - }; - } - } - } - if self.need_deref { - if let (Some(vmctx_loc), Some(frame_info)) = - (label_location.get(&vmctx_label), frame_info) - { - if !append_memory_deref( - &mut code_buf, - frame_info, - *vmctx_loc, - endian, - self.isa, - )? { - continue 'range; - } - } else { - continue 'range; - }; - } - result.push(( - write::Address::Symbol { - symbol: func_index.index(), - addend: start as i64, - }, - (end - start) as u64, - write::Expression(code_buf), - )); - } - - Ok(result) + } + if self.need_deref { + deref!(); + } + Ok(Some((func_index, start, end, code_buf))) + }, + ) + .filter_map(Result::transpose), + )); } } @@ -299,12 +364,11 @@ fn is_old_expression_format(buf: &[u8]) -> bool { buf.contains(&(gimli::constants::DW_OP_plus_uconst.0 as u8)) } -pub fn compile_expression<'a, R>( +pub fn compile_expression( expr: &Expression, encoding: gimli::Encoding, frame_base: Option<&CompiledExpression>, - isa: &'a dyn TargetIsa, -) -> Result>, Error> +) -> Result, Error> where R: Reader, { @@ -315,10 +379,21 @@ where if is_old_expression_format(&buf) && frame_base.is_some() { // Still supporting old DWARF variable expressions without fbreg. parts.extend_from_slice(&frame_base.unwrap().parts); + if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() { + *trailing = false; + } need_deref = frame_base.unwrap().need_deref; } let base_len = parts.len(); let mut code_chunk = Vec::new(); + macro_rules! flush_code_chunk { + () => { + if !code_chunk.is_empty() { + parts.push(CompiledExpressionPart::Code(code_chunk)); + code_chunk = Vec::new(); + } + }; + }; while !pc.is_empty() { let next = buf[pc.offset_from(&expr.0).into_u64() as usize]; need_deref = true; @@ -331,54 +406,52 @@ where // TODO support wasm globals? return Ok(None); } - let index = pc.read_uleb128()?; - if pc.read_u8()? != 159 { - // FIXME The following operator is not DW_OP_stack_value, e.g. : - // DW_AT_location (0x00000ea5: - // [0x00001e19, 0x00001e26): DW_OP_WASM_location 0x0 +1, DW_OP_plus_uconst 0x10, DW_OP_stack_value - // [0x00001e5a, 0x00001e72): DW_OP_WASM_location 0x0 +20, DW_OP_stack_value - // ) - return Ok(None); - } - if !code_chunk.is_empty() { - parts.push(CompiledExpressionPart::Code(code_chunk)); - code_chunk = Vec::new(); - } + let index = pc.read_sleb128()?; + flush_code_chunk!(); let label = ValueLabel::from_u32(index as u32); - parts.push(CompiledExpressionPart::Local(label)); + parts.push(CompiledExpressionPart::Local { + label, + trailing: false, + }); } else { let pos = pc.offset_from(&expr.0).into_u64() as usize; let op = Operation::parse(&mut pc, &expr.0, encoding)?; match op { Operation::FrameOffset { offset } => { // Expand DW_OP_fpreg into frame location and DW_OP_plus_uconst. - use gimli::write::Writer; if frame_base.is_some() { // Add frame base expressions. - if !code_chunk.is_empty() { - parts.push(CompiledExpressionPart::Code(code_chunk)); - code_chunk = Vec::new(); - } + flush_code_chunk!(); parts.extend_from_slice(&frame_base.unwrap().parts); - need_deref = frame_base.unwrap().need_deref; + } + if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() { + // Reset local trailing flag. + *trailing = false; } // Append DW_OP_plus_uconst part. - let endian = gimli::RunTimeEndian::Little; - let mut writer = write::EndianVec::new(endian); - writer.write_u8(gimli::constants::DW_OP_plus_uconst.0 as u8)?; + let mut writer = ExpressionWriter::new(); + writer.write_op(gimli::constants::DW_OP_plus_uconst)?; writer.write_uleb128(offset as u64)?; code_chunk.extend(writer.into_vec()); continue; } - Operation::Literal { .. } | Operation::PlusConstant { .. } => (), + Operation::Literal { .. } + | Operation::PlusConstant { .. } + | Operation::Piece { .. } => (), Operation::StackValue => { need_deref = false; + + // Find extra stack_value, that follow wasm-local operators, + // and mark such locals with special flag. + if let (Some(CompiledExpressionPart::Local { trailing, .. }), true) = + (parts.last_mut(), code_chunk.is_empty()) + { + *trailing = true; + continue; + } } Operation::Deref { .. } => { - if !code_chunk.is_empty() { - parts.push(CompiledExpressionPart::Code(code_chunk)); - code_chunk = Vec::new(); - } + flush_code_chunk!(); parts.push(CompiledExpressionPart::Deref); } _ => { @@ -406,11 +479,7 @@ where } } - Ok(Some(CompiledExpression { - parts, - need_deref, - isa, - })) + Ok(Some(CompiledExpression { parts, need_deref })) } #[derive(Debug, Clone)] @@ -423,34 +492,30 @@ struct CachedValueLabelRange { struct ValueLabelRangesBuilder<'a, 'b> { ranges: Vec, - addr_tr: &'a AddressTransform, frame_info: Option<&'a FunctionFrameInfo<'b>>, processed_labels: HashSet, } impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> { - fn new( + pub fn new( scope: &[(u64, u64)], // wasm ranges addr_tr: &'a AddressTransform, frame_info: Option<&'a FunctionFrameInfo<'b>>, ) -> Self { let mut ranges = Vec::new(); - for s in scope { - if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(s.0, s.1) { - for (start, end) in tr { - ranges.push(CachedValueLabelRange { - func_index, - start, - end, - label_location: HashMap::new(), - }) - } + for (wasm_start, wasm_end) in scope { + if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(*wasm_start, *wasm_end) { + ranges.extend(tr.into_iter().map(|(start, end)| CachedValueLabelRange { + func_index, + start, + end, + label_location: HashMap::new(), + })); } } ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start)); ValueLabelRangesBuilder { ranges, - addr_tr, frame_info, processed_labels: HashSet::new(), } @@ -462,76 +527,282 @@ impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> { } self.processed_labels.insert(label); - let value_ranges = if let Some(frame_info) = self.frame_info { - &frame_info.value_ranges - } else { - return; + let value_ranges = match self.frame_info.and_then(|fi| fi.value_ranges.get(&label)) { + Some(value_ranges) => value_ranges, + None => { + return; + } }; let ranges = &mut self.ranges; - if let Some(local_ranges) = value_ranges.get(&label) { - for local_range in local_ranges { - let wasm_start = local_range.start; - let wasm_end = local_range.end; - let loc = local_range.loc; - // Find all native ranges for the value label ranges. - for (addr, len) in self - .addr_tr - .translate_ranges(wasm_start as u64, wasm_end as u64) - { - let (range_start, range_end) = self.addr_tr.convert_to_code_range(addr, len); - if range_start == range_end { - continue; - } - assert_lt!(range_start, range_end); - // Find acceptable scope of ranges to intersect with. - let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) { - Ok(i) => i, - Err(i) => { - if i > 0 && range_start < ranges[i - 1].end { - i - 1 - } else { - i - } - } - }; - let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) { - Ok(i) | Err(i) => i, - }; - // Starting for the end, intersect (range_start..range_end) with - // self.ranges array. - for i in (i..j).rev() { - if range_end <= ranges[i].start || ranges[i].end <= range_start { - continue; - } - if range_end < ranges[i].end { - // Cutting some of the range from the end. - let mut tail = ranges[i].clone(); - ranges[i].end = range_end; - tail.start = range_end; - ranges.insert(i + 1, tail); - } - assert_le!(ranges[i].end, range_end); - if range_start <= ranges[i].start { - ranges[i].label_location.insert(label, loc); - continue; - } - // Cutting some of the range from the start. - let mut tail = ranges[i].clone(); - ranges[i].end = range_start; - tail.start = range_start; - tail.label_location.insert(label, loc); - ranges.insert(i + 1, tail); + for value_range in value_ranges { + let range_start = value_range.start as usize; + let range_end = value_range.end as usize; + let loc = value_range.loc; + if range_start == range_end { + continue; + } + assert_lt!(range_start, range_end); + + // Find acceptable scope of ranges to intersect with. + let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) { + Ok(i) => i, + Err(i) => { + if i > 0 && range_start < ranges[i - 1].end { + i - 1 + } else { + i } } + }; + let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) { + Ok(i) | Err(i) => i, + }; + // Starting from the end, intersect (range_start..range_end) with + // self.ranges array. + for i in (i..j).rev() { + if range_end <= ranges[i].start || ranges[i].end <= range_start { + continue; + } + if range_end < ranges[i].end { + // Cutting some of the range from the end. + let mut tail = ranges[i].clone(); + ranges[i].end = range_end; + tail.start = range_end; + ranges.insert(i + 1, tail); + } + assert_le!(ranges[i].end, range_end); + if range_start <= ranges[i].start { + ranges[i].label_location.insert(label, loc); + continue; + } + // Cutting some of the range from the start. + let mut tail = ranges[i].clone(); + ranges[i].end = range_start; + tail.start = range_start; + tail.label_location.insert(label, loc); + ranges.insert(i + 1, tail); } } } - fn remove_incomplete_ranges(&mut self) { + pub fn into_ranges(self) -> impl Iterator { // Ranges with not-enough labels are discarded. let processed_labels_len = self.processed_labels.len(); self.ranges - .retain(|r| r.label_location.len() == processed_labels_len); + .into_iter() + .filter(move |r| r.label_location.len() == processed_labels_len) + } +} + +#[cfg(test)] +mod tests { + use super::compile_expression; + use super::{AddressTransform, FunctionFrameInfo, ValueLabel, ValueLabelsRanges}; + use gimli::{self, Encoding, EndianSlice, Expression, RunTimeEndian}; + + macro_rules! expression { + ($($i:literal),*) => { + Expression(EndianSlice::new( + &[$($i),*], + RunTimeEndian::Little, + )) + } + } + + static DWARF_ENCODING: Encoding = Encoding { + address_size: 4, + format: gimli::Format::Dwarf32, + version: 4, + }; + + #[test] + fn test_debug_parse_expressions() { + use super::{CompiledExpression, CompiledExpressionPart}; + use wasmtime_environ::entity::EntityRef; + + let (val1, val3, val20) = (ValueLabel::new(1), ValueLabel::new(3), ValueLabel::new(20)); + + // DW_OP_WASM_location 0x0 +20, DW_OP_stack_value + let e = expression!(0xed, 0x00, 0x14, 0x9f); + let ce = compile_expression(&e, DWARF_ENCODING, None) + .expect("non-error") + .expect("expression"); + assert_eq!( + ce, + CompiledExpression { + parts: vec![CompiledExpressionPart::Local { + label: val20, + trailing: true + }], + need_deref: false + } + ); + + // DW_OP_WASM_location 0x0 +1, DW_OP_plus_uconst 0x10, DW_OP_stack_value + let e = expression!(0xed, 0x00, 0x01, 0x23, 0x10, 0x9f); + let ce = compile_expression(&e, DWARF_ENCODING, None) + .expect("non-error") + .expect("expression"); + assert_eq!( + ce, + CompiledExpression { + parts: vec![ + CompiledExpressionPart::Local { + label: val1, + trailing: false + }, + CompiledExpressionPart::Code(vec![35, 16, 159]) + ], + need_deref: false + } + ); + + // Frame base: DW_OP_WASM_location 0x0 +3, DW_OP_stack_value + let e = expression!(0xed, 0x00, 0x03, 0x9f); + let fe = compile_expression(&e, DWARF_ENCODING, None).expect("non-error"); + // DW_OP_fpreg 0x12 + let e = expression!(0x91, 0x12); + let ce = compile_expression(&e, DWARF_ENCODING, fe.as_ref()) + .expect("non-error") + .expect("expression"); + assert_eq!( + ce, + CompiledExpression { + parts: vec![ + CompiledExpressionPart::Local { + label: val3, + trailing: false + }, + CompiledExpressionPart::Code(vec![35, 18]) + ], + need_deref: true + } + ); + } + + fn create_mock_address_transform() -> AddressTransform { + use crate::read_debuginfo::WasmFileInfo; + use wasmtime_environ::entity::PrimaryMap; + use wasmtime_environ::ir::SourceLoc; + use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap}; + let mut module_map = PrimaryMap::new(); + let code_section_offset: u32 = 100; + module_map.push(FunctionAddressMap { + instructions: vec![ + InstructionAddressMap { + srcloc: SourceLoc::new(code_section_offset + 12), + code_offset: 5, + code_len: 3, + }, + InstructionAddressMap { + srcloc: SourceLoc::new(code_section_offset + 17), + code_offset: 15, + code_len: 8, + }, + ], + start_srcloc: SourceLoc::new(code_section_offset + 10), + end_srcloc: SourceLoc::new(code_section_offset + 20), + body_offset: 0, + body_len: 30, + }); + let fi = WasmFileInfo { + code_section_offset: code_section_offset.into(), + funcs: Box::new([]), + imported_func_count: 0, + path: None, + }; + AddressTransform::new(&module_map, &fi) + } + + fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) { + use std::collections::HashMap; + use wasmtime_environ::entity::EntityRef; + use wasmtime_environ::ir::{ValueLoc, ValueLocRange}; + let mut value_ranges = HashMap::new(); + let value_0 = ValueLabel::new(0); + let value_1 = ValueLabel::new(1); + let value_2 = ValueLabel::new(2); + value_ranges.insert( + value_0, + vec![ValueLocRange { + loc: ValueLoc::Unassigned, + start: 0, + end: 25, + }], + ); + value_ranges.insert( + value_1, + vec![ValueLocRange { + loc: ValueLoc::Unassigned, + start: 5, + end: 30, + }], + ); + value_ranges.insert( + value_2, + vec![ + ValueLocRange { + loc: ValueLoc::Unassigned, + start: 0, + end: 10, + }, + ValueLocRange { + loc: ValueLoc::Unassigned, + start: 20, + end: 30, + }, + ], + ); + (value_ranges, (value_0, value_1, value_2)) + } + + #[test] + fn test_debug_value_range_builder() { + use super::ValueLabelRangesBuilder; + use wasmtime_environ::entity::EntityRef; + use wasmtime_environ::ir::StackSlots; + use wasmtime_environ::wasm::DefinedFuncIndex; + use wasmtime_environ::ModuleMemoryOffset; + + let addr_tr = create_mock_address_transform(); + let stack_slots = StackSlots::new(); + let (value_ranges, value_labels) = create_mock_value_ranges(); + let fi = FunctionFrameInfo { + memory_offset: ModuleMemoryOffset::None, + stack_slots: &stack_slots, + value_ranges: &value_ranges, + }; + + // No value labels, testing if entire function range coming through. + let builder = ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi)); + let ranges = builder.into_ranges().collect::>(); + assert_eq!(ranges.len(), 1); + assert_eq!(ranges[0].func_index, DefinedFuncIndex::new(0)); + assert_eq!(ranges[0].start, 0); + assert_eq!(ranges[0].end, 30); + + // Two labels (val0@0..25 and val1@5..30), their common lifetime intersect at 5..25. + let mut builder = ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi)); + builder.process_label(value_labels.0); + builder.process_label(value_labels.1); + let ranges = builder.into_ranges().collect::>(); + assert_eq!(ranges.len(), 1); + assert_eq!(ranges[0].start, 5); + assert_eq!(ranges[0].end, 25); + + // Adds val2 with complex lifetime @0..10 and @20..30 to the previous test, and + // also narrows range. + let mut builder = ValueLabelRangesBuilder::new(&[(11, 17)], &addr_tr, Some(&fi)); + builder.process_label(value_labels.0); + builder.process_label(value_labels.1); + builder.process_label(value_labels.2); + let ranges = builder.into_ranges().collect::>(); + // Result is two ranges @5..10 and @20..23 + assert_eq!(ranges.len(), 2); + assert_eq!(ranges[0].start, 5); + assert_eq!(ranges[0].end, 10); + assert_eq!(ranges[1].start, 20); + assert_eq!(ranges[1].end, 23); } } diff --git a/crates/debug/src/transform/simulate.rs b/crates/debug/src/transform/simulate.rs index ec70dec4ea..cb8b38c9ed 100644 --- a/crates/debug/src/transform/simulate.rs +++ b/crates/debug/src/transform/simulate.rs @@ -228,19 +228,16 @@ fn generate_vars( }; let loc_list_id = { - let endian = gimli::RunTimeEndian::Little; - - let expr = CompiledExpression::from_label(*label, isa); - let mut locs = Vec::new(); - for (begin, length, data) in - expr.build_with_locals(scope_ranges, addr_tr, Some(frame_info), endian)? - { - locs.push(write::Location::StartLength { - begin, - length, - data, - }); - } + let locs = CompiledExpression::from_label(*label) + .build_with_locals(scope_ranges, addr_tr, Some(frame_info), isa) + .map(|i| { + i.map(|(begin, length, data)| write::Location::StartLength { + begin, + length, + data, + }) + }) + .collect::, _>>()?; unit.locations.add(write::LocationList(locs)) }; diff --git a/crates/debug/src/transform/unit.rs b/crates/debug/src/transform/unit.rs index c37788e5fa..a04f11b4ec 100644 --- a/crates/debug/src/transform/unit.rs +++ b/crates/debug/src/transform/unit.rs @@ -386,7 +386,7 @@ where } if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? { - if let Some(expr) = compile_expression(&expr, unit.encoding(), None, isa)? { + if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? { current_frame_base.push(new_stack_len, expr); } } @@ -432,7 +432,11 @@ where out_strings, &mut pending_die_refs, &mut pending_di_refs, - FileAttributeContext::Children(&file_map, file_index_base, current_frame_base.top()), + FileAttributeContext::Children { + file_map: &file_map, + file_index_base, + frame_base: current_frame_base.top(), + }, isa, )?; diff --git a/crates/debug/src/transform/utils.rs b/crates/debug/src/transform/utils.rs index 506b6b71c0..f6a30ab21a 100644 --- a/crates/debug/src/transform/utils.rs +++ b/crates/debug/src/transform/utils.rs @@ -134,19 +134,17 @@ pub(crate) fn append_vmctx_info( isa: &dyn TargetIsa, ) -> Result<(), Error> { let loc = { - let endian = gimli::RunTimeEndian::Little; - - let expr = CompiledExpression::vmctx(isa); - let mut locs = Vec::new(); - for (begin, length, data) in - expr.build_with_locals(scope_ranges, addr_tr, frame_info, endian)? - { - locs.push(write::Location::StartLength { - begin, - length, - data, - }); - } + let expr = CompiledExpression::vmctx(); + let locs = expr + .build_with_locals(scope_ranges, addr_tr, frame_info, isa) + .map(|i| { + i.map(|(begin, length, data)| write::Location::StartLength { + begin, + length, + data, + }) + }) + .collect::, _>>()?; let list_id = comp_unit.locations.add(write::LocationList(locs)); write::AttributeValue::LocationListRef(list_id) }; diff --git a/crates/environ/src/data_structures.rs b/crates/environ/src/data_structures.rs index 3fcfb3a54a..e12768a4cd 100755 --- a/crates/environ/src/data_structures.rs +++ b/crates/environ/src/data_structures.rs @@ -5,7 +5,7 @@ pub mod ir { types, AbiParam, ArgumentPurpose, Signature, SourceLoc, StackSlots, TrapCode, Type, ValueLabel, ValueLoc, }; - pub use cranelift_codegen::ValueLabelsRanges; + pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange}; } pub mod settings {