wasmtime_internal_cranelift/debug/transform/
expression.rs

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