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