1 use super::address_transform::AddressTransform;
2 use anyhow::{Context, Error, Result};
3 use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, X86_64};
4 use more_asserts::{assert_le, assert_lt};
5 use std::collections::{HashMap, HashSet};
6 use wasmtime_environ::entity::EntityRef;
7 use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc};
8 use wasmtime_environ::isa::TargetIsa;
9 use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
10 use wasmtime_environ::ModuleMemoryOffset;
11
12 #[derive(Debug)]
13 pub struct FunctionFrameInfo<'a> {
14 pub value_ranges: &'a ValueLabelsRanges,
15 pub memory_offset: ModuleMemoryOffset,
16 pub stack_slots: &'a StackSlots,
17 }
18
19 impl<'a> FunctionFrameInfo<'a> {
vmctx_memory_offset(&self) -> Option<i64>20 fn vmctx_memory_offset(&self) -> Option<i64> {
21 match self.memory_offset {
22 ModuleMemoryOffset::Defined(x) => Some(x as i64),
23 ModuleMemoryOffset::Imported(_) => {
24 // TODO implement memory offset for imported memory
25 None
26 }
27 ModuleMemoryOffset::None => None,
28 }
29 }
30 }
31
32 struct ExpressionWriter(write::EndianVec<gimli::RunTimeEndian>);
33
34 impl ExpressionWriter {
new() -> Self35 pub fn new() -> Self {
36 let endian = gimli::RunTimeEndian::Little;
37 let writer = write::EndianVec::new(endian);
38 ExpressionWriter(writer)
39 }
40
write_op(&mut self, op: gimli::DwOp) -> write::Result<()>41 pub fn write_op(&mut self, op: gimli::DwOp) -> write::Result<()> {
42 self.write_u8(op.0 as u8)
43 }
44
write_op_reg(&mut self, reg: u16) -> write::Result<()>45 pub fn write_op_reg(&mut self, reg: u16) -> write::Result<()> {
46 if reg < 32 {
47 self.write_u8(gimli::constants::DW_OP_reg0.0 as u8 + reg as u8)
48 } else {
49 self.write_op(gimli::constants::DW_OP_regx)?;
50 self.write_uleb128(reg.into())
51 }
52 }
53
write_op_breg(&mut self, reg: u16) -> write::Result<()>54 pub fn write_op_breg(&mut self, reg: u16) -> write::Result<()> {
55 if reg < 32 {
56 self.write_u8(gimli::constants::DW_OP_breg0.0 as u8 + reg as u8)
57 } else {
58 self.write_op(gimli::constants::DW_OP_bregx)?;
59 self.write_uleb128(reg.into())
60 }
61 }
62
write_u8(&mut self, b: u8) -> write::Result<()>63 pub fn write_u8(&mut self, b: u8) -> write::Result<()> {
64 write::Writer::write_u8(&mut self.0, b)
65 }
66
write_uleb128(&mut self, i: u64) -> write::Result<()>67 pub fn write_uleb128(&mut self, i: u64) -> write::Result<()> {
68 write::Writer::write_uleb128(&mut self.0, i)
69 }
70
write_sleb128(&mut self, i: i64) -> write::Result<()>71 pub fn write_sleb128(&mut self, i: i64) -> write::Result<()> {
72 write::Writer::write_sleb128(&mut self.0, i)
73 }
74
into_vec(self) -> Vec<u8>75 pub fn into_vec(self) -> Vec<u8> {
76 self.0.into_vec()
77 }
78 }
79
80 #[derive(Debug, Clone, PartialEq)]
81 enum CompiledExpressionPart {
82 // Untranslated DWARF expression.
83 Code(Vec<u8>),
84 // The wasm-local DWARF operator. The label points to `ValueLabel`.
85 // The trailing field denotes that the operator was last in sequence,
86 // and it is the DWARF location (not a pointer).
87 Local { label: ValueLabel, trailing: bool },
88 // Dereference is needed.
89 Deref,
90 }
91
92 #[derive(Debug, Clone, PartialEq)]
93 pub struct CompiledExpression {
94 parts: Vec<CompiledExpressionPart>,
95 need_deref: bool,
96 }
97
98 impl CompiledExpression {
vmctx() -> CompiledExpression99 pub fn vmctx() -> CompiledExpression {
100 CompiledExpression::from_label(get_vmctx_value_label())
101 }
102
from_label(label: ValueLabel) -> CompiledExpression103 pub fn from_label(label: ValueLabel) -> CompiledExpression {
104 CompiledExpression {
105 parts: vec![CompiledExpressionPart::Local {
106 label,
107 trailing: true,
108 }],
109 need_deref: false,
110 }
111 }
112 }
113
114 const X86_64_STACK_OFFSET: i64 = 16;
115
translate_loc( loc: ValueLoc, frame_info: Option<&FunctionFrameInfo>, isa: &dyn TargetIsa, add_stack_value: bool, ) -> Result<Option<Vec<u8>>>116 fn translate_loc(
117 loc: ValueLoc,
118 frame_info: Option<&FunctionFrameInfo>,
119 isa: &dyn TargetIsa,
120 add_stack_value: bool,
121 ) -> Result<Option<Vec<u8>>> {
122 Ok(match loc {
123 ValueLoc::Reg(reg) if add_stack_value => {
124 let machine_reg = isa.map_dwarf_register(reg)?;
125 let mut writer = ExpressionWriter::new();
126 writer.write_op_reg(machine_reg)?;
127 Some(writer.into_vec())
128 }
129 ValueLoc::Reg(reg) => {
130 assert!(!add_stack_value);
131 let machine_reg = isa.map_dwarf_register(reg)?;
132 let mut writer = ExpressionWriter::new();
133 writer.write_op_breg(machine_reg)?;
134 writer.write_sleb128(0)?;
135 Some(writer.into_vec())
136 }
137 ValueLoc::Stack(ss) => {
138 if let Some(frame_info) = frame_info {
139 if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
140 let mut writer = ExpressionWriter::new();
141 writer.write_op_breg(X86_64::RBP.0)?;
142 writer.write_sleb128(ss_offset as i64 + X86_64_STACK_OFFSET)?;
143 if !add_stack_value {
144 writer.write_op(gimli::constants::DW_OP_deref)?;
145 }
146 return Ok(Some(writer.into_vec()));
147 }
148 }
149 None
150 }
151 _ => None,
152 })
153 }
154
append_memory_deref( buf: &mut Vec<u8>, frame_info: &FunctionFrameInfo, vmctx_loc: ValueLoc, isa: &dyn TargetIsa, ) -> Result<bool>155 fn append_memory_deref(
156 buf: &mut Vec<u8>,
157 frame_info: &FunctionFrameInfo,
158 vmctx_loc: ValueLoc,
159 isa: &dyn TargetIsa,
160 ) -> Result<bool> {
161 let mut writer = ExpressionWriter::new();
162 // FIXME for imported memory
163 match vmctx_loc {
164 ValueLoc::Reg(vmctx_reg) => {
165 let reg = isa.map_dwarf_register(vmctx_reg)? as u8;
166 writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg)?;
167 let memory_offset = match frame_info.vmctx_memory_offset() {
168 Some(offset) => offset,
169 None => {
170 return Ok(false);
171 }
172 };
173 writer.write_sleb128(memory_offset)?;
174 }
175 ValueLoc::Stack(ss) => {
176 if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
177 writer.write_op_breg(X86_64::RBP.0)?;
178 writer.write_sleb128(ss_offset as i64 + X86_64_STACK_OFFSET)?;
179 writer.write_op(gimli::constants::DW_OP_deref)?;
180 writer.write_op(gimli::constants::DW_OP_consts)?;
181 let memory_offset = match frame_info.vmctx_memory_offset() {
182 Some(offset) => offset,
183 None => {
184 return Ok(false);
185 }
186 };
187 writer.write_sleb128(memory_offset)?;
188 writer.write_op(gimli::constants::DW_OP_plus)?;
189 } else {
190 return Ok(false);
191 }
192 }
193 _ => {
194 return Ok(false);
195 }
196 }
197 writer.write_op(gimli::constants::DW_OP_deref)?;
198 writer.write_op(gimli::constants::DW_OP_swap)?;
199 writer.write_op(gimli::constants::DW_OP_constu)?;
200 writer.write_uleb128(0xffff_ffff)?;
201 writer.write_op(gimli::constants::DW_OP_and)?;
202 writer.write_op(gimli::constants::DW_OP_plus)?;
203 buf.extend(writer.into_vec());
204 Ok(true)
205 }
206
207 impl CompiledExpression {
is_simple(&self) -> bool208 pub fn is_simple(&self) -> bool {
209 if let [CompiledExpressionPart::Code(_)] = self.parts.as_slice() {
210 true
211 } else {
212 self.parts.is_empty()
213 }
214 }
215
build(&self) -> Option<write::Expression>216 pub fn build(&self) -> Option<write::Expression> {
217 if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() {
218 return Some(write::Expression(code.to_vec()));
219 }
220 // locals found, not supported
221 None
222 }
223
build_with_locals<'a>( &'a self, scope: &'a [(u64, u64)], addr_tr: &'a AddressTransform, frame_info: Option<&'a FunctionFrameInfo>, isa: &'a dyn TargetIsa, ) -> impl Iterator<Item = Result<(write::Address, u64, write::Expression)>> + 'a224 pub fn build_with_locals<'a>(
225 &'a self,
226 scope: &'a [(u64, u64)], // wasm ranges
227 addr_tr: &'a AddressTransform,
228 frame_info: Option<&'a FunctionFrameInfo>,
229 isa: &'a dyn TargetIsa,
230 ) -> impl Iterator<Item = Result<(write::Address, u64, write::Expression)>> + 'a {
231 enum BuildWithLocalsResult<'a> {
232 Empty,
233 Simple(
234 Box<dyn Iterator<Item = (write::Address, u64)> + 'a>,
235 Vec<u8>,
236 ),
237 Ranges(
238 Box<dyn Iterator<Item = Result<(DefinedFuncIndex, usize, usize, Vec<u8>)>> + 'a>,
239 ),
240 }
241 impl Iterator for BuildWithLocalsResult<'_> {
242 type Item = Result<(write::Address, u64, write::Expression)>;
243 fn next(&mut self) -> Option<Self::Item> {
244 match self {
245 BuildWithLocalsResult::Empty => None,
246 BuildWithLocalsResult::Simple(it, code) => it
247 .next()
248 .map(|(addr, len)| Ok((addr, len, write::Expression(code.to_vec())))),
249 BuildWithLocalsResult::Ranges(it) => it.next().map(|r| {
250 r.map(|(func_index, start, end, code_buf)| {
251 (
252 write::Address::Symbol {
253 symbol: func_index.index(),
254 addend: start as i64,
255 },
256 (end - start) as u64,
257 write::Expression(code_buf),
258 )
259 })
260 }),
261 }
262 }
263 }
264
265 if scope.is_empty() {
266 return BuildWithLocalsResult::Empty;
267 }
268
269 // If it a simple DWARF code, no need in locals processing. Just translate
270 // the scope ranges.
271 if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() {
272 return BuildWithLocalsResult::Simple(
273 Box::new(scope.iter().flat_map(move |(wasm_start, wasm_end)| {
274 addr_tr.translate_ranges(*wasm_start, *wasm_end)
275 })),
276 code.clone(),
277 );
278 }
279
280 let vmctx_label = get_vmctx_value_label();
281
282 // Some locals are present, preparing and divided ranges based on the scope
283 // and frame_info data.
284 let mut ranges_builder = ValueLabelRangesBuilder::new(scope, addr_tr, frame_info);
285 for p in self.parts.iter() {
286 match p {
287 CompiledExpressionPart::Code(_) => (),
288 CompiledExpressionPart::Local { label, .. } => ranges_builder.process_label(*label),
289 CompiledExpressionPart::Deref => ranges_builder.process_label(vmctx_label),
290 }
291 }
292 if self.need_deref {
293 ranges_builder.process_label(vmctx_label);
294 }
295 let ranges = ranges_builder.into_ranges();
296
297 return BuildWithLocalsResult::Ranges(Box::new(
298 ranges
299 .into_iter()
300 .map(
301 move |CachedValueLabelRange {
302 func_index,
303 start,
304 end,
305 label_location,
306 }| {
307 // build expression
308 let mut code_buf = Vec::new();
309 macro_rules! deref {
310 () => {
311 if let (Some(vmctx_loc), Some(frame_info)) =
312 (label_location.get(&vmctx_label), frame_info)
313 {
314 if !append_memory_deref(
315 &mut code_buf,
316 frame_info,
317 *vmctx_loc,
318 isa,
319 )? {
320 return Ok(None);
321 }
322 } else {
323 return Ok(None);
324 };
325 };
326 }
327 for part in &self.parts {
328 match part {
329 CompiledExpressionPart::Code(c) => {
330 code_buf.extend_from_slice(c.as_slice())
331 }
332 CompiledExpressionPart::Local { label, trailing } => {
333 let loc =
334 *label_location.get(&label).context("label_location")?;
335 if let Some(expr) =
336 translate_loc(loc, frame_info, isa, *trailing)?
337 {
338 code_buf.extend_from_slice(&expr)
339 } else {
340 return Ok(None);
341 }
342 }
343 CompiledExpressionPart::Deref => deref!(),
344 }
345 }
346 if self.need_deref {
347 deref!();
348 }
349 Ok(Some((func_index, start, end, code_buf)))
350 },
351 )
352 .filter_map(Result::transpose),
353 ));
354 }
355 }
356
is_old_expression_format(buf: &[u8]) -> bool357 fn is_old_expression_format(buf: &[u8]) -> bool {
358 // Heuristic to detect old variable expression format without DW_OP_fbreg:
359 // DW_OP_plus_uconst op must be present, but not DW_OP_fbreg.
360 if buf.contains(&(gimli::constants::DW_OP_fbreg.0 as u8)) {
361 // Stop check if DW_OP_fbreg exist.
362 return false;
363 }
364 buf.contains(&(gimli::constants::DW_OP_plus_uconst.0 as u8))
365 }
366
compile_expression<R>( expr: &Expression<R>, encoding: gimli::Encoding, frame_base: Option<&CompiledExpression>, ) -> Result<Option<CompiledExpression>, Error> where R: Reader,367 pub fn compile_expression<R>(
368 expr: &Expression<R>,
369 encoding: gimli::Encoding,
370 frame_base: Option<&CompiledExpression>,
371 ) -> Result<Option<CompiledExpression>, Error>
372 where
373 R: Reader,
374 {
375 let mut pc = expr.0.clone();
376 let buf = expr.0.to_slice()?;
377 let mut parts = Vec::new();
378 let mut need_deref = false;
379 if is_old_expression_format(&buf) && frame_base.is_some() {
380 // Still supporting old DWARF variable expressions without fbreg.
381 parts.extend_from_slice(&frame_base.unwrap().parts);
382 if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() {
383 *trailing = false;
384 }
385 need_deref = frame_base.unwrap().need_deref;
386 }
387 let base_len = parts.len();
388 let mut code_chunk = Vec::new();
389 macro_rules! flush_code_chunk {
390 () => {
391 if !code_chunk.is_empty() {
392 parts.push(CompiledExpressionPart::Code(code_chunk));
393 code_chunk = Vec::new();
394 }
395 };
396 };
397 while !pc.is_empty() {
398 let next = buf[pc.offset_from(&expr.0).into_u64() as usize];
399 need_deref = true;
400 if next == 0xED {
401 // WebAssembly DWARF extension
402 pc.read_u8()?;
403 let ty = pc.read_uleb128()?;
404 // Supporting only wasm locals.
405 if ty != 0 {
406 // TODO support wasm globals?
407 return Ok(None);
408 }
409 let index = pc.read_sleb128()?;
410 flush_code_chunk!();
411 let label = ValueLabel::from_u32(index as u32);
412 parts.push(CompiledExpressionPart::Local {
413 label,
414 trailing: false,
415 });
416 } else {
417 let pos = pc.offset_from(&expr.0).into_u64() as usize;
418 let op = Operation::parse(&mut pc, &expr.0, encoding)?;
419 match op {
420 Operation::FrameOffset { offset } => {
421 // Expand DW_OP_fpreg into frame location and DW_OP_plus_uconst.
422 if frame_base.is_some() {
423 // Add frame base expressions.
424 flush_code_chunk!();
425 parts.extend_from_slice(&frame_base.unwrap().parts);
426 }
427 if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() {
428 // Reset local trailing flag.
429 *trailing = false;
430 }
431 // Append DW_OP_plus_uconst part.
432 let mut writer = ExpressionWriter::new();
433 writer.write_op(gimli::constants::DW_OP_plus_uconst)?;
434 writer.write_uleb128(offset as u64)?;
435 code_chunk.extend(writer.into_vec());
436 continue;
437 }
438 Operation::Literal { .. }
439 | Operation::PlusConstant { .. }
440 | Operation::Piece { .. } => (),
441 Operation::StackValue => {
442 need_deref = false;
443
444 // Find extra stack_value, that follow wasm-local operators,
445 // and mark such locals with special flag.
446 if let (Some(CompiledExpressionPart::Local { trailing, .. }), true) =
447 (parts.last_mut(), code_chunk.is_empty())
448 {
449 *trailing = true;
450 continue;
451 }
452 }
453 Operation::Deref { .. } => {
454 flush_code_chunk!();
455 parts.push(CompiledExpressionPart::Deref);
456 }
457 _ => {
458 return Ok(None);
459 }
460 }
461 let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize];
462 code_chunk.extend_from_slice(chunk);
463 }
464 }
465
466 if !code_chunk.is_empty() {
467 parts.push(CompiledExpressionPart::Code(code_chunk));
468 }
469
470 if base_len > 0 && base_len + 1 < parts.len() {
471 // see if we can glue two code chunks
472 if let [CompiledExpressionPart::Code(cc1), CompiledExpressionPart::Code(cc2)] =
473 &parts[base_len..=base_len]
474 {
475 let mut combined = cc1.clone();
476 combined.extend_from_slice(cc2);
477 parts[base_len] = CompiledExpressionPart::Code(combined);
478 parts.remove(base_len + 1);
479 }
480 }
481
482 Ok(Some(CompiledExpression { parts, need_deref }))
483 }
484
485 #[derive(Debug, Clone)]
486 struct CachedValueLabelRange {
487 func_index: DefinedFuncIndex,
488 start: usize,
489 end: usize,
490 label_location: HashMap<ValueLabel, ValueLoc>,
491 }
492
493 struct ValueLabelRangesBuilder<'a, 'b> {
494 ranges: Vec<CachedValueLabelRange>,
495 frame_info: Option<&'a FunctionFrameInfo<'b>>,
496 processed_labels: HashSet<ValueLabel>,
497 }
498
499 impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> {
new( scope: &[(u64, u64)], addr_tr: &'a AddressTransform, frame_info: Option<&'a FunctionFrameInfo<'b>>, ) -> Self500 pub fn new(
501 scope: &[(u64, u64)], // wasm ranges
502 addr_tr: &'a AddressTransform,
503 frame_info: Option<&'a FunctionFrameInfo<'b>>,
504 ) -> Self {
505 let mut ranges = Vec::new();
506 for (wasm_start, wasm_end) in scope {
507 if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(*wasm_start, *wasm_end) {
508 ranges.extend(tr.into_iter().map(|(start, end)| CachedValueLabelRange {
509 func_index,
510 start,
511 end,
512 label_location: HashMap::new(),
513 }));
514 }
515 }
516 ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start));
517 ValueLabelRangesBuilder {
518 ranges,
519 frame_info,
520 processed_labels: HashSet::new(),
521 }
522 }
523
process_label(&mut self, label: ValueLabel)524 fn process_label(&mut self, label: ValueLabel) {
525 if self.processed_labels.contains(&label) {
526 return;
527 }
528 self.processed_labels.insert(label);
529
530 let value_ranges = match self.frame_info.and_then(|fi| fi.value_ranges.get(&label)) {
531 Some(value_ranges) => value_ranges,
532 None => {
533 return;
534 }
535 };
536
537 let ranges = &mut self.ranges;
538 for value_range in value_ranges {
539 let range_start = value_range.start as usize;
540 let range_end = value_range.end as usize;
541 let loc = value_range.loc;
542 if range_start == range_end {
543 continue;
544 }
545 assert_lt!(range_start, range_end);
546
547 // Find acceptable scope of ranges to intersect with.
548 let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) {
549 Ok(i) => i,
550 Err(i) => {
551 if i > 0 && range_start < ranges[i - 1].end {
552 i - 1
553 } else {
554 i
555 }
556 }
557 };
558 let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) {
559 Ok(i) | Err(i) => i,
560 };
561 // Starting from the end, intersect (range_start..range_end) with
562 // self.ranges array.
563 for i in (i..j).rev() {
564 if range_end <= ranges[i].start || ranges[i].end <= range_start {
565 continue;
566 }
567 if range_end < ranges[i].end {
568 // Cutting some of the range from the end.
569 let mut tail = ranges[i].clone();
570 ranges[i].end = range_end;
571 tail.start = range_end;
572 ranges.insert(i + 1, tail);
573 }
574 assert_le!(ranges[i].end, range_end);
575 if range_start <= ranges[i].start {
576 ranges[i].label_location.insert(label, loc);
577 continue;
578 }
579 // Cutting some of the range from the start.
580 let mut tail = ranges[i].clone();
581 ranges[i].end = range_start;
582 tail.start = range_start;
583 tail.label_location.insert(label, loc);
584 ranges.insert(i + 1, tail);
585 }
586 }
587 }
588
into_ranges(self) -> impl Iterator<Item = CachedValueLabelRange>589 pub fn into_ranges(self) -> impl Iterator<Item = CachedValueLabelRange> {
590 // Ranges with not-enough labels are discarded.
591 let processed_labels_len = self.processed_labels.len();
592 self.ranges
593 .into_iter()
594 .filter(move |r| r.label_location.len() == processed_labels_len)
595 }
596 }
597
598 #[cfg(test)]
599 mod tests {
600 use super::compile_expression;
601 use super::{AddressTransform, FunctionFrameInfo, ValueLabel, ValueLabelsRanges};
602 use gimli::{self, Encoding, EndianSlice, Expression, RunTimeEndian};
603
604 macro_rules! expression {
605 ($($i:literal),*) => {
606 Expression(EndianSlice::new(
607 &[$($i),*],
608 RunTimeEndian::Little,
609 ))
610 }
611 }
612
613 static DWARF_ENCODING: Encoding = Encoding {
614 address_size: 4,
615 format: gimli::Format::Dwarf32,
616 version: 4,
617 };
618
619 #[test]
test_debug_parse_expressions()620 fn test_debug_parse_expressions() {
621 use super::{CompiledExpression, CompiledExpressionPart};
622 use wasmtime_environ::entity::EntityRef;
623
624 let (val1, val3, val20) = (ValueLabel::new(1), ValueLabel::new(3), ValueLabel::new(20));
625
626 // DW_OP_WASM_location 0x0 +20, DW_OP_stack_value
627 let e = expression!(0xed, 0x00, 0x14, 0x9f);
628 let ce = compile_expression(&e, DWARF_ENCODING, None)
629 .expect("non-error")
630 .expect("expression");
631 assert_eq!(
632 ce,
633 CompiledExpression {
634 parts: vec![CompiledExpressionPart::Local {
635 label: val20,
636 trailing: true
637 }],
638 need_deref: false
639 }
640 );
641
642 // DW_OP_WASM_location 0x0 +1, DW_OP_plus_uconst 0x10, DW_OP_stack_value
643 let e = expression!(0xed, 0x00, 0x01, 0x23, 0x10, 0x9f);
644 let ce = compile_expression(&e, DWARF_ENCODING, None)
645 .expect("non-error")
646 .expect("expression");
647 assert_eq!(
648 ce,
649 CompiledExpression {
650 parts: vec![
651 CompiledExpressionPart::Local {
652 label: val1,
653 trailing: false
654 },
655 CompiledExpressionPart::Code(vec![35, 16, 159])
656 ],
657 need_deref: false
658 }
659 );
660
661 // Frame base: DW_OP_WASM_location 0x0 +3, DW_OP_stack_value
662 let e = expression!(0xed, 0x00, 0x03, 0x9f);
663 let fe = compile_expression(&e, DWARF_ENCODING, None).expect("non-error");
664 // DW_OP_fpreg 0x12
665 let e = expression!(0x91, 0x12);
666 let ce = compile_expression(&e, DWARF_ENCODING, fe.as_ref())
667 .expect("non-error")
668 .expect("expression");
669 assert_eq!(
670 ce,
671 CompiledExpression {
672 parts: vec![
673 CompiledExpressionPart::Local {
674 label: val3,
675 trailing: false
676 },
677 CompiledExpressionPart::Code(vec![35, 18])
678 ],
679 need_deref: true
680 }
681 );
682 }
683
create_mock_address_transform() -> AddressTransform684 fn create_mock_address_transform() -> AddressTransform {
685 use crate::read_debuginfo::WasmFileInfo;
686 use wasmtime_environ::entity::PrimaryMap;
687 use wasmtime_environ::ir::SourceLoc;
688 use wasmtime_environ::{FunctionAddressMap, InstructionAddressMap};
689 let mut module_map = PrimaryMap::new();
690 let code_section_offset: u32 = 100;
691 module_map.push(FunctionAddressMap {
692 instructions: vec![
693 InstructionAddressMap {
694 srcloc: SourceLoc::new(code_section_offset + 12),
695 code_offset: 5,
696 code_len: 3,
697 },
698 InstructionAddressMap {
699 srcloc: SourceLoc::new(code_section_offset + 17),
700 code_offset: 15,
701 code_len: 8,
702 },
703 ],
704 start_srcloc: SourceLoc::new(code_section_offset + 10),
705 end_srcloc: SourceLoc::new(code_section_offset + 20),
706 body_offset: 0,
707 body_len: 30,
708 });
709 let fi = WasmFileInfo {
710 code_section_offset: code_section_offset.into(),
711 funcs: Box::new([]),
712 imported_func_count: 0,
713 path: None,
714 };
715 AddressTransform::new(&module_map, &fi)
716 }
717
create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel))718 fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) {
719 use std::collections::HashMap;
720 use wasmtime_environ::entity::EntityRef;
721 use wasmtime_environ::ir::{ValueLoc, ValueLocRange};
722 let mut value_ranges = HashMap::new();
723 let value_0 = ValueLabel::new(0);
724 let value_1 = ValueLabel::new(1);
725 let value_2 = ValueLabel::new(2);
726 value_ranges.insert(
727 value_0,
728 vec![ValueLocRange {
729 loc: ValueLoc::Unassigned,
730 start: 0,
731 end: 25,
732 }],
733 );
734 value_ranges.insert(
735 value_1,
736 vec![ValueLocRange {
737 loc: ValueLoc::Unassigned,
738 start: 5,
739 end: 30,
740 }],
741 );
742 value_ranges.insert(
743 value_2,
744 vec![
745 ValueLocRange {
746 loc: ValueLoc::Unassigned,
747 start: 0,
748 end: 10,
749 },
750 ValueLocRange {
751 loc: ValueLoc::Unassigned,
752 start: 20,
753 end: 30,
754 },
755 ],
756 );
757 (value_ranges, (value_0, value_1, value_2))
758 }
759
760 #[test]
test_debug_value_range_builder()761 fn test_debug_value_range_builder() {
762 use super::ValueLabelRangesBuilder;
763 use wasmtime_environ::entity::EntityRef;
764 use wasmtime_environ::ir::StackSlots;
765 use wasmtime_environ::wasm::DefinedFuncIndex;
766 use wasmtime_environ::ModuleMemoryOffset;
767
768 let addr_tr = create_mock_address_transform();
769 let stack_slots = StackSlots::new();
770 let (value_ranges, value_labels) = create_mock_value_ranges();
771 let fi = FunctionFrameInfo {
772 memory_offset: ModuleMemoryOffset::None,
773 stack_slots: &stack_slots,
774 value_ranges: &value_ranges,
775 };
776
777 // No value labels, testing if entire function range coming through.
778 let builder = ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi));
779 let ranges = builder.into_ranges().collect::<Vec<_>>();
780 assert_eq!(ranges.len(), 1);
781 assert_eq!(ranges[0].func_index, DefinedFuncIndex::new(0));
782 assert_eq!(ranges[0].start, 0);
783 assert_eq!(ranges[0].end, 30);
784
785 // Two labels (val0@0..25 and val1@5..30), their common lifetime intersect at 5..25.
786 let mut builder = ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi));
787 builder.process_label(value_labels.0);
788 builder.process_label(value_labels.1);
789 let ranges = builder.into_ranges().collect::<Vec<_>>();
790 assert_eq!(ranges.len(), 1);
791 assert_eq!(ranges[0].start, 5);
792 assert_eq!(ranges[0].end, 25);
793
794 // Adds val2 with complex lifetime @0..10 and @20..30 to the previous test, and
795 // also narrows range.
796 let mut builder = ValueLabelRangesBuilder::new(&[(11, 17)], &addr_tr, Some(&fi));
797 builder.process_label(value_labels.0);
798 builder.process_label(value_labels.1);
799 builder.process_label(value_labels.2);
800 let ranges = builder.into_ranges().collect::<Vec<_>>();
801 // Result is two ranges @5..10 and @20..23
802 assert_eq!(ranges.len(), 2);
803 assert_eq!(ranges[0].start, 5);
804 assert_eq!(ranges[0].end, 10);
805 assert_eq!(ranges[1].start, 20);
806 assert_eq!(ranges[1].end, 23);
807 }
808 }
809