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