wasmtime_cranelift_shared/
compiled_function.rs

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