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