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