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