wasmtime_internal_cranelift/
compiled_function.rs

1use crate::{Relocation, mach_reloc_to_reloc, mach_trap_to_trap};
2use cranelift_codegen::{
3    Final, MachBufferFinalized, MachBufferFrameLayout, MachSrcLoc, ValueLabelsRanges, ir,
4    isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo,
5};
6use 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.
13pub 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)]
39pub 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.
56pub 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
70impl 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.
172fn 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}