1 use crate::{Relocation, mach_reloc_to_reloc, mach_trap_to_trap};
2 use cranelift_codegen::{
3     Final, MachBufferFinalized, MachBufferFrameLayout, MachSrcLoc, ValueLabelsRanges, ir,
4     isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo,
5 };
6 use wasmtime_environ::{
7     FilePos, FrameStateSlotBuilder, InstructionAddressMap, PrimaryMap, TrapInformation,
8 };
9 
10 #[derive(Debug, Clone, PartialEq, Eq, Default)]
11 /// Metadata to translate from binary offsets back to the original
12 /// location found in the wasm input.
13 pub struct FunctionAddressMap {
14     /// An array of data for the instructions in this function, indicating where
15     /// each instruction maps back to in the original function.
16     ///
17     /// This array is sorted least-to-greatest by the `code_offset` field.
18     /// Additionally the span of each `InstructionAddressMap` is implicitly the
19     /// gap between it and the next item in the array.
20     pub instructions: Box<[InstructionAddressMap]>,
21 
22     /// Function's initial offset in the source file, specified in bytes from
23     /// the front of the file.
24     pub start_srcloc: FilePos,
25 
26     /// Function's end offset in the source file, specified in bytes from
27     /// the front of the file.
28     pub end_srcloc: FilePos,
29 
30     /// Generated function body offset if applicable, otherwise 0.
31     pub body_offset: usize,
32 
33     /// Generated function body length.
34     pub body_len: u32,
35 }
36 
37 /// The metadata for the compiled function.
38 #[derive(Default)]
39 pub struct CompiledFunctionMetadata {
40     /// The function address map to translate from binary
41     /// back to the original source.
42     pub address_map: FunctionAddressMap,
43     /// The unwind information.
44     pub unwind_info: Option<UnwindInfo>,
45     /// CFA-based unwind information for DWARF debugging support.
46     pub cfa_unwind_info: Option<CfaUnwindInfo>,
47     /// Mapping of value labels and their locations.
48     pub value_labels_ranges: ValueLabelsRanges,
49     /// Start source location.
50     pub start_srcloc: FilePos,
51     /// End source location.
52     pub end_srcloc: FilePos,
53 }
54 
55 /// Compiled function: machine code body, jump table offsets, and unwind information.
56 pub struct CompiledFunction {
57     /// The machine code buffer for this function.
58     pub buffer: MachBufferFinalized<Final>,
59     /// What names each name ref corresponds to.
60     name_map: PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
61     /// The alignment for the compiled function.
62     pub alignment: u32,
63     /// The metadata for the compiled function, including unwind information
64     /// the function address map.
65     metadata: CompiledFunctionMetadata,
66     /// Debug metadata for the top-level function's state slot.
67     pub debug_slot_descriptor: Option<FrameStateSlotBuilder>,
68 }
69 
70 impl CompiledFunction {
71     /// Creates a [CompiledFunction] from a [`cranelift_codegen::MachBufferFinalized<Final>`]
72     /// This function uses the information in the machine buffer to derive the traps and relocations
73     /// fields. The compiled function metadata is loaded with the default values.
74     pub fn new(
75         buffer: MachBufferFinalized<Final>,
76         name_map: PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
77         alignment: u32,
78     ) -> Self {
79         Self {
80             buffer,
81             name_map,
82             alignment,
83             metadata: Default::default(),
84             debug_slot_descriptor: None,
85         }
86     }
87 
88     /// Returns an iterator to the function's relocation information.
89     pub fn relocations(&self) -> impl Iterator<Item = Relocation> + '_ {
90         self.buffer
91             .relocs()
92             .iter()
93             .map(|r| mach_reloc_to_reloc(r, &self.name_map))
94     }
95 
96     /// Returns an iterator to the function's trap information.
97     pub fn traps(&self) -> impl Iterator<Item = TrapInformation> + '_ {
98         self.buffer.traps().iter().filter_map(mach_trap_to_trap)
99     }
100 
101     /// Get the function's address map from the metadata.
102     pub fn address_map(&self) -> &FunctionAddressMap {
103         &self.metadata.address_map
104     }
105 
106     /// Create and return the compiled function address map from the original source offset
107     /// and length.
108     pub fn set_address_map(&mut self, offset: u32, length: u32, with_instruction_addresses: bool) {
109         assert!((offset + length) <= u32::max_value());
110         let len = self.buffer.data().len();
111         let srclocs = self
112             .buffer
113             .get_srclocs_sorted()
114             .into_iter()
115             .map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start)));
116         let instructions = if with_instruction_addresses {
117             collect_address_maps(len.try_into().unwrap(), srclocs)
118         } else {
119             Default::default()
120         };
121         let start_srcloc = FilePos::new(offset);
122         let end_srcloc = FilePos::new(offset + length);
123 
124         let address_map = FunctionAddressMap {
125             instructions: instructions.into(),
126             start_srcloc,
127             end_srcloc,
128             body_offset: 0,
129             body_len: len.try_into().unwrap(),
130         };
131 
132         self.metadata.address_map = address_map;
133     }
134 
135     /// Get a reference to the unwind information from the
136     /// function's metadata.
137     pub fn unwind_info(&self) -> Option<&UnwindInfo> {
138         self.metadata.unwind_info.as_ref()
139     }
140 
141     /// Get a reference to the compiled function metadata.
142     pub fn metadata(&self) -> &CompiledFunctionMetadata {
143         &self.metadata
144     }
145 
146     /// Set the value labels ranges in the function's metadata.
147     pub fn set_value_labels_ranges(&mut self, ranges: ValueLabelsRanges) {
148         self.metadata.value_labels_ranges = ranges;
149     }
150 
151     /// Set the unwind info in the function's metadata.
152     pub fn set_unwind_info(&mut self, unwind: UnwindInfo) {
153         self.metadata.unwind_info = Some(unwind);
154     }
155 
156     /// Set the CFA-based unwind info in the function's metadata.
157     pub fn set_cfa_unwind_info(&mut self, unwind: CfaUnwindInfo) {
158         self.metadata.cfa_unwind_info = Some(unwind);
159     }
160 
161     /// Returns the frame-layout metadata for this function.
162     pub fn frame_layout(&self) -> &MachBufferFrameLayout {
163         self.buffer
164             .frame_layout()
165             .expect("Single-function MachBuffer must have frame layout information")
166     }
167 }
168 
169 // Collects an iterator of `InstructionAddressMap` into a `Vec` for insertion
170 // into a `FunctionAddressMap`. This will automatically coalesce adjacent
171 // instructions which map to the same original source position.
172 fn collect_address_maps(
173     code_size: u32,
174     iter: impl IntoIterator<Item = (ir::SourceLoc, u32, u32)>,
175 ) -> Vec<InstructionAddressMap> {
176     let mut iter = iter.into_iter();
177     let (mut cur_loc, mut cur_offset, mut cur_len) = match iter.next() {
178         Some(i) => i,
179         None => return Vec::new(),
180     };
181     let mut ret = Vec::new();
182     for (loc, offset, len) in iter {
183         // If this instruction is adjacent to the previous and has the same
184         // source location then we can "coalesce" it with the current
185         // instruction.
186         if cur_offset + cur_len == offset && loc == cur_loc {
187             cur_len += len;
188             continue;
189         }
190 
191         // Push an entry for the previous source item.
192         ret.push(InstructionAddressMap {
193             srcloc: cvt(cur_loc),
194             code_offset: cur_offset,
195         });
196         // And push a "dummy" entry if necessary to cover the span of ranges,
197         // if any, between the previous source offset and this one.
198         if cur_offset + cur_len != offset {
199             ret.push(InstructionAddressMap {
200                 srcloc: FilePos::default(),
201                 code_offset: cur_offset + cur_len,
202             });
203         }
204         // Update our current location to get extended later or pushed on at
205         // the end.
206         cur_loc = loc;
207         cur_offset = offset;
208         cur_len = len;
209     }
210     ret.push(InstructionAddressMap {
211         srcloc: cvt(cur_loc),
212         code_offset: cur_offset,
213     });
214     if cur_offset + cur_len != code_size {
215         ret.push(InstructionAddressMap {
216             srcloc: FilePos::default(),
217             code_offset: cur_offset + cur_len,
218         });
219     }
220 
221     return ret;
222 
223     fn cvt(loc: ir::SourceLoc) -> FilePos {
224         if loc.is_default() {
225             FilePos::default()
226         } else {
227             FilePos::new(loc.bits())
228         }
229     }
230 }
231