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