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