Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/cranelift/src/compiled_function.rs
3052 views
1
use std::ops::Range;
2
3
use crate::{Relocation, mach_reloc_to_reloc, mach_trap_to_trap};
4
use cranelift_codegen::{
5
Final, MachBufferFinalized, MachBufferFrameLayout, MachSrcLoc, ValueLabelsRanges, ir,
6
isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo,
7
};
8
use wasmtime_environ::{
9
FilePos, FrameStateSlotBuilder, InstructionAddressMap, PrimaryMap, TrapInformation,
10
};
11
12
#[derive(Debug, Clone, PartialEq, Eq, Default)]
13
/// Metadata to translate from binary offsets back to the original
14
/// location found in the wasm input.
15
pub struct FunctionAddressMap {
16
/// An array of data for the instructions in this function, indicating where
17
/// each instruction maps back to in the original function.
18
///
19
/// This array is sorted least-to-greatest by the `code_offset` field.
20
/// Additionally the span of each `InstructionAddressMap` is implicitly the
21
/// gap between it and the next item in the array.
22
pub instructions: Box<[InstructionAddressMap]>,
23
24
/// Function's initial offset in the source file, specified in bytes from
25
/// the front of the file.
26
pub start_srcloc: FilePos,
27
28
/// Function's end offset in the source file, specified in bytes from
29
/// the front of the file.
30
pub end_srcloc: FilePos,
31
32
/// Generated function body offset if applicable, otherwise 0.
33
pub body_offset: usize,
34
35
/// Generated function body length.
36
pub body_len: u32,
37
}
38
39
/// The metadata for the compiled function.
40
#[derive(Default)]
41
pub struct CompiledFunctionMetadata {
42
/// The function address map to translate from binary
43
/// back to the original source.
44
pub address_map: FunctionAddressMap,
45
/// The unwind information.
46
pub unwind_info: Option<UnwindInfo>,
47
/// CFA-based unwind information for DWARF debugging support.
48
pub cfa_unwind_info: Option<CfaUnwindInfo>,
49
/// Mapping of value labels and their locations.
50
pub value_labels_ranges: ValueLabelsRanges,
51
/// Start source location.
52
pub start_srcloc: FilePos,
53
/// End source location.
54
pub end_srcloc: FilePos,
55
}
56
57
/// Compiled function: machine code body, jump table offsets, and unwind information.
58
pub struct CompiledFunction {
59
/// The machine code buffer for this function.
60
pub buffer: MachBufferFinalized<Final>,
61
/// What names each name ref corresponds to.
62
name_map: PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
63
/// The alignment for the compiled function.
64
pub alignment: u32,
65
/// The metadata for the compiled function, including unwind information
66
/// the function address map.
67
metadata: CompiledFunctionMetadata,
68
/// Debug metadata for the top-level function's state slot.
69
pub debug_slot_descriptor: Option<FrameStateSlotBuilder>,
70
/// Debug breakpoint patches: Wasm PC, offset range in buffer.
71
pub breakpoint_patch_points: Vec<(u32, Range<u32>)>,
72
}
73
74
impl CompiledFunction {
75
/// Creates a [CompiledFunction] from a [`cranelift_codegen::MachBufferFinalized<Final>`]
76
/// This function uses the information in the machine buffer to derive the traps and relocations
77
/// fields. The compiled function metadata is loaded with the default values.
78
pub fn new(
79
buffer: MachBufferFinalized<Final>,
80
name_map: PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
81
alignment: u32,
82
) -> Self {
83
let mut this = Self {
84
buffer,
85
name_map,
86
alignment,
87
metadata: Default::default(),
88
debug_slot_descriptor: None,
89
breakpoint_patch_points: vec![],
90
};
91
this.finalize_breakpoints();
92
93
this
94
}
95
96
/// Finalize breakpoint patches: edit the buffer to have NOPs by
97
/// default, and place patch data in the debug breakpoint data
98
/// tables.
99
fn finalize_breakpoints(&mut self) {
100
// Traverse debug tags and patchable callsites together. All
101
// patchable callsites should have debug tags. Given both, we
102
// can know the Wasm PC and we can emit a breakpoint record.
103
let mut tags = self.buffer.debug_tags().peekable();
104
let mut patchable_callsites = self.buffer.patchable_call_sites().peekable();
105
106
while let (Some(tag), Some(patchable_callsite)) = (tags.peek(), patchable_callsites.peek())
107
{
108
if tag.offset > patchable_callsite.ret_addr {
109
patchable_callsites.next();
110
continue;
111
}
112
if patchable_callsite.ret_addr > tag.offset {
113
tags.next();
114
continue;
115
}
116
assert_eq!(tag.offset, patchable_callsite.ret_addr);
117
118
// Tag format used by our Wasm-to-CLIF format is
119
// (stackslot, wasm_pc, stack_shape). Taking the
120
// second-to-last tag will get the innermost Wasm PC (if
121
// there are multiple nested frames due to inlining).
122
assert!(tag.tags.len() >= 3);
123
let ir::DebugTag::User(wasm_pc) = tag.tags[tag.tags.len() - 2] else {
124
panic!("invalid tag")
125
};
126
127
let patchable_start = patchable_callsite.ret_addr - patchable_callsite.len;
128
let patchable_end = patchable_callsite.ret_addr;
129
130
self.breakpoint_patch_points
131
.push((wasm_pc, patchable_start..patchable_end));
132
133
tags.next();
134
patchable_callsites.next();
135
}
136
}
137
138
/// Returns an iterator to the function's relocation information.
139
pub fn relocations(&self) -> impl Iterator<Item = Relocation> + '_ {
140
self.buffer
141
.relocs()
142
.iter()
143
.map(|r| mach_reloc_to_reloc(r, &self.name_map))
144
}
145
146
/// Returns an iterator to the function's trap information.
147
pub fn traps(&self) -> impl Iterator<Item = TrapInformation> + '_ {
148
self.buffer.traps().iter().filter_map(mach_trap_to_trap)
149
}
150
151
/// Get the function's address map from the metadata.
152
pub fn address_map(&self) -> &FunctionAddressMap {
153
&self.metadata.address_map
154
}
155
156
/// Create and return the compiled function address map from the original source offset
157
/// and length.
158
pub fn set_address_map(&mut self, offset: u32, length: u32, with_instruction_addresses: bool) {
159
assert!((offset + length) <= u32::max_value());
160
let len = self.buffer.data().len();
161
let srclocs = self
162
.buffer
163
.get_srclocs_sorted()
164
.into_iter()
165
.map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start)));
166
let instructions = if with_instruction_addresses {
167
collect_address_maps(len.try_into().unwrap(), srclocs)
168
} else {
169
Default::default()
170
};
171
let start_srcloc = FilePos::new(offset);
172
let end_srcloc = FilePos::new(offset + length);
173
174
let address_map = FunctionAddressMap {
175
instructions: instructions.into(),
176
start_srcloc,
177
end_srcloc,
178
body_offset: 0,
179
body_len: len.try_into().unwrap(),
180
};
181
182
self.metadata.address_map = address_map;
183
}
184
185
/// Get a reference to the unwind information from the
186
/// function's metadata.
187
pub fn unwind_info(&self) -> Option<&UnwindInfo> {
188
self.metadata.unwind_info.as_ref()
189
}
190
191
/// Get a reference to the compiled function metadata.
192
pub fn metadata(&self) -> &CompiledFunctionMetadata {
193
&self.metadata
194
}
195
196
/// Set the value labels ranges in the function's metadata.
197
pub fn set_value_labels_ranges(&mut self, ranges: ValueLabelsRanges) {
198
self.metadata.value_labels_ranges = ranges;
199
}
200
201
/// Set the unwind info in the function's metadata.
202
pub fn set_unwind_info(&mut self, unwind: UnwindInfo) {
203
self.metadata.unwind_info = Some(unwind);
204
}
205
206
/// Set the CFA-based unwind info in the function's metadata.
207
pub fn set_cfa_unwind_info(&mut self, unwind: CfaUnwindInfo) {
208
self.metadata.cfa_unwind_info = Some(unwind);
209
}
210
211
/// Returns the frame-layout metadata for this function.
212
pub fn frame_layout(&self) -> &MachBufferFrameLayout {
213
self.buffer
214
.frame_layout()
215
.expect("Single-function MachBuffer must have frame layout information")
216
}
217
218
/// Returns an iterator over breakpoint patches for this function.
219
///
220
/// Each tuple is (wasm PC, buffer offset range).
221
pub fn breakpoint_patches(&self) -> impl Iterator<Item = (u32, Range<u32>)> + '_ {
222
self.breakpoint_patch_points.iter().cloned()
223
}
224
}
225
226
// Collects an iterator of `InstructionAddressMap` into a `Vec` for insertion
227
// into a `FunctionAddressMap`. This will automatically coalesce adjacent
228
// instructions which map to the same original source position.
229
fn collect_address_maps(
230
code_size: u32,
231
iter: impl IntoIterator<Item = (ir::SourceLoc, u32, u32)>,
232
) -> Vec<InstructionAddressMap> {
233
let mut iter = iter.into_iter();
234
let (mut cur_loc, mut cur_offset, mut cur_len) = match iter.next() {
235
Some(i) => i,
236
None => return Vec::new(),
237
};
238
let mut ret = Vec::new();
239
for (loc, offset, len) in iter {
240
// If this instruction is adjacent to the previous and has the same
241
// source location then we can "coalesce" it with the current
242
// instruction.
243
if cur_offset + cur_len == offset && loc == cur_loc {
244
cur_len += len;
245
continue;
246
}
247
248
// Push an entry for the previous source item.
249
ret.push(InstructionAddressMap {
250
srcloc: cvt(cur_loc),
251
code_offset: cur_offset,
252
});
253
// And push a "dummy" entry if necessary to cover the span of ranges,
254
// if any, between the previous source offset and this one.
255
if cur_offset + cur_len != offset {
256
ret.push(InstructionAddressMap {
257
srcloc: FilePos::default(),
258
code_offset: cur_offset + cur_len,
259
});
260
}
261
// Update our current location to get extended later or pushed on at
262
// the end.
263
cur_loc = loc;
264
cur_offset = offset;
265
cur_len = len;
266
}
267
ret.push(InstructionAddressMap {
268
srcloc: cvt(cur_loc),
269
code_offset: cur_offset,
270
});
271
if cur_offset + cur_len != code_size {
272
ret.push(InstructionAddressMap {
273
srcloc: FilePos::default(),
274
code_offset: cur_offset + cur_len,
275
});
276
}
277
278
return ret;
279
280
fn cvt(loc: ir::SourceLoc) -> FilePos {
281
if loc.is_default() {
282
FilePos::default()
283
} else {
284
FilePos::new(loc.bits())
285
}
286
}
287
}
288
289