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