1 use super::address_transform::AddressTransform;
2 use super::dbi_log;
3 use crate::debug::ModuleMemoryOffset;
4 use crate::debug::transform::debug_transform_logging::{
5     dbi_log_enabled, log_get_value_loc, log_get_value_name, log_get_value_ranges,
6 };
7 use crate::translate::get_vmctx_value_label;
8 use core::fmt;
9 use cranelift_codegen::LabelValueLoc;
10 use cranelift_codegen::ValueLabelsRanges;
11 use cranelift_codegen::ir::ValueLabel;
12 use cranelift_codegen::isa::TargetIsa;
13 use gimli::{Expression, Operation, Reader, ReaderOffset, write};
14 use itertools::Itertools;
15 use std::cmp::PartialEq;
16 use std::collections::{HashMap, HashSet};
17 use std::hash::{Hash, Hasher};
18 use std::rc::Rc;
19 use wasmtime_environ::error::{Context, Error, Result};
20 
21 #[derive(Debug)]
22 pub struct FunctionFrameInfo<'a> {
23     pub value_ranges: &'a ValueLabelsRanges,
24     pub memory_offset: ModuleMemoryOffset,
25 }
26 
27 struct ExpressionWriter(write::EndianVec<gimli::RunTimeEndian>);
28 
29 enum VmctxBase {
30     Reg(u16),
31     OnStack,
32 }
33 
34 impl ExpressionWriter {
new() -> Self35     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     fn write_op(&mut self, op: gimli::DwOp) -> write::Result<()> {
42         self.write_u8(op.0)
43     }
44 
write_op_reg(&mut self, reg: u16) -> write::Result<()>45     fn write_op_reg(&mut self, reg: u16) -> write::Result<()> {
46         if reg < 32 {
47             self.write_u8(gimli::constants::DW_OP_reg0.0 + 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     fn write_op_breg(&mut self, reg: u16) -> write::Result<()> {
55         if reg < 32 {
56             self.write_u8(gimli::constants::DW_OP_breg0.0 + 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     fn write_u8(&mut self, b: u8) -> write::Result<()> {
64         write::Writer::write_u8(&mut self.0, b)
65     }
66 
write_u32(&mut self, b: u32) -> write::Result<()>67     fn write_u32(&mut self, b: u32) -> write::Result<()> {
68         write::Writer::write_u32(&mut self.0, b)
69     }
70 
write_uleb128(&mut self, i: u64) -> write::Result<()>71     fn write_uleb128(&mut self, i: u64) -> write::Result<()> {
72         write::Writer::write_uleb128(&mut self.0, i)
73     }
74 
write_sleb128(&mut self, i: i64) -> write::Result<()>75     fn write_sleb128(&mut self, i: i64) -> write::Result<()> {
76         write::Writer::write_sleb128(&mut self.0, i)
77     }
78 
into_vec(self) -> Vec<u8>79     fn into_vec(self) -> Vec<u8> {
80         self.0.into_vec()
81     }
82 
gen_address_of_memory_base_pointer( &mut self, vmctx: VmctxBase, memory_base: &ModuleMemoryOffset, ) -> write::Result<()>83     fn gen_address_of_memory_base_pointer(
84         &mut self,
85         vmctx: VmctxBase,
86         memory_base: &ModuleMemoryOffset,
87     ) -> write::Result<()> {
88         match *memory_base {
89             ModuleMemoryOffset::Defined(offset) => match vmctx {
90                 VmctxBase::Reg(reg) => {
91                     self.write_op_breg(reg)?;
92                     self.write_sleb128(offset.into())?;
93                 }
94                 VmctxBase::OnStack => {
95                     self.write_op(gimli::constants::DW_OP_consts)?;
96                     self.write_sleb128(offset.into())?;
97                     self.write_op(gimli::constants::DW_OP_plus)?;
98                 }
99             },
100             ModuleMemoryOffset::Imported {
101                 offset_to_vm_memory_definition,
102                 offset_to_memory_base,
103             } => {
104                 match vmctx {
105                     VmctxBase::Reg(reg) => {
106                         self.write_op_breg(reg)?;
107                         self.write_sleb128(offset_to_vm_memory_definition.into())?;
108                     }
109                     VmctxBase::OnStack => {
110                         if offset_to_vm_memory_definition > 0 {
111                             self.write_op(gimli::constants::DW_OP_consts)?;
112                             self.write_sleb128(offset_to_vm_memory_definition.into())?;
113                         }
114                         self.write_op(gimli::constants::DW_OP_plus)?;
115                     }
116                 }
117                 self.write_op(gimli::constants::DW_OP_deref)?;
118                 if offset_to_memory_base > 0 {
119                     self.write_op(gimli::constants::DW_OP_consts)?;
120                     self.write_sleb128(offset_to_memory_base.into())?;
121                     self.write_op(gimli::constants::DW_OP_plus)?;
122                 }
123             }
124             ModuleMemoryOffset::None => return Err(write::Error::InvalidAttributeValue),
125         }
126         Ok(())
127     }
128 }
129 
130 #[derive(Debug, Clone, PartialEq)]
131 enum CompiledExpressionPart {
132     // Untranslated DWARF expression.
133     Code(Vec<u8>),
134     // The wasm-local DWARF operator. The label points to `ValueLabel`.
135     // The trailing field denotes that the operator was last in sequence,
136     // and it is the DWARF location (not a pointer).
137     Local {
138         label: ValueLabel,
139         trailing: bool,
140     },
141     // Dereference is needed.
142     Deref,
143     // Jumping in the expression.
144     Jump {
145         conditionally: bool,
146         target: JumpTargetMarker,
147     },
148     // Floating landing pad.
149     LandingPad(JumpTargetMarker),
150 }
151 
152 #[derive(Debug, Clone, PartialEq)]
153 pub struct CompiledExpression {
154     parts: Vec<CompiledExpressionPart>,
155     need_deref: bool,
156 }
157 
158 impl CompiledExpression {
vmctx() -> CompiledExpression159     pub fn vmctx() -> CompiledExpression {
160         CompiledExpression::from_label(get_vmctx_value_label())
161     }
162 
from_label(label: ValueLabel) -> CompiledExpression163     pub fn from_label(label: ValueLabel) -> CompiledExpression {
164         CompiledExpression {
165             parts: vec![CompiledExpressionPart::Local {
166                 label,
167                 trailing: true,
168             }],
169             need_deref: false,
170         }
171     }
172 }
173 
translate_loc( loc: LabelValueLoc, isa: &dyn TargetIsa, add_stack_value: bool, ) -> Result<Option<Vec<u8>>>174 fn translate_loc(
175     loc: LabelValueLoc,
176     isa: &dyn TargetIsa,
177     add_stack_value: bool,
178 ) -> Result<Option<Vec<u8>>> {
179     Ok(match loc {
180         LabelValueLoc::Reg(r) => {
181             let machine_reg = isa.map_regalloc_reg_to_dwarf(r)?;
182             let mut writer = ExpressionWriter::new();
183             if add_stack_value {
184                 writer.write_op_reg(machine_reg)?;
185             } else {
186                 writer.write_op_breg(machine_reg)?;
187                 writer.write_sleb128(0)?;
188             }
189             Some(writer.into_vec())
190         }
191         LabelValueLoc::CFAOffset(off) => {
192             let mut writer = ExpressionWriter::new();
193             writer.write_op(gimli::constants::DW_OP_fbreg)?;
194             writer.write_sleb128(off)?;
195             if !add_stack_value {
196                 writer.write_op(gimli::constants::DW_OP_deref)?;
197             }
198             return Ok(Some(writer.into_vec()));
199         }
200     })
201 }
202 
append_memory_deref( buf: &mut Vec<u8>, frame_info: &FunctionFrameInfo, vmctx_loc: LabelValueLoc, isa: &dyn TargetIsa, ) -> Result<bool>203 fn append_memory_deref(
204     buf: &mut Vec<u8>,
205     frame_info: &FunctionFrameInfo,
206     vmctx_loc: LabelValueLoc,
207     isa: &dyn TargetIsa,
208 ) -> Result<bool> {
209     let mut writer = ExpressionWriter::new();
210     let vmctx_base = match vmctx_loc {
211         LabelValueLoc::Reg(r) => VmctxBase::Reg(isa.map_regalloc_reg_to_dwarf(r)?),
212         LabelValueLoc::CFAOffset(off) => {
213             writer.write_op(gimli::constants::DW_OP_fbreg)?;
214             writer.write_sleb128(off)?;
215             writer.write_op(gimli::constants::DW_OP_deref)?;
216             VmctxBase::OnStack
217         }
218     };
219     writer.gen_address_of_memory_base_pointer(vmctx_base, &frame_info.memory_offset)?;
220     writer.write_op(gimli::constants::DW_OP_deref)?;
221     writer.write_op(gimli::constants::DW_OP_swap)?;
222     writer.write_op(gimli::constants::DW_OP_const4u)?;
223     writer.write_u32(0xffff_ffff)?;
224     writer.write_op(gimli::constants::DW_OP_and)?;
225     writer.write_op(gimli::constants::DW_OP_plus)?;
226     buf.extend(writer.into_vec());
227     Ok(true)
228 }
229 
230 pub struct BuiltCompiledExpression<TIter> {
231     pub expressions: TIter,
232     pub covers_entire_scope: bool,
233 }
234 
235 impl CompiledExpression {
is_simple(&self) -> bool236     pub fn is_simple(&self) -> bool {
237         if let [CompiledExpressionPart::Code(_)] = self.parts.as_slice() {
238             true
239         } else {
240             self.parts.is_empty()
241         }
242     }
243 
build(&self) -> Option<write::Expression>244     pub fn build(&self) -> Option<write::Expression> {
245         if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() {
246             return Some(write::Expression::raw(code.to_vec()));
247         }
248         // locals found, not supported
249         None
250     }
251 
build_with_locals<'a>( &'a self, scope: &'a [(u64, u64)], addr_tr: &'a AddressTransform, frame_info: Option<&'a FunctionFrameInfo>, isa: &'a dyn TargetIsa, ) -> BuiltCompiledExpression< impl Iterator<Item = Result<(write::Address, u64, write::Expression)>> + use<'a>, >252     pub fn build_with_locals<'a>(
253         &'a self,
254         scope: &'a [(u64, u64)], // wasm ranges
255         addr_tr: &'a AddressTransform,
256         frame_info: Option<&'a FunctionFrameInfo>,
257         isa: &'a dyn TargetIsa,
258     ) -> BuiltCompiledExpression<
259         impl Iterator<Item = Result<(write::Address, u64, write::Expression)>> + use<'a>,
260     > {
261         enum BuildWithLocalsResult<'a> {
262             Empty,
263             Simple(
264                 Box<dyn Iterator<Item = (write::Address, u64)> + 'a>,
265                 Vec<u8>,
266             ),
267             Ranges(Box<dyn Iterator<Item = Result<(usize, usize, usize, Vec<u8>)>> + 'a>),
268         }
269         impl Iterator for BuildWithLocalsResult<'_> {
270             type Item = Result<(write::Address, u64, write::Expression)>;
271             fn next(&mut self) -> Option<Self::Item> {
272                 match self {
273                     BuildWithLocalsResult::Empty => None,
274                     BuildWithLocalsResult::Simple(it, code) => it
275                         .next()
276                         .map(|(addr, len)| Ok((addr, len, write::Expression::raw(code.to_vec())))),
277                     BuildWithLocalsResult::Ranges(it) => it.next().map(|r| {
278                         r.map(|(symbol, start, end, code_buf)| {
279                             (
280                                 write::Address::Symbol {
281                                     symbol,
282                                     addend: start as i64,
283                                 },
284                                 (end - start) as u64,
285                                 write::Expression::raw(code_buf),
286                             )
287                         })
288                     }),
289                 }
290             }
291         }
292 
293         if scope.is_empty() {
294             return BuiltCompiledExpression {
295                 expressions: BuildWithLocalsResult::Empty,
296                 covers_entire_scope: false,
297             };
298         }
299 
300         // If it a simple DWARF code, no need in locals processing. Just translate
301         // the scope ranges.
302         if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() {
303             return BuiltCompiledExpression {
304                 expressions: BuildWithLocalsResult::Simple(
305                     Box::new(scope.iter().flat_map(move |(wasm_start, wasm_end)| {
306                         addr_tr.translate_ranges(*wasm_start, *wasm_end)
307                     })),
308                     code.clone(),
309                 ),
310                 covers_entire_scope: false,
311             };
312         }
313 
314         let vmctx_label = get_vmctx_value_label();
315 
316         // Some locals are present, preparing and divided ranges based on the scope
317         // and frame_info data.
318         let mut ranges_builder = ValueLabelRangesBuilder::new(scope, addr_tr, frame_info, isa);
319         for p in self.parts.iter() {
320             match p {
321                 CompiledExpressionPart::Code(_)
322                 | CompiledExpressionPart::Jump { .. }
323                 | CompiledExpressionPart::LandingPad { .. } => (),
324                 CompiledExpressionPart::Local { label, .. } => ranges_builder.process_label(*label),
325                 CompiledExpressionPart::Deref => ranges_builder.process_label(vmctx_label),
326             }
327         }
328         if self.need_deref {
329             ranges_builder.process_label(vmctx_label);
330         }
331 
332         let ranges = ranges_builder.into_ranges();
333         let expressions = BuildWithLocalsResult::Ranges(Box::new(
334             ranges
335                 .ranges
336                 .map(
337                     move |CachedValueLabelRange {
338                               func_index,
339                               start,
340                               end,
341                               label_location,
342                           }| {
343                         // build expression
344                         let mut code_buf = Vec::new();
345                         let mut jump_positions = Vec::new();
346                         let mut landing_positions = HashMap::new();
347 
348                         macro_rules! deref {
349                             () => {
350                                 if let (Some(vmctx_loc), Some(frame_info)) =
351                                     (label_location.get(&vmctx_label), frame_info)
352                                 {
353                                     if !append_memory_deref(
354                                         &mut code_buf,
355                                         frame_info,
356                                         *vmctx_loc,
357                                         isa,
358                                     )? {
359                                         return Ok(None);
360                                     }
361                                 } else {
362                                     return Ok(None);
363                                 }
364                             };
365                         }
366                         for part in &self.parts {
367                             match part {
368                                 CompiledExpressionPart::Code(c) => {
369                                     code_buf.extend_from_slice(c.as_slice())
370                                 }
371                                 CompiledExpressionPart::LandingPad(marker) => {
372                                     landing_positions.insert(marker.clone(), code_buf.len());
373                                 }
374                                 CompiledExpressionPart::Jump {
375                                     conditionally,
376                                     target,
377                                 } => {
378                                     code_buf.push(
379                                         match conditionally {
380                                             true => gimli::constants::DW_OP_bra,
381                                             false => gimli::constants::DW_OP_skip,
382                                         }
383                                         .0,
384                                     );
385                                     code_buf.push(!0);
386                                     code_buf.push(!0); // these will be relocated below
387                                     jump_positions.push((target.clone(), code_buf.len()));
388                                 }
389                                 CompiledExpressionPart::Local { label, trailing } => {
390                                     let loc =
391                                         *label_location.get(&label).context("label_location")?;
392                                     if let Some(expr) = translate_loc(loc, isa, *trailing)? {
393                                         code_buf.extend_from_slice(&expr)
394                                     } else {
395                                         return Ok(None);
396                                     }
397                                 }
398                                 CompiledExpressionPart::Deref => deref!(),
399                             }
400                         }
401                         if self.need_deref {
402                             deref!();
403                         }
404 
405                         for (marker, new_from) in jump_positions {
406                             // relocate jump targets
407                             let new_to = landing_positions[&marker];
408                             let new_diff = new_to as isize - new_from as isize;
409                             // FIXME: use encoding? LittleEndian for now...
410                             code_buf[new_from - 2..new_from]
411                                 .copy_from_slice(&(new_diff as i16).to_le_bytes());
412                         }
413                         Ok(Some((func_index, start, end, code_buf)))
414                     },
415                 )
416                 .filter_map(Result::transpose),
417         ));
418 
419         BuiltCompiledExpression {
420             expressions,
421             covers_entire_scope: ranges.covers_entire_scope,
422         }
423     }
424 }
425 
is_old_expression_format(buf: &[u8]) -> bool426 fn is_old_expression_format(buf: &[u8]) -> bool {
427     // Heuristic to detect old variable expression format without DW_OP_fbreg:
428     // DW_OP_plus_uconst op must be present, but not DW_OP_fbreg.
429     if buf.contains(&(gimli::constants::DW_OP_fbreg.0)) {
430         // Stop check if DW_OP_fbreg exist.
431         return false;
432     }
433     buf.contains(&(gimli::constants::DW_OP_plus_uconst.0))
434 }
435 
compile_expression<R>( expr: &Expression<R>, encoding: gimli::Encoding, frame_base: Option<&CompiledExpression>, ) -> Result<Option<CompiledExpression>, Error> where R: Reader,436 pub fn compile_expression<R>(
437     expr: &Expression<R>,
438     encoding: gimli::Encoding,
439     frame_base: Option<&CompiledExpression>,
440 ) -> Result<Option<CompiledExpression>, Error>
441 where
442     R: Reader,
443 {
444     // Bail when `frame_base` is complicated.
445     if let Some(expr) = frame_base {
446         if expr.parts.iter().any(|p| match p {
447             CompiledExpressionPart::Jump { .. } => true,
448             _ => false,
449         }) {
450             return Ok(None);
451         }
452     }
453 
454     // jump_targets key is offset in buf starting from the end
455     // (see also `unread_bytes` below)
456     let mut jump_targets: HashMap<u64, JumpTargetMarker> = HashMap::new();
457     let mut pc = expr.0.clone();
458 
459     let buf = expr.0.to_slice()?;
460     let mut parts = Vec::new();
461     macro_rules! push {
462         ($part:expr) => {{
463             let part = $part;
464             if let (CompiledExpressionPart::Code(cc2), Some(CompiledExpressionPart::Code(cc1))) =
465                 (&part, parts.last_mut())
466             {
467                 cc1.extend_from_slice(cc2);
468             } else {
469                 parts.push(part)
470             }
471         }};
472     }
473     let mut need_deref = false;
474     if is_old_expression_format(&buf) && frame_base.is_some() {
475         // Still supporting old DWARF variable expressions without fbreg.
476         parts.extend_from_slice(&frame_base.unwrap().parts);
477         if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() {
478             *trailing = false;
479         }
480         need_deref = frame_base.unwrap().need_deref;
481     }
482     let mut code_chunk = Vec::new();
483     macro_rules! flush_code_chunk {
484         () => {
485             if !code_chunk.is_empty() {
486                 push!(CompiledExpressionPart::Code(code_chunk));
487                 code_chunk = Vec::new();
488                 let _ = code_chunk; // suppresses warning for final flush
489             }
490         };
491     }
492 
493     // Find all landing pads by scanning bytes, do not care about
494     // false location at this moment.
495     // Looks hacky but it is fast; does not need to be really exact.
496     if buf.len() > 2 {
497         for i in 0..buf.len() - 2 {
498             let op = buf[i];
499             if op == gimli::constants::DW_OP_bra.0 || op == gimli::constants::DW_OP_skip.0 {
500                 // TODO fix for big-endian
501                 let offset = i16::from_le_bytes([buf[i + 1], buf[i + 2]]);
502                 let origin = i + 3;
503                 // Discarding out-of-bounds jumps (also some of falsely detected ops)
504                 if (offset >= 0 && offset as usize + origin <= buf.len())
505                     || (offset < 0 && -offset as usize <= origin)
506                 {
507                     let target = buf.len() as isize - origin as isize - offset as isize;
508                     jump_targets.insert(target as u64, JumpTargetMarker::new());
509                 }
510             }
511         }
512     }
513 
514     while !pc.is_empty() {
515         let unread_bytes = pc.len().into_u64();
516         if let Some(marker) = jump_targets.get(&unread_bytes) {
517             flush_code_chunk!();
518             parts.push(CompiledExpressionPart::LandingPad(marker.clone()));
519         }
520 
521         need_deref = true;
522 
523         let pos = pc.offset_from(&expr.0).into_u64() as usize;
524         let op = Operation::parse(&mut pc, encoding)?;
525         match op {
526             Operation::FrameOffset { offset } => {
527                 // Expand DW_OP_fbreg into frame location and DW_OP_plus_uconst.
528                 if frame_base.is_some() {
529                     // Add frame base expressions.
530                     flush_code_chunk!();
531                     parts.extend_from_slice(&frame_base.unwrap().parts);
532                 }
533                 if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() {
534                     // Reset local trailing flag.
535                     *trailing = false;
536                 }
537                 // Append DW_OP_plus_uconst part.
538                 let mut writer = ExpressionWriter::new();
539                 writer.write_op(gimli::constants::DW_OP_plus_uconst)?;
540                 writer.write_uleb128(offset as u64)?;
541                 code_chunk.extend(writer.into_vec());
542                 continue;
543             }
544             Operation::Drop { .. }
545             | Operation::Pick { .. }
546             | Operation::Swap { .. }
547             | Operation::Rot { .. }
548             | Operation::Nop { .. }
549             | Operation::UnsignedConstant { .. }
550             | Operation::SignedConstant { .. }
551             | Operation::ConstantIndex { .. }
552             | Operation::PlusConstant { .. }
553             | Operation::Abs { .. }
554             | Operation::And { .. }
555             | Operation::Or { .. }
556             | Operation::Xor { .. }
557             | Operation::Shl { .. }
558             | Operation::Plus { .. }
559             | Operation::Minus { .. }
560             | Operation::Div { .. }
561             | Operation::Mod { .. }
562             | Operation::Mul { .. }
563             | Operation::Neg { .. }
564             | Operation::Not { .. }
565             | Operation::Lt { .. }
566             | Operation::Gt { .. }
567             | Operation::Le { .. }
568             | Operation::Ge { .. }
569             | Operation::Eq { .. }
570             | Operation::Ne { .. }
571             | Operation::TypedLiteral { .. }
572             | Operation::Convert { .. }
573             | Operation::Reinterpret { .. }
574             | Operation::Piece { .. } => (),
575             Operation::Bra { target } | Operation::Skip { target } => {
576                 flush_code_chunk!();
577                 let arc_to = (pc.len().into_u64() as isize - target as isize) as u64;
578                 let marker = match jump_targets.get(&arc_to) {
579                     Some(m) => m.clone(),
580                     None => {
581                         // Marker not found: probably out of bounds.
582                         return Ok(None);
583                     }
584                 };
585                 push!(CompiledExpressionPart::Jump {
586                     conditionally: match op {
587                         Operation::Bra { .. } => true,
588                         _ => false,
589                     },
590                     target: marker,
591                 });
592                 continue;
593             }
594             Operation::StackValue => {
595                 need_deref = false;
596 
597                 // Find extra stack_value, that follow wasm-local operators,
598                 // and mark such locals with special flag.
599                 if let (Some(CompiledExpressionPart::Local { trailing, .. }), true) =
600                     (parts.last_mut(), code_chunk.is_empty())
601                 {
602                     *trailing = true;
603                     continue;
604                 }
605             }
606             Operation::Deref { .. } => {
607                 flush_code_chunk!();
608                 push!(CompiledExpressionPart::Deref);
609                 // Don't re-enter the loop here (i.e. continue), because the
610                 // DW_OP_deref still needs to be kept.
611             }
612             Operation::WasmLocal { index } => {
613                 flush_code_chunk!();
614                 let label = ValueLabel::from_u32(index);
615                 push!(CompiledExpressionPart::Local {
616                     label,
617                     trailing: false,
618                 });
619                 continue;
620             }
621             Operation::Shr { .. } | Operation::Shra { .. } => {
622                 // Insert value normalisation part.
623                 // The semantic value is 32 bits (TODO: check unit)
624                 // but the target architecture is 64-bits. So we'll
625                 // clean out the upper 32 bits (in a sign-correct way)
626                 // to avoid contamination of the result with randomness.
627                 let mut writer = ExpressionWriter::new();
628                 writer.write_op(gimli::constants::DW_OP_plus_uconst)?;
629                 writer.write_uleb128(32)?; // increase shift amount
630                 writer.write_op(gimli::constants::DW_OP_swap)?;
631                 writer.write_op(gimli::constants::DW_OP_const1u)?;
632                 writer.write_u8(32)?;
633                 writer.write_op(gimli::constants::DW_OP_shl)?;
634                 writer.write_op(gimli::constants::DW_OP_swap)?;
635                 code_chunk.extend(writer.into_vec());
636                 // Don't re-enter the loop here (i.e. continue), because the
637                 // DW_OP_shr* still needs to be kept.
638             }
639             Operation::Address { .. }
640             | Operation::AddressIndex { .. }
641             | Operation::Call { .. }
642             | Operation::Register { .. }
643             | Operation::RegisterOffset { .. }
644             | Operation::CallFrameCFA
645             | Operation::PushObjectAddress
646             | Operation::TLS
647             | Operation::ImplicitValue { .. }
648             | Operation::ImplicitPointer { .. }
649             | Operation::EntryValue { .. }
650             | Operation::ParameterRef { .. }
651             | Operation::VariableValue { .. }
652             | Operation::Uninitialized => {
653                 return Ok(None);
654             }
655             Operation::WasmGlobal { index: _ } | Operation::WasmStack { index: _ } => {
656                 // TODO support those two
657                 return Ok(None);
658             }
659         }
660         let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize];
661         code_chunk.extend_from_slice(chunk);
662     }
663 
664     flush_code_chunk!();
665     if let Some(marker) = jump_targets.get(&0) {
666         parts.push(CompiledExpressionPart::LandingPad(marker.clone()));
667     }
668 
669     Ok(Some(CompiledExpression { parts, need_deref }))
670 }
671 
672 #[derive(Debug, Clone)]
673 struct CachedValueLabelRange {
674     func_index: usize,
675     start: usize,
676     end: usize,
677     label_location: HashMap<ValueLabel, LabelValueLoc>,
678 }
679 
680 struct BuiltRangeSummary<'a> {
681     range: &'a CachedValueLabelRange,
682     isa: &'a dyn TargetIsa,
683 }
684 
685 impl<'a> fmt::Debug for BuiltRangeSummary<'a> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result686     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
687         let range = self.range;
688         write!(f, "[")?;
689         let mut is_first = true;
690         for (value, value_loc) in &range.label_location {
691             if !is_first {
692                 write!(f, ", ")?;
693             } else {
694                 is_first = false;
695             }
696             write!(
697                 f,
698                 "{:?}:{:?}",
699                 log_get_value_name(*value),
700                 log_get_value_loc(*value_loc, self.isa)
701             )?;
702         }
703         write!(f, "]@[{}..{})", range.start, range.end)?;
704         Ok(())
705     }
706 }
707 
708 struct ValueLabelRangesBuilder<'a, 'b> {
709     isa: &'a dyn TargetIsa,
710     ranges: Vec<CachedValueLabelRange>,
711     frame_info: Option<&'a FunctionFrameInfo<'b>>,
712     processed_labels: HashSet<ValueLabel>,
713     covers_entire_scope: bool,
714 }
715 
716 struct BuiltValueLabelRanges<TIter> {
717     ranges: TIter,
718     covers_entire_scope: bool,
719 }
720 
721 impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> {
new( scope: &[(u64, u64)], addr_tr: &'a AddressTransform, frame_info: Option<&'a FunctionFrameInfo<'b>>, isa: &'a dyn TargetIsa, ) -> Self722     pub fn new(
723         scope: &[(u64, u64)], // wasm ranges
724         addr_tr: &'a AddressTransform,
725         frame_info: Option<&'a FunctionFrameInfo<'b>>,
726         isa: &'a dyn TargetIsa,
727     ) -> Self {
728         let mut ranges = Vec::new();
729         for (wasm_start, wasm_end) in scope {
730             if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(*wasm_start, *wasm_end) {
731                 ranges.extend(tr.into_iter().map(|(start, end)| CachedValueLabelRange {
732                     func_index,
733                     start,
734                     end,
735                     label_location: HashMap::new(),
736                 }));
737             }
738         }
739         ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start));
740 
741         dbi_log!(
742             "Building ranges for values in scope: {}\n{:?}",
743             ranges
744                 .iter()
745                 .map(|r| format!("[{}..{})", r.start, r.end))
746                 .join(" "),
747             log_get_value_ranges(frame_info.map(|f| f.value_ranges), isa)
748         );
749         ValueLabelRangesBuilder {
750             isa,
751             ranges,
752             frame_info,
753             processed_labels: HashSet::new(),
754             covers_entire_scope: true,
755         }
756     }
757 
process_label(&mut self, label: ValueLabel)758     fn process_label(&mut self, label: ValueLabel) {
759         if self.processed_labels.contains(&label) {
760             return;
761         }
762         dbi_log!("Intersecting with {:?}", log_get_value_name(label));
763         self.processed_labels.insert(label);
764 
765         let value_ranges = match self.frame_info.and_then(|fi| fi.value_ranges.get(&label)) {
766             Some(value_ranges) => value_ranges,
767             None => {
768                 return;
769             }
770         };
771 
772         let ranges = &mut self.ranges;
773         for value_range in value_ranges {
774             let range_start = value_range.start as usize;
775             let range_end = value_range.end as usize;
776             let loc = value_range.loc;
777             if range_start == range_end {
778                 continue;
779             }
780             assert!(range_start < range_end);
781 
782             // Find acceptable scope of ranges to intersect with.
783             let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) {
784                 Ok(i) => i,
785                 Err(i) => {
786                     if i > 0 && range_start < ranges[i - 1].end {
787                         i - 1
788                     } else {
789                         i
790                     }
791                 }
792             };
793             let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) {
794                 Ok(i) | Err(i) => i,
795             };
796             // Starting from the end, intersect (range_start..range_end) with
797             // self.ranges array.
798             for i in (i..j).rev() {
799                 if range_end <= ranges[i].start || ranges[i].end <= range_start {
800                     continue;
801                 }
802                 if range_end < ranges[i].end {
803                     // Cutting some of the range from the end.
804                     let mut tail = ranges[i].clone();
805                     ranges[i].end = range_end;
806                     tail.start = range_end;
807                     ranges.insert(i + 1, tail);
808                     self.covers_entire_scope = false;
809                 }
810                 assert!(ranges[i].end <= range_end);
811                 if range_start <= ranges[i].start {
812                     ranges[i].label_location.insert(label, loc);
813                     continue;
814                 }
815                 // Cutting some of the range from the start.
816                 let mut tail = ranges[i].clone();
817                 ranges[i].end = range_start;
818                 tail.start = range_start;
819                 tail.label_location.insert(label, loc);
820                 ranges.insert(i + 1, tail);
821                 self.covers_entire_scope = false;
822             }
823         }
824     }
825 
into_ranges( self, ) -> BuiltValueLabelRanges<impl Iterator<Item = CachedValueLabelRange> + use<>>826     pub fn into_ranges(
827         self,
828     ) -> BuiltValueLabelRanges<impl Iterator<Item = CachedValueLabelRange> + use<>> {
829         // Ranges with not-enough labels are discarded.
830         let processed_labels_len = self.processed_labels.len();
831         let is_valid_range =
832             move |r: &CachedValueLabelRange| r.label_location.len() == processed_labels_len;
833 
834         if dbi_log_enabled!() {
835             dbi_log!("Built ranges:");
836             for range in self.ranges.iter().filter(|r| is_valid_range(*r)) {
837                 dbi_log!(
838                     "{:?}",
839                     BuiltRangeSummary {
840                         range,
841                         isa: self.isa
842                     }
843                 );
844             }
845             dbi_log!("");
846         }
847 
848         BuiltValueLabelRanges {
849             ranges: self.ranges.into_iter().filter(is_valid_range),
850             covers_entire_scope: self.covers_entire_scope,
851         }
852     }
853 }
854 
855 /// Marker for tracking incoming jumps.
856 /// Different when created new, and the same when cloned.
857 #[derive(Clone, Eq)]
858 struct JumpTargetMarker(Rc<u32>);
859 
860 impl JumpTargetMarker {
new() -> JumpTargetMarker861     fn new() -> JumpTargetMarker {
862         // Create somewhat unique hash data -- using part of
863         // the pointer of the RcBox.
864         let mut rc = Rc::new(0);
865         let hash_data = rc.as_ref() as *const u32 as usize as u32;
866         *Rc::get_mut(&mut rc).unwrap() = hash_data;
867         JumpTargetMarker(rc)
868     }
869 }
870 
871 impl PartialEq for JumpTargetMarker {
eq(&self, other: &JumpTargetMarker) -> bool872     fn eq(&self, other: &JumpTargetMarker) -> bool {
873         Rc::ptr_eq(&self.0, &other.0)
874     }
875 }
876 
877 impl Hash for JumpTargetMarker {
hash<H: Hasher>(&self, hasher: &mut H)878     fn hash<H: Hasher>(&self, hasher: &mut H) {
879         hasher.write_u32(*self.0);
880     }
881 }
882 impl std::fmt::Debug for JumpTargetMarker {
fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error>883     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
884         write!(
885             f,
886             "JumpMarker<{:08x}>",
887             self.0.as_ref() as *const u32 as usize
888         )
889     }
890 }
891 
892 #[cfg(test)]
893 #[expect(trivial_numeric_casts, reason = "macro-generated code")]
894 mod tests {
895     use super::{
896         AddressTransform, CompiledExpression, CompiledExpressionPart, FunctionFrameInfo,
897         JumpTargetMarker, ValueLabel, ValueLabelsRanges, compile_expression,
898     };
899     use crate::CompiledFunctionMetadata;
900     use cranelift_codegen::{isa::lookup, settings::Flags};
901     use gimli::{Encoding, EndianSlice, Expression, RunTimeEndian, constants};
902     use target_lexicon::triple;
903     use wasmtime_environ::FilePos;
904 
905     macro_rules! dw_op {
906         (DW_OP_WASM_location) => {
907             0xed
908         };
909         ($i:literal) => {
910             $i
911         };
912         ($d:ident) => {
913             constants::$d.0 as u8
914         };
915         ($e:expr) => {
916             $e as u8
917         };
918     }
919 
920     macro_rules! expression {
921         ($($t:tt),*) => {
922             Expression(EndianSlice::new(
923                 &[$(dw_op!($t)),*],
924                 RunTimeEndian::Little,
925             ))
926         }
927     }
928 
find_jump_targets<'a>(ce: &'a CompiledExpression) -> Vec<&'a JumpTargetMarker>929     fn find_jump_targets<'a>(ce: &'a CompiledExpression) -> Vec<&'a JumpTargetMarker> {
930         ce.parts
931             .iter()
932             .filter_map(|p| {
933                 if let CompiledExpressionPart::LandingPad(t) = p {
934                     Some(t)
935                 } else {
936                     None
937                 }
938             })
939             .collect::<Vec<_>>()
940     }
941 
942     static DWARF_ENCODING: Encoding = Encoding {
943         address_size: 4,
944         format: gimli::Format::Dwarf32,
945         version: 4,
946     };
947 
948     #[test]
test_debug_expression_jump_target()949     fn test_debug_expression_jump_target() {
950         let m1 = JumpTargetMarker::new();
951         let m2 = JumpTargetMarker::new();
952         assert!(m1 != m2);
953         assert!(m1 == m1.clone());
954 
955         // Internal hash_data test (theoretically can fail intermittently).
956         assert!(m1.0 != m2.0);
957     }
958 
959     #[test]
test_debug_parse_expressions()960     fn test_debug_parse_expressions() {
961         use cranelift_entity::EntityRef;
962 
963         let (val1, val3, val20) = (ValueLabel::new(1), ValueLabel::new(3), ValueLabel::new(20));
964 
965         let e = expression!(DW_OP_WASM_location, 0x0, 20, DW_OP_stack_value);
966         let ce = compile_expression(&e, DWARF_ENCODING, None)
967             .expect("non-error")
968             .expect("expression");
969         assert_eq!(
970             ce,
971             CompiledExpression {
972                 parts: vec![CompiledExpressionPart::Local {
973                     label: val20,
974                     trailing: true
975                 }],
976                 need_deref: false,
977             }
978         );
979 
980         let e = expression!(
981             DW_OP_WASM_location,
982             0x0,
983             1,
984             DW_OP_plus_uconst,
985             0x10,
986             DW_OP_stack_value
987         );
988         let ce = compile_expression(&e, DWARF_ENCODING, None)
989             .expect("non-error")
990             .expect("expression");
991         assert_eq!(
992             ce,
993             CompiledExpression {
994                 parts: vec![
995                     CompiledExpressionPart::Local {
996                         label: val1,
997                         trailing: false
998                     },
999                     CompiledExpressionPart::Code(vec![35, 16, 159])
1000                 ],
1001                 need_deref: false,
1002             }
1003         );
1004 
1005         let e = expression!(DW_OP_WASM_location, 0x0, 3, DW_OP_stack_value);
1006         let fe = compile_expression(&e, DWARF_ENCODING, None).expect("non-error");
1007         let e = expression!(DW_OP_fbreg, 0x12);
1008         let ce = compile_expression(&e, DWARF_ENCODING, fe.as_ref())
1009             .expect("non-error")
1010             .expect("expression");
1011         assert_eq!(
1012             ce,
1013             CompiledExpression {
1014                 parts: vec![
1015                     CompiledExpressionPart::Local {
1016                         label: val3,
1017                         trailing: false
1018                     },
1019                     CompiledExpressionPart::Code(vec![35, 18])
1020                 ],
1021                 need_deref: true,
1022             }
1023         );
1024 
1025         let e = expression!(
1026             DW_OP_WASM_location,
1027             0x0,
1028             1,
1029             DW_OP_plus_uconst,
1030             5,
1031             DW_OP_deref,
1032             DW_OP_stack_value
1033         );
1034         let ce = compile_expression(&e, DWARF_ENCODING, None)
1035             .expect("non-error")
1036             .expect("expression");
1037         assert_eq!(
1038             ce,
1039             CompiledExpression {
1040                 parts: vec![
1041                     CompiledExpressionPart::Local {
1042                         label: val1,
1043                         trailing: false
1044                     },
1045                     CompiledExpressionPart::Code(vec![35, 5]),
1046                     CompiledExpressionPart::Deref,
1047                     CompiledExpressionPart::Code(vec![6, 159])
1048                 ],
1049                 need_deref: false,
1050             }
1051         );
1052 
1053         let e = expression!(
1054             DW_OP_WASM_location,
1055             0x0,
1056             1,
1057             DW_OP_lit16,
1058             DW_OP_shra,
1059             DW_OP_stack_value
1060         );
1061         let ce = compile_expression(&e, DWARF_ENCODING, None)
1062             .expect("non-error")
1063             .expect("expression");
1064         assert_eq!(
1065             ce,
1066             CompiledExpression {
1067                 parts: vec![
1068                     CompiledExpressionPart::Local {
1069                         label: val1,
1070                         trailing: false
1071                     },
1072                     CompiledExpressionPart::Code(vec![64, 35, 32, 22, 8, 32, 36, 22, 38, 159])
1073                 ],
1074                 need_deref: false,
1075             }
1076         );
1077 
1078         let e = expression!(
1079             DW_OP_lit1,
1080             DW_OP_dup,
1081             DW_OP_WASM_location,
1082             0x0,
1083             1,
1084             DW_OP_and,
1085             DW_OP_bra,
1086             5,
1087             0, // --> pointer
1088             DW_OP_swap,
1089             DW_OP_shr,
1090             DW_OP_skip,
1091             2,
1092             0, // --> done
1093             // pointer:
1094             DW_OP_plus,
1095             DW_OP_deref,
1096             // done:
1097             DW_OP_stack_value
1098         );
1099         let ce = compile_expression(&e, DWARF_ENCODING, None)
1100             .expect("non-error")
1101             .expect("expression");
1102         let targets = find_jump_targets(&ce);
1103         assert_eq!(targets.len(), 2);
1104         assert_eq!(
1105             ce,
1106             CompiledExpression {
1107                 parts: vec![
1108                     CompiledExpressionPart::Code(vec![49, 18]),
1109                     CompiledExpressionPart::Local {
1110                         label: val1,
1111                         trailing: false
1112                     },
1113                     CompiledExpressionPart::Code(vec![26]),
1114                     CompiledExpressionPart::Jump {
1115                         conditionally: true,
1116                         target: targets[0].clone(),
1117                     },
1118                     CompiledExpressionPart::Code(vec![22, 35, 32, 22, 8, 32, 36, 22, 37]),
1119                     CompiledExpressionPart::Jump {
1120                         conditionally: false,
1121                         target: targets[1].clone(),
1122                     },
1123                     CompiledExpressionPart::LandingPad(targets[0].clone()), // capture from
1124                     CompiledExpressionPart::Code(vec![34]),
1125                     CompiledExpressionPart::Deref,
1126                     CompiledExpressionPart::Code(vec![6]),
1127                     CompiledExpressionPart::LandingPad(targets[1].clone()), // capture to
1128                     CompiledExpressionPart::Code(vec![159])
1129                 ],
1130                 need_deref: false,
1131             }
1132         );
1133 
1134         let e = expression!(
1135             DW_OP_lit1,
1136             DW_OP_dup,
1137             DW_OP_bra,
1138             2,
1139             0, // --> target
1140             DW_OP_deref,
1141             DW_OP_lit0,
1142             // target:
1143             DW_OP_stack_value
1144         );
1145         let ce = compile_expression(&e, DWARF_ENCODING, None)
1146             .expect("non-error")
1147             .expect("expression");
1148         let targets = find_jump_targets(&ce);
1149         assert_eq!(targets.len(), 1);
1150         assert_eq!(
1151             ce,
1152             CompiledExpression {
1153                 parts: vec![
1154                     CompiledExpressionPart::Code(vec![49, 18]),
1155                     CompiledExpressionPart::Jump {
1156                         conditionally: true,
1157                         target: targets[0].clone(),
1158                     },
1159                     CompiledExpressionPart::Deref,
1160                     CompiledExpressionPart::Code(vec![6, 48]),
1161                     CompiledExpressionPart::LandingPad(targets[0].clone()), // capture to
1162                     CompiledExpressionPart::Code(vec![159])
1163                 ],
1164                 need_deref: false,
1165             }
1166         );
1167 
1168         let e = expression!(
1169             DW_OP_lit1,
1170             /* loop */ DW_OP_dup,
1171             DW_OP_lit25,
1172             DW_OP_ge,
1173             DW_OP_bra,
1174             5,
1175             0, // --> done
1176             DW_OP_plus_uconst,
1177             1,
1178             DW_OP_skip,
1179             (-11_i8),
1180             (!0), // --> loop
1181             /* done */ DW_OP_stack_value
1182         );
1183         let ce = compile_expression(&e, DWARF_ENCODING, None)
1184             .expect("non-error")
1185             .expect("expression");
1186         let targets = find_jump_targets(&ce);
1187         assert_eq!(targets.len(), 2);
1188         assert_eq!(
1189             ce,
1190             CompiledExpression {
1191                 parts: vec![
1192                     CompiledExpressionPart::Code(vec![49]),
1193                     CompiledExpressionPart::LandingPad(targets[0].clone()),
1194                     CompiledExpressionPart::Code(vec![18, 73, 42]),
1195                     CompiledExpressionPart::Jump {
1196                         conditionally: true,
1197                         target: targets[1].clone(),
1198                     },
1199                     CompiledExpressionPart::Code(vec![35, 1]),
1200                     CompiledExpressionPart::Jump {
1201                         conditionally: false,
1202                         target: targets[0].clone(),
1203                     },
1204                     CompiledExpressionPart::LandingPad(targets[1].clone()),
1205                     CompiledExpressionPart::Code(vec![159])
1206                 ],
1207                 need_deref: false,
1208             }
1209         );
1210 
1211         let e = expression!(DW_OP_WASM_location, 0x0, 1, DW_OP_plus_uconst, 5);
1212         let ce = compile_expression(&e, DWARF_ENCODING, None)
1213             .expect("non-error")
1214             .expect("expression");
1215         assert_eq!(
1216             ce,
1217             CompiledExpression {
1218                 parts: vec![
1219                     CompiledExpressionPart::Local {
1220                         label: val1,
1221                         trailing: false
1222                     },
1223                     CompiledExpressionPart::Code(vec![35, 5])
1224                 ],
1225                 need_deref: true,
1226             }
1227         );
1228     }
1229 
create_mock_address_transform() -> AddressTransform1230     fn create_mock_address_transform() -> AddressTransform {
1231         use crate::FunctionAddressMap;
1232         use cranelift_entity::PrimaryMap;
1233         use wasmtime_environ::InstructionAddressMap;
1234         use wasmtime_environ::WasmFileInfo;
1235 
1236         let mut module_map = PrimaryMap::new();
1237         let code_section_offset: u32 = 100;
1238         let func = CompiledFunctionMetadata {
1239             address_map: FunctionAddressMap {
1240                 instructions: vec![
1241                     InstructionAddressMap {
1242                         srcloc: FilePos::new(code_section_offset + 12),
1243                         code_offset: 5,
1244                     },
1245                     InstructionAddressMap {
1246                         srcloc: FilePos::default(),
1247                         code_offset: 8,
1248                     },
1249                     InstructionAddressMap {
1250                         srcloc: FilePos::new(code_section_offset + 17),
1251                         code_offset: 15,
1252                     },
1253                     InstructionAddressMap {
1254                         srcloc: FilePos::default(),
1255                         code_offset: 23,
1256                     },
1257                 ]
1258                 .into(),
1259                 start_srcloc: FilePos::new(code_section_offset + 10),
1260                 end_srcloc: FilePos::new(code_section_offset + 20),
1261                 body_offset: 0,
1262                 body_len: 30,
1263             },
1264             ..Default::default()
1265         };
1266         module_map.push(&func);
1267         let fi = WasmFileInfo {
1268             code_section_offset: code_section_offset.into(),
1269             funcs: Vec::new(),
1270             imported_func_count: 0,
1271             path: None,
1272         };
1273         AddressTransform::mock(&module_map, fi)
1274     }
1275 
create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel))1276     fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) {
1277         use cranelift_codegen::{LabelValueLoc, ValueLocRange};
1278         use cranelift_entity::EntityRef;
1279         use std::collections::HashMap;
1280         let mut value_ranges = HashMap::new();
1281         let value_0 = ValueLabel::new(0);
1282         let value_1 = ValueLabel::new(1);
1283         let value_2 = ValueLabel::new(2);
1284         value_ranges.insert(
1285             value_0,
1286             vec![ValueLocRange {
1287                 loc: LabelValueLoc::CFAOffset(0),
1288                 start: 0,
1289                 end: 25,
1290             }],
1291         );
1292         value_ranges.insert(
1293             value_1,
1294             vec![ValueLocRange {
1295                 loc: LabelValueLoc::CFAOffset(0),
1296                 start: 5,
1297                 end: 30,
1298             }],
1299         );
1300         value_ranges.insert(
1301             value_2,
1302             vec![
1303                 ValueLocRange {
1304                     loc: LabelValueLoc::CFAOffset(0),
1305                     start: 0,
1306                     end: 10,
1307                 },
1308                 ValueLocRange {
1309                     loc: LabelValueLoc::CFAOffset(0),
1310                     start: 20,
1311                     end: 30,
1312                 },
1313             ],
1314         );
1315         (value_ranges, (value_0, value_1, value_2))
1316     }
1317 
1318     #[test]
test_debug_value_range_builder()1319     fn test_debug_value_range_builder() {
1320         use super::ValueLabelRangesBuilder;
1321         use crate::debug::ModuleMemoryOffset;
1322 
1323         // Ignore this test if cranelift doesn't support the native platform.
1324         if cranelift_native::builder().is_err() {
1325             return;
1326         }
1327 
1328         let isa = lookup(triple!("x86_64"))
1329             .expect("expect x86_64 ISA")
1330             .finish(Flags::new(cranelift_codegen::settings::builder()))
1331             .expect("Creating ISA");
1332 
1333         let addr_tr = create_mock_address_transform();
1334         let (value_ranges, value_labels) = create_mock_value_ranges();
1335         let fi = FunctionFrameInfo {
1336             memory_offset: ModuleMemoryOffset::None,
1337             value_ranges: &value_ranges,
1338         };
1339 
1340         // No value labels, testing if entire function range coming through.
1341         let builder = ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi), isa.as_ref());
1342         let ranges = builder.into_ranges().ranges.collect::<Vec<_>>();
1343         assert_eq!(ranges.len(), 1);
1344         assert_eq!(ranges[0].func_index, 0);
1345         assert_eq!(ranges[0].start, 0);
1346         assert_eq!(ranges[0].end, 30);
1347 
1348         // Two labels ([email protected] and [email protected]), their common lifetime intersect at 5..25.
1349         let mut builder =
1350             ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi), isa.as_ref());
1351         builder.process_label(value_labels.0);
1352         builder.process_label(value_labels.1);
1353         let ranges = builder.into_ranges().ranges.collect::<Vec<_>>();
1354         assert_eq!(ranges.len(), 1);
1355         assert_eq!(ranges[0].start, 5);
1356         assert_eq!(ranges[0].end, 25);
1357 
1358         // Adds val2 with complex lifetime @0..10 and @20..30 to the previous test, and
1359         // also narrows range.
1360         let mut builder =
1361             ValueLabelRangesBuilder::new(&[(11, 17)], &addr_tr, Some(&fi), isa.as_ref());
1362         builder.process_label(value_labels.0);
1363         builder.process_label(value_labels.1);
1364         builder.process_label(value_labels.2);
1365         let ranges = builder.into_ranges().ranges.collect::<Vec<_>>();
1366         // Result is two ranges @5..10 and @20..23
1367         assert_eq!(ranges.len(), 2);
1368         assert_eq!(ranges[0].start, 5);
1369         assert_eq!(ranges[0].end, 10);
1370         assert_eq!(ranges[1].start, 20);
1371         assert_eq!(ranges[1].end, 23);
1372     }
1373 }
1374