wasmtime_internal_cranelift/
lib.rs1#![warn(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
16
17use cranelift_codegen::{
18 FinalizedMachReloc, FinalizedRelocTarget, MachTrap, binemit,
19 cursor::FuncCursor,
20 ir::{self, AbiParam, ArgumentPurpose, ExternalName, InstBuilder, Signature, TrapCode},
21 isa::{CallConv, TargetIsa},
22 settings,
23};
24use cranelift_entity::PrimaryMap;
25
26use target_lexicon::Architecture;
27use wasmtime_environ::{
28 BuiltinFunctionIndex, CompiledTrap, FlagValue, FuncKey, Trap, TrapInformation, Tunables,
29 WasmFuncType, WasmHeapTopType, WasmHeapType, WasmValType,
30};
31
32pub use builder::builder;
33
34pub mod isa_builder;
35mod obj;
36pub use obj::*;
37mod compiled_function;
38pub use compiled_function::*;
39
40mod alias_region;
41mod bounds_checks;
42mod builder;
43mod compiler;
44mod debug;
45mod func_environ;
46mod translate;
47mod trap;
48
49use self::compiler::Compiler;
50
51const TRAP_INTERNAL_ASSERT: TrapCode = TrapCode::unwrap_user(1);
52const TRAP_GC_HEAP_CORRUPT: TrapCode = TrapCode::unwrap_user(2);
53const TRAP_OFFSET: u8 = 3;
54pub const TRAP_CANNOT_LEAVE_COMPONENT: TrapCode =
55 TrapCode::unwrap_user(Trap::CannotLeaveComponent as u8 + TRAP_OFFSET);
56pub const TRAP_INDIRECT_CALL_TO_NULL: TrapCode =
57 TrapCode::unwrap_user(Trap::IndirectCallToNull as u8 + TRAP_OFFSET);
58pub const TRAP_BAD_SIGNATURE: TrapCode =
59 TrapCode::unwrap_user(Trap::BadSignature as u8 + TRAP_OFFSET);
60pub const TRAP_NULL_REFERENCE: TrapCode =
61 TrapCode::unwrap_user(Trap::NullReference as u8 + TRAP_OFFSET);
62pub const TRAP_ALLOCATION_TOO_LARGE: TrapCode =
63 TrapCode::unwrap_user(Trap::AllocationTooLarge as u8 + TRAP_OFFSET);
64pub const TRAP_ARRAY_OUT_OF_BOUNDS: TrapCode =
65 TrapCode::unwrap_user(Trap::ArrayOutOfBounds as u8 + TRAP_OFFSET);
66pub const TRAP_UNREACHABLE: TrapCode =
67 TrapCode::unwrap_user(Trap::UnreachableCodeReached as u8 + TRAP_OFFSET);
68pub const TRAP_HEAP_MISALIGNED: TrapCode =
69 TrapCode::unwrap_user(Trap::HeapMisaligned as u8 + TRAP_OFFSET);
70pub const TRAP_TABLE_OUT_OF_BOUNDS: TrapCode =
71 TrapCode::unwrap_user(Trap::TableOutOfBounds as u8 + TRAP_OFFSET);
72pub const TRAP_UNHANDLED_TAG: TrapCode =
73 TrapCode::unwrap_user(Trap::UnhandledTag as u8 + TRAP_OFFSET);
74pub const TRAP_CONTINUATION_ALREADY_CONSUMED: TrapCode =
75 TrapCode::unwrap_user(Trap::ContinuationAlreadyConsumed as u8 + TRAP_OFFSET);
76pub const TRAP_CAST_FAILURE: TrapCode =
77 TrapCode::unwrap_user(Trap::CastFailure as u8 + TRAP_OFFSET);
78pub const TRAP_UNCAUGHT_EXCEPTION: TrapCode =
79 TrapCode::unwrap_user(Trap::UncaughtException as u8 + TRAP_OFFSET);
80
81fn blank_sig(isa: &dyn TargetIsa, call_conv: CallConv) -> ir::Signature {
86 let pointer_type = isa.pointer_type();
87 let mut sig = ir::Signature::new(call_conv);
88 sig.params.push(ir::AbiParam::special(
90 pointer_type,
91 ir::ArgumentPurpose::VMContext,
92 ));
93 sig.params.push(ir::AbiParam::new(pointer_type));
94 return sig;
95}
96
97fn unbarriered_store_type_at_offset(
106 pos: &mut FuncCursor,
107 flags: ir::MemFlagsData,
108 base: ir::Value,
109 offset: i32,
110 value: ir::Value,
111) {
112 pos.ins().store(flags, value, base, offset);
113}
114
115fn unbarriered_load_type_at_offset(
125 isa: &dyn TargetIsa,
126 pos: &mut FuncCursor,
127 ty: WasmValType,
128 flags: ir::MemFlagsData,
129 base: ir::Value,
130 offset: i32,
131) -> ir::Value {
132 let ir_ty = value_type(isa, ty);
133 pos.ins().load(ir_ty, flags, base, offset)
134}
135
136fn value_type(isa: &dyn TargetIsa, ty: WasmValType) -> ir::types::Type {
138 match ty {
139 WasmValType::I32 => ir::types::I32,
140 WasmValType::I64 => ir::types::I64,
141 WasmValType::F32 => ir::types::F32,
142 WasmValType::F64 => ir::types::F64,
143 WasmValType::V128 => ir::types::I8X16,
144 WasmValType::Ref(rt) => reference_type(rt.heap_type, isa.pointer_type()),
145 }
146}
147
148fn array_call_signature(isa: &dyn TargetIsa) -> ir::Signature {
164 let mut sig = blank_sig(isa, CallConv::triple_default(isa.triple()));
165 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
169 sig.params.push(ir::AbiParam::new(isa.pointer_type()));
170 sig.returns.push(ir::AbiParam::new(ir::types::I8));
172 sig
173}
174
175fn wasm_call_conv(isa: &dyn TargetIsa, tunables: &Tunables) -> CallConv {
177 if tunables.winch_callable {
187 assert!(
188 matches!(
189 isa.triple().architecture,
190 Architecture::X86_64 | Architecture::Aarch64(_)
191 ),
192 "The Winch calling convention is only implemented for x86_64 and aarch64"
193 );
194 CallConv::Winch
195 } else {
196 CallConv::Tail
197 }
198}
199
200fn wasm_call_signature(
202 isa: &dyn TargetIsa,
203 wasm_func_ty: &WasmFuncType,
204 tunables: &Tunables,
205) -> ir::Signature {
206 let call_conv = wasm_call_conv(isa, tunables);
207 let mut sig = blank_sig(isa, call_conv);
208 let cvt = |ty: &WasmValType| ir::AbiParam::new(value_type(isa, *ty));
209 sig.params.extend(wasm_func_ty.params().iter().map(&cvt));
210 sig.returns.extend(wasm_func_ty.results().iter().map(&cvt));
211 sig
212}
213
214fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
216 match wasm_ht.top() {
217 WasmHeapTopType::Func => pointer_type,
218 WasmHeapTopType::Any | WasmHeapTopType::Extern | WasmHeapTopType::Exn => ir::types::I32,
219 WasmHeapTopType::Cont => {
220 ir::Type::int((2 * pointer_type.bits()).try_into().unwrap()).unwrap()
222 }
223 }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct Relocation {
231 pub reloc: binemit::Reloc,
233 pub reloc_target: FuncKey,
235 pub offset: binemit::CodeOffset,
237 pub addend: binemit::Addend,
239}
240
241pub fn clif_flags_to_wasmtime(
243 flags: impl IntoIterator<Item = settings::Value>,
244) -> Vec<(&'static str, FlagValue<'static>)> {
245 flags
246 .into_iter()
247 .map(|val| (val.name, to_flag_value(&val)))
248 .collect()
249}
250
251fn to_flag_value(v: &settings::Value) -> FlagValue<'static> {
252 match v.kind() {
253 settings::SettingKind::Enum => FlagValue::Enum(v.as_enum().unwrap()),
254 settings::SettingKind::Num => FlagValue::Num(v.as_num().unwrap()),
255 settings::SettingKind::Bool => FlagValue::Bool(v.as_bool().unwrap()),
256 settings::SettingKind::Preset => unreachable!(),
257 }
258}
259
260pub fn mach_trap_to_trap(trap: &MachTrap, tunables: &Tunables) -> Option<TrapInformation> {
262 let &MachTrap { offset, code } = trap;
263 Some(TrapInformation {
264 code_offset: offset,
265 trap_code: clif_trap_to_env_trap(code, tunables)?,
266 })
267}
268
269fn clif_trap_to_env_trap(trap: ir::TrapCode, tunables: &Tunables) -> Option<CompiledTrap> {
270 Some(CompiledTrap::Normal(match trap {
271 ir::TrapCode::STACK_OVERFLOW => Trap::StackOverflow,
272 ir::TrapCode::HEAP_OUT_OF_BOUNDS => Trap::MemoryOutOfBounds,
273 ir::TrapCode::INTEGER_OVERFLOW => Trap::IntegerOverflow,
274 ir::TrapCode::INTEGER_DIVISION_BY_ZERO => Trap::IntegerDivisionByZero,
275 ir::TrapCode::BAD_CONVERSION_TO_INTEGER => Trap::BadConversionToInteger,
276
277 TRAP_INTERNAL_ASSERT => {
278 return if tunables.metadata_for_internal_asserts {
279 Some(CompiledTrap::InternalAssert)
280 } else {
281 None
282 };
283 }
284 TRAP_GC_HEAP_CORRUPT => {
285 return if tunables.metadata_for_gc_heap_corruption {
286 Some(CompiledTrap::GcHeapCorrupt)
287 } else {
288 None
289 };
290 }
291
292 other => Trap::from_u8(other.as_raw().get() - TRAP_OFFSET).unwrap(),
293 }))
294}
295
296fn mach_reloc_to_reloc(
299 reloc: &FinalizedMachReloc,
300 name_map: &PrimaryMap<ir::UserExternalNameRef, ir::UserExternalName>,
301) -> Relocation {
302 let &FinalizedMachReloc {
303 offset,
304 kind,
305 ref target,
306 addend,
307 } = reloc;
308 let reloc_target = match *target {
309 FinalizedRelocTarget::ExternalName(ExternalName::User(user_func_ref)) => {
310 let name = &name_map[user_func_ref];
311 FuncKey::from_raw_parts(name.namespace, name.index)
312 }
313 FinalizedRelocTarget::ExternalName(ExternalName::LibCall(libcall)) => {
314 panic!("unexpected libcall {libcall:?}");
317 }
318 _ => panic!("unrecognized external name {target:?}"),
319 };
320 Relocation {
321 reloc: kind,
322 reloc_target,
323 offset,
324 addend,
325 }
326}
327
328struct BuiltinFunctionSignatures {
330 pointer_type: ir::Type,
331
332 host_call_conv: CallConv,
333 wasm_call_conv: CallConv,
334 argument_extension: ir::ArgumentExtension,
335}
336
337impl BuiltinFunctionSignatures {
338 fn new(compiler: &Compiler) -> Self {
339 Self {
340 pointer_type: compiler.isa().pointer_type(),
341 host_call_conv: CallConv::triple_default(compiler.isa().triple()),
342 wasm_call_conv: wasm_call_conv(compiler.isa(), compiler.tunables()),
343 argument_extension: compiler.isa().default_argument_extension(),
344 }
345 }
346
347 fn vmctx(&self) -> AbiParam {
348 AbiParam::special(self.pointer_type, ArgumentPurpose::VMContext)
349 }
350
351 fn pointer(&self) -> AbiParam {
352 AbiParam::new(self.pointer_type)
353 }
354
355 fn u32(&self) -> AbiParam {
356 AbiParam::new(ir::types::I32)
357 }
358
359 fn u64(&self) -> AbiParam {
360 AbiParam::new(ir::types::I64)
361 }
362
363 fn f32(&self) -> AbiParam {
364 AbiParam::new(ir::types::F32)
365 }
366
367 fn f64(&self) -> AbiParam {
368 AbiParam::new(ir::types::F64)
369 }
370
371 fn u8(&self) -> AbiParam {
372 AbiParam::new(ir::types::I8)
373 }
374
375 fn i8x16(&self) -> AbiParam {
376 AbiParam::new(ir::types::I8X16)
377 }
378
379 fn f32x4(&self) -> AbiParam {
380 AbiParam::new(ir::types::F32X4)
381 }
382
383 fn f64x2(&self) -> AbiParam {
384 AbiParam::new(ir::types::F64X2)
385 }
386
387 fn bool(&self) -> AbiParam {
388 AbiParam::new(ir::types::I8)
389 }
390
391 fn size(&self) -> AbiParam {
392 AbiParam::new(self.pointer_type)
393 }
394
395 fn wasm_signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
396 let mut _cur = 0;
397 macro_rules! iter {
398 (
399 $(
400 $( #[$attr:meta] )*
401 $name:ident( $( $pname:ident: $param:ident ),* ) $( -> $result:ident )?;
402 )*
403 ) => {
404 $(
405 $( #[$attr] )*
406 if _cur == builtin.index() {
407 return Signature {
408 params: vec![ $( self.$param() ),* ],
409 returns: vec![ $( self.$result() )? ],
410 call_conv: self.wasm_call_conv,
411 };
412 }
413 _cur += 1;
414 )*
415 };
416 }
417
418 wasmtime_environ::foreach_builtin_function!(iter);
419
420 unreachable!();
421 }
422
423 fn host_signature(&self, builtin: BuiltinFunctionIndex) -> Signature {
424 let mut sig = self.wasm_signature(builtin);
425 sig.call_conv = self.host_call_conv;
426
427 for arg in sig.params.iter_mut().chain(sig.returns.iter_mut()) {
431 if arg.value_type.is_int() {
432 arg.extension = self.argument_extension;
433 }
434 }
435
436 sig
437 }
438}
439
440const I31_REF_DISCRIMINANT: u32 = 1;
446
447#[derive(PartialEq, Eq)]
453#[must_use]
454enum Reachability<T> {
455 Reachable(T),
457 Unreachable,
461}