wasmtime_internal_cranelift/
compiled_function.rs1use std::ops::Range;
2
3use crate::{Relocation, mach_reloc_to_reloc, mach_trap_to_trap};
4use cranelift_codegen::{
5 Final, MachBufferFinalized, MachBufferFrameLayout, MachSrcLoc, ValueLabelsRanges, ir,
6 isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo,
7};
8use wasmtime_environ::{
9 FilePos, FrameStateSlotBuilder, InstructionAddressMap, PrimaryMap, TrapInformation,
10};
11
12#[derive(Debug, Clone, PartialEq, Eq, Default)]
13pub struct FunctionAddressMap {
16 pub instructions: Box<[InstructionAddressMap]>,
23
24 pub start_srcloc: FilePos,
27
28 pub end_srcloc: FilePos,
31
32 pub body_offset: usize,
34
35 pub body_len: u32,
37}
38
39#[derive(Default)]
41pub struct CompiledFunctionMetadata {
42 pub address_map: FunctionAddressMap,
45 pub unwind_info: Option<UnwindInfo>,
47 pub cfa_unwind_info: Option<CfaUnwindInfo>,
49 pub value_labels_ranges: ValueLabelsRanges,
51 pub start_srcloc: FilePos,
53 pub end_srcloc: FilePos,
55}
56
57pub struct CompiledFunction {
59 pub buffer: MachBufferFinalized<Final>,
61 name_map: PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
63 pub alignment: u32,
65 metadata: CompiledFunctionMetadata,
68 pub debug_slot_descriptor: Option<FrameStateSlotBuilder>,
70 pub breakpoint_patch_points: Vec<(u32, Range<u32>)>,
72}
73
74impl CompiledFunction {
75 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 fn finalize_breakpoints(&mut self) {
100 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 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 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 pub fn traps(&self) -> impl Iterator<Item = TrapInformation> + '_ {
148 self.buffer.traps().iter().filter_map(mach_trap_to_trap)
149 }
150
151 pub fn address_map(&self) -> &FunctionAddressMap {
153 &self.metadata.address_map
154 }
155
156 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 pub fn unwind_info(&self) -> Option<&UnwindInfo> {
188 self.metadata.unwind_info.as_ref()
189 }
190
191 pub fn metadata(&self) -> &CompiledFunctionMetadata {
193 &self.metadata
194 }
195
196 pub fn set_value_labels_ranges(&mut self, ranges: ValueLabelsRanges) {
198 self.metadata.value_labels_ranges = ranges;
199 }
200
201 pub fn set_unwind_info(&mut self, unwind: UnwindInfo) {
203 self.metadata.unwind_info = Some(unwind);
204 }
205
206 pub fn set_cfa_unwind_info(&mut self, unwind: CfaUnwindInfo) {
208 self.metadata.cfa_unwind_info = Some(unwind);
209 }
210
211 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 pub fn breakpoint_patches(&self) -> impl Iterator<Item = (u32, Range<u32>)> + '_ {
222 self.breakpoint_patch_points.iter().cloned()
223 }
224}
225
226fn 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 cur_offset + cur_len == offset && loc == cur_loc {
244 cur_len += len;
245 continue;
246 }
247
248 ret.push(InstructionAddressMap {
250 srcloc: cvt(cur_loc),
251 code_offset: cur_offset,
252 });
253 if cur_offset + cur_len != offset {
256 ret.push(InstructionAddressMap {
257 srcloc: FilePos::default(),
258 code_offset: cur_offset + cur_len,
259 });
260 }
261 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}