wasmtime_cranelift/lib.rs
1//! Support for compiling with Cranelift.
2//!
3//! This crate provides an implementation of the `wasmtime_environ::Compiler`
4//! and `wasmtime_environ::CompilerBuilder` traits.
5
6use cranelift_codegen::ir;
7use cranelift_codegen::isa::{unwind::UnwindInfo, CallConv, TargetIsa};
8use cranelift_entity::PrimaryMap;
9use cranelift_wasm::{DefinedFuncIndex, FuncIndex, WasmFuncType, WasmType};
10use target_lexicon::{Architecture, CallingConvention};
11use wasmtime_cranelift_shared::Relocation;
12use wasmtime_environ::{
13 FilePos, InstructionAddressMap, ModuleTranslation, ModuleTypes, TrapInformation,
14};
15
16pub use builder::builder;
17
18mod builder;
19mod compiler;
20mod debug;
21mod func_environ;
22
23type CompiledFunctions<'a> = PrimaryMap<DefinedFuncIndex, &'a CompiledFunction>;
24
25/// Compiled function: machine code body, jump table offsets, and unwind information.
26#[derive(Default)]
27pub struct CompiledFunction {
28 /// The machine code for this function.
29 body: Vec<u8>,
30
31 /// The unwind information.
32 unwind_info: Option<UnwindInfo>,
33
34 /// Information used to translate from binary offsets back to the original
35 /// location found in the wasm input.
36 address_map: FunctionAddressMap,
37
38 /// Metadata about traps in this module, mapping code offsets to the trap
39 /// that they may cause.
40 traps: Vec<TrapInformation>,
41
42 relocations: Vec<Relocation>,
43 value_labels_ranges: cranelift_codegen::ValueLabelsRanges,
44 sized_stack_slots: ir::StackSlots,
45 alignment: u32,
46}
47
48/// Function and its instructions addresses mappings.
49#[derive(Debug, Clone, PartialEq, Eq, Default)]
50struct FunctionAddressMap {
51 /// An array of data for the instructions in this function, indicating where
52 /// each instruction maps back to in the original function.
53 ///
54 /// This array is sorted least-to-greatest by the `code_offset` field.
55 /// Additionally the span of each `InstructionAddressMap` is implicitly the
56 /// gap between it and the next item in the array.
57 instructions: Box<[InstructionAddressMap]>,
58
59 /// Function's initial offset in the source file, specified in bytes from
60 /// the front of the file.
61 start_srcloc: FilePos,
62
63 /// Function's end offset in the source file, specified in bytes from
64 /// the front of the file.
65 end_srcloc: FilePos,
66
67 /// Generated function body offset if applicable, otherwise 0.
68 body_offset: usize,
69
70 /// Generated function body length.
71 body_len: u32,
72}
73
74/// Creates a new cranelift `Signature` with no wasm params/results for the
75/// given calling convention.
76///
77/// This will add the default vmctx/etc parameters to the signature returned.
78fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
79 let pointer_type = isa.pointer_type();
80 let mut sig = ir::Signature::new(call_conv);
81 // Add the caller/callee `vmctx` parameters.
82 sig.params.push(ir::AbiParam::special(
83 pointer_type,
84 ir::ArgumentPurpose::VMContext,
85 ));
86 sig.params.push(ir::AbiParam::new(pointer_type));
87 return sig;
88}
89
90/// Returns the default calling convention for the `isa` provided.
91///
92/// Note that this calling convention is used for exported functions.
93fn wasmtime_call_conv(isa: &dyn TargetIsa) -> CallConv {
94 match isa.triple().default_calling_convention() {
95 Ok(CallingConvention::AppleAarch64) => CallConv::WasmtimeAppleAarch64,
96 Ok(CallingConvention::SystemV) | Err(()) => CallConv::WasmtimeSystemV,
97 Ok(CallingConvention::WindowsFastcall) => CallConv::WasmtimeFastcall,
98 Ok(unimp) => unimplemented!("calling convention: {:?}", unimp),
99 }
100}
101
102/// Appends the types of the `wasm` function signature into the `sig` signature
103/// provided.
104///
105/// Typically the `sig` signature will have been created from [`blank_sig`]
106/// above.
107fn push_types(isa: &dyn TargetIsa, sig: &mut ir::Signature, wasm: &WasmFuncType) {
108 let cvt = |ty: &WasmType| ir::AbiParam::new(value_type(isa, *ty));
109 sig.params.extend(wasm.params().iter().map(&cvt));
110 sig.returns.extend(wasm.returns().iter().map(&cvt));
111}
112
113/// Returns the corresponding cranelift type for the provided wasm type.
114fn value_type(isa: &dyn TargetIsa, ty: WasmType) -> ir::types::Type {
115 match ty {
116 WasmType::I32 => ir::types::I32,
117 WasmType::I64 => ir::types::I64,
118 WasmType::F32 => ir::types::F32,
119 WasmType::F64 => ir::types::F64,
120 WasmType::V128 => ir::types::I8X16,
121 WasmType::FuncRef | WasmType::ExternRef => reference_type(ty, isa.pointer_type()),
122 }
123}
124
125/// Returns a cranelift signature suitable to indirectly call the wasm signature
126/// specified by `wasm`.
127///
128/// This will implicitly use the default calling convention for `isa` since to
129/// indirectly call a wasm function it must be possibly exported somehow (e.g.
130/// this assumes the function target to call doesn't use the "fast" calling
131/// convention).
132fn indirect_signature(isa: &dyn TargetIsa, wasm: &WasmFuncType) -> ir::Signature {
133 let mut sig = blank_sig(isa, wasmtime_call_conv(isa));
134 push_types(isa, &mut sig, wasm);
135 return sig;
136}
137
138/// Returns the cranelift function signature of the function specified.
139///
140/// Note that this will determine the calling convention for the function, and
141/// namely includes an optimization where functions never exported from a module
142/// use a custom theoretically faster calling convention instead of the default.
143fn func_signature(
144 isa: &dyn TargetIsa,
145 translation: &ModuleTranslation,
146 types: &ModuleTypes,
147 index: FuncIndex,
148) -> ir::Signature {
149 let func = &translation.module.functions[index];
150 let call_conv = match translation.module.defined_func_index(index) {
151 // If this is a defined function in the module and it doesn't escape
152 // then we can optimize this function to use the fastest calling
153 // convention since it's purely an internal implementation detail of
154 // the module itself.
155 Some(_idx) if !func.is_escaping() => {
156 let on_apple_aarch64 = isa
157 .triple()
158 .default_calling_convention()
159 .unwrap_or(CallingConvention::SystemV)
160 == CallingConvention::AppleAarch64;
161
162 if on_apple_aarch64 {
163 // FIXME: We need an Apple-specific calling convention, so that
164 // Cranelift's ABI implementation generates unwinding directives
165 // about pointer authentication usage, so we can't just use
166 // `CallConv::Fast`.
167 CallConv::WasmtimeAppleAarch64
168 } else if isa.triple().architecture == Architecture::S390x {
169 // On S390x we need a Wasmtime calling convention to ensure
170 // we're using little-endian vector lane order.
171 wasmtime_call_conv(isa)
172 } else {
173 CallConv::Fast
174 }
175 }
176
177 // ... otherwise if it's an imported function or if it's a possibly
178 // exported function then we use the default ABI wasmtime would
179 // otherwise select.
180 _ => wasmtime_call_conv(isa),
181 };
182 let mut sig = blank_sig(isa, call_conv);
183 push_types(isa, &mut sig, &types[func.signature]);
184 return sig;
185}
186
187/// Returns the reference type to use for the provided wasm type.
188fn reference_type(wasm_ty: cranelift_wasm::WasmType, pointer_type: ir::Type) -> ir::Type {
189 match wasm_ty {
190 cranelift_wasm::WasmType::FuncRef => pointer_type,
191 cranelift_wasm::WasmType::ExternRef => match pointer_type {
192 ir::types::I32 => ir::types::R32,
193 ir::types::I64 => ir::types::R64,
194 _ => panic!("unsupported pointer type"),
195 },
196 _ => panic!("unsupported Wasm reference type"),
197 }
198}