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 Code(Vec<u8>),
134 Local {
138 label: ValueLabel,
139 trailing: bool,
140 },
141 Deref,
143 Jump {
145 conditionally: bool,
146 target: JumpTargetMarker,
147 },
148 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 None
250 }
251
252 pub fn build_with_locals<'a>(
253 &'a self,
254 scope: &'a [(u64, u64)], 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 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 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 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); 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 let new_to = landing_positions[&marker];
408 let new_diff = new_to as isize - new_from as isize;
409 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 if buf.contains(&(gimli::constants::DW_OP_fbreg.0)) {
430 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 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 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 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; }
490 };
491 }
492
493 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 let offset = i16::from_le_bytes([buf[i + 1], buf[i + 2]]);
502 let origin = i + 3;
503 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 if frame_base.is_some() {
529 flush_code_chunk!();
531 parts.extend_from_slice(&frame_base.unwrap().parts);
532 }
533 if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() {
534 *trailing = false;
536 }
537 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 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 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 }
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 let mut writer = ExpressionWriter::new();
628 writer.write_op(gimli::constants::DW_OP_plus_uconst)?;
629 writer.write_uleb128(32)?; 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 }
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 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)], 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 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 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 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 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 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#[derive(Clone, Eq)]
856struct JumpTargetMarker(Rc<u32>);
857
858impl JumpTargetMarker {
859 fn new() -> JumpTargetMarker {
860 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 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, DW_OP_swap,
1087 DW_OP_shr,
1088 DW_OP_skip,
1089 2,
1090 0, DW_OP_plus,
1093 DW_OP_deref,
1094 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()), CompiledExpressionPart::Code(vec![34]),
1123 CompiledExpressionPart::Deref,
1124 CompiledExpressionPart::Code(vec![6]),
1125 CompiledExpressionPart::LandingPad(targets[1].clone()), 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, DW_OP_deref,
1139 DW_OP_lit0,
1140 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()), CompiledExpressionPart::Code(vec![159])
1161 ],
1162 need_deref: false,
1163 }
1164 );
1165
1166 let e = expression!(
1167 DW_OP_lit1,
1168 DW_OP_dup,
1169 DW_OP_lit25,
1170 DW_OP_ge,
1171 DW_OP_bra,
1172 5,
1173 0, DW_OP_plus_uconst,
1175 1,
1176 DW_OP_skip,
1177 (-11_i8),
1178 (!0), 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 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 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 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 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 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}