wasmtime_cranelift/
compiled_function.rs

1use crate::{mach_reloc_to_reloc, mach_trap_to_trap, Relocation};
2use cranelift_codegen::{
3    ir, isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo, Final, MachBufferFinalized,
4    MachSrcLoc, ValueLabelsRanges,
5};
6use 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.
11pub 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)]
37pub 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.
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}
67
68impl 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.
167fn 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}