Path: blob/main/crates/cranelift/src/compiled_function.rs
3052 views
use std::ops::Range;12use crate::{Relocation, mach_reloc_to_reloc, mach_trap_to_trap};3use cranelift_codegen::{4Final, MachBufferFinalized, MachBufferFrameLayout, MachSrcLoc, ValueLabelsRanges, ir,5isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo,6};7use wasmtime_environ::{8FilePos, FrameStateSlotBuilder, InstructionAddressMap, PrimaryMap, TrapInformation,9};1011#[derive(Debug, Clone, PartialEq, Eq, Default)]12/// Metadata to translate from binary offsets back to the original13/// location found in the wasm input.14pub struct FunctionAddressMap {15/// An array of data for the instructions in this function, indicating where16/// each instruction maps back to in the original function.17///18/// This array is sorted least-to-greatest by the `code_offset` field.19/// Additionally the span of each `InstructionAddressMap` is implicitly the20/// gap between it and the next item in the array.21pub instructions: Box<[InstructionAddressMap]>,2223/// Function's initial offset in the source file, specified in bytes from24/// the front of the file.25pub start_srcloc: FilePos,2627/// Function's end offset in the source file, specified in bytes from28/// the front of the file.29pub end_srcloc: FilePos,3031/// Generated function body offset if applicable, otherwise 0.32pub body_offset: usize,3334/// Generated function body length.35pub body_len: u32,36}3738/// The metadata for the compiled function.39#[derive(Default)]40pub struct CompiledFunctionMetadata {41/// The function address map to translate from binary42/// back to the original source.43pub address_map: FunctionAddressMap,44/// The unwind information.45pub unwind_info: Option<UnwindInfo>,46/// CFA-based unwind information for DWARF debugging support.47pub cfa_unwind_info: Option<CfaUnwindInfo>,48/// Mapping of value labels and their locations.49pub value_labels_ranges: ValueLabelsRanges,50/// Start source location.51pub start_srcloc: FilePos,52/// End source location.53pub end_srcloc: FilePos,54}5556/// Compiled function: machine code body, jump table offsets, and unwind information.57pub struct CompiledFunction {58/// The machine code buffer for this function.59pub buffer: MachBufferFinalized<Final>,60/// What names each name ref corresponds to.61name_map: PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,62/// The alignment for the compiled function.63pub alignment: u32,64/// The metadata for the compiled function, including unwind information65/// the function address map.66metadata: CompiledFunctionMetadata,67/// Debug metadata for the top-level function's state slot.68pub debug_slot_descriptor: Option<FrameStateSlotBuilder>,69/// Debug breakpoint patches: Wasm PC, offset range in buffer.70pub breakpoint_patch_points: Vec<(u32, Range<u32>)>,71}7273impl CompiledFunction {74/// Creates a [CompiledFunction] from a [`cranelift_codegen::MachBufferFinalized<Final>`]75/// This function uses the information in the machine buffer to derive the traps and relocations76/// fields. The compiled function metadata is loaded with the default values.77pub fn new(78buffer: MachBufferFinalized<Final>,79name_map: PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,80alignment: u32,81) -> Self {82let mut this = Self {83buffer,84name_map,85alignment,86metadata: Default::default(),87debug_slot_descriptor: None,88breakpoint_patch_points: vec![],89};90this.finalize_breakpoints();9192this93}9495/// Finalize breakpoint patches: edit the buffer to have NOPs by96/// default, and place patch data in the debug breakpoint data97/// tables.98fn finalize_breakpoints(&mut self) {99// Traverse debug tags and patchable callsites together. All100// patchable callsites should have debug tags. Given both, we101// can know the Wasm PC and we can emit a breakpoint record.102let mut tags = self.buffer.debug_tags().peekable();103let mut patchable_callsites = self.buffer.patchable_call_sites().peekable();104105while let (Some(tag), Some(patchable_callsite)) = (tags.peek(), patchable_callsites.peek())106{107if tag.offset > patchable_callsite.ret_addr {108patchable_callsites.next();109continue;110}111if patchable_callsite.ret_addr > tag.offset {112tags.next();113continue;114}115assert_eq!(tag.offset, patchable_callsite.ret_addr);116117// Tag format used by our Wasm-to-CLIF format is118// (stackslot, wasm_pc, stack_shape). Taking the119// second-to-last tag will get the innermost Wasm PC (if120// there are multiple nested frames due to inlining).121assert!(tag.tags.len() >= 3);122let ir::DebugTag::User(wasm_pc) = tag.tags[tag.tags.len() - 2] else {123panic!("invalid tag")124};125126let patchable_start = patchable_callsite.ret_addr - patchable_callsite.len;127let patchable_end = patchable_callsite.ret_addr;128129self.breakpoint_patch_points130.push((wasm_pc, patchable_start..patchable_end));131132tags.next();133patchable_callsites.next();134}135}136137/// Returns an iterator to the function's relocation information.138pub fn relocations(&self) -> impl Iterator<Item = Relocation> + '_ {139self.buffer140.relocs()141.iter()142.map(|r| mach_reloc_to_reloc(r, &self.name_map))143}144145/// Returns an iterator to the function's trap information.146pub fn traps(&self) -> impl Iterator<Item = TrapInformation> + '_ {147self.buffer.traps().iter().filter_map(mach_trap_to_trap)148}149150/// Get the function's address map from the metadata.151pub fn address_map(&self) -> &FunctionAddressMap {152&self.metadata.address_map153}154155/// Create and return the compiled function address map from the original source offset156/// and length.157pub fn set_address_map(&mut self, offset: u32, length: u32, with_instruction_addresses: bool) {158assert!((offset + length) <= u32::max_value());159let len = self.buffer.data().len();160let srclocs = self161.buffer162.get_srclocs_sorted()163.into_iter()164.map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start)));165let instructions = if with_instruction_addresses {166collect_address_maps(len.try_into().unwrap(), srclocs)167} else {168Default::default()169};170let start_srcloc = FilePos::new(offset);171let end_srcloc = FilePos::new(offset + length);172173let address_map = FunctionAddressMap {174instructions: instructions.into(),175start_srcloc,176end_srcloc,177body_offset: 0,178body_len: len.try_into().unwrap(),179};180181self.metadata.address_map = address_map;182}183184/// Get a reference to the unwind information from the185/// function's metadata.186pub fn unwind_info(&self) -> Option<&UnwindInfo> {187self.metadata.unwind_info.as_ref()188}189190/// Get a reference to the compiled function metadata.191pub fn metadata(&self) -> &CompiledFunctionMetadata {192&self.metadata193}194195/// Set the value labels ranges in the function's metadata.196pub fn set_value_labels_ranges(&mut self, ranges: ValueLabelsRanges) {197self.metadata.value_labels_ranges = ranges;198}199200/// Set the unwind info in the function's metadata.201pub fn set_unwind_info(&mut self, unwind: UnwindInfo) {202self.metadata.unwind_info = Some(unwind);203}204205/// Set the CFA-based unwind info in the function's metadata.206pub fn set_cfa_unwind_info(&mut self, unwind: CfaUnwindInfo) {207self.metadata.cfa_unwind_info = Some(unwind);208}209210/// Returns the frame-layout metadata for this function.211pub fn frame_layout(&self) -> &MachBufferFrameLayout {212self.buffer213.frame_layout()214.expect("Single-function MachBuffer must have frame layout information")215}216217/// Returns an iterator over breakpoint patches for this function.218///219/// Each tuple is (wasm PC, buffer offset range).220pub fn breakpoint_patches(&self) -> impl Iterator<Item = (u32, Range<u32>)> + '_ {221self.breakpoint_patch_points.iter().cloned()222}223}224225// Collects an iterator of `InstructionAddressMap` into a `Vec` for insertion226// into a `FunctionAddressMap`. This will automatically coalesce adjacent227// instructions which map to the same original source position.228fn collect_address_maps(229code_size: u32,230iter: impl IntoIterator<Item = (ir::SourceLoc, u32, u32)>,231) -> Vec<InstructionAddressMap> {232let mut iter = iter.into_iter();233let (mut cur_loc, mut cur_offset, mut cur_len) = match iter.next() {234Some(i) => i,235None => return Vec::new(),236};237let mut ret = Vec::new();238for (loc, offset, len) in iter {239// If this instruction is adjacent to the previous and has the same240// source location then we can "coalesce" it with the current241// instruction.242if cur_offset + cur_len == offset && loc == cur_loc {243cur_len += len;244continue;245}246247// Push an entry for the previous source item.248ret.push(InstructionAddressMap {249srcloc: cvt(cur_loc),250code_offset: cur_offset,251});252// And push a "dummy" entry if necessary to cover the span of ranges,253// if any, between the previous source offset and this one.254if cur_offset + cur_len != offset {255ret.push(InstructionAddressMap {256srcloc: FilePos::default(),257code_offset: cur_offset + cur_len,258});259}260// Update our current location to get extended later or pushed on at261// the end.262cur_loc = loc;263cur_offset = offset;264cur_len = len;265}266ret.push(InstructionAddressMap {267srcloc: cvt(cur_loc),268code_offset: cur_offset,269});270if cur_offset + cur_len != code_size {271ret.push(InstructionAddressMap {272srcloc: FilePos::default(),273code_offset: cur_offset + cur_len,274});275}276277return ret;278279fn cvt(loc: ir::SourceLoc) -> FilePos {280if loc.is_default() {281FilePos::default()282} else {283FilePos::new(loc.bits())284}285}286}287288289