1 use crate::TRAP_INTERNAL_ASSERT; 2 use crate::compiler::Compiler; 3 use cranelift_codegen::cursor::FuncCursor; 4 use cranelift_codegen::ir::condcodes::IntCC; 5 use cranelift_codegen::ir::types::I8; 6 use cranelift_codegen::ir::{self, InstBuilder}; 7 use cranelift_frontend::FunctionBuilder; 8 use wasmtime_environ::{BuiltinFunctionIndex, TripleExt}; 9 10 /// Helper trait to share translation of traps between core functions and 11 /// component trampolines. 12 /// 13 /// Traps are conditionally performed as libcalls when signals-based-traps are 14 /// disabled, for example, but otherwise use the native CLIF `trap` instruction. 15 pub trait TranslateTrap { compiler(&self) -> &Compiler16 fn compiler(&self) -> &Compiler; vmctx_val(&mut self, cursor: &mut FuncCursor<'_>) -> ir::Value17 fn vmctx_val(&mut self, cursor: &mut FuncCursor<'_>) -> ir::Value; builtin_funcref( &mut self, builder: &mut FunctionBuilder<'_>, index: BuiltinFunctionIndex, ) -> ir::FuncRef18 fn builtin_funcref( 19 &mut self, 20 builder: &mut FunctionBuilder<'_>, 21 index: BuiltinFunctionIndex, 22 ) -> ir::FuncRef; debug_tags(&self, _srcloc: ir::SourceLoc) -> Vec<ir::DebugTag>23 fn debug_tags(&self, _srcloc: ir::SourceLoc) -> Vec<ir::DebugTag> { 24 vec![] 25 } 26 trap(&mut self, builder: &mut FunctionBuilder, trap: ir::TrapCode)27 fn trap(&mut self, builder: &mut FunctionBuilder, trap: ir::TrapCode) { 28 match ( 29 self.clif_instruction_traps_enabled(), 30 crate::clif_trap_to_env_trap(trap), 31 ) { 32 // If libcall traps are disabled or there's no wasmtime-defined trap 33 // code for this, then emit a native trap instruction. 34 (true, _) | (_, None) => { 35 builder.ins().trap(trap); 36 } 37 // ... otherwise with libcall traps explicitly enabled and a 38 // wasmtime-based trap code invoke the libcall to raise a trap and 39 // pass in our trap code. Leave a debug `unreachable` in place 40 // afterwards as a defense-in-depth measure. 41 (false, Some(trap)) => { 42 let debug_tags = self.debug_tags(builder.srcloc()); 43 let trap_libcall = self.builtin_funcref(builder, BuiltinFunctionIndex::trap()); 44 let vmctx = self.vmctx_val(&mut builder.cursor()); 45 let trap_code = builder.ins().iconst(I8, i64::from(trap as u8)); 46 builder.ins().call(trap_libcall, &[vmctx, trap_code]); 47 let raise_libcall = self.builtin_funcref(builder, BuiltinFunctionIndex::raise()); 48 let inst = builder.ins().call(raise_libcall, &[vmctx]); 49 builder.func.debug_tags.set(inst, debug_tags); 50 builder.ins().trap(TRAP_INTERNAL_ASSERT); 51 } 52 } 53 } 54 trapz(&mut self, builder: &mut FunctionBuilder, value: ir::Value, trap: ir::TrapCode)55 fn trapz(&mut self, builder: &mut FunctionBuilder, value: ir::Value, trap: ir::TrapCode) { 56 if self.clif_instruction_traps_enabled() { 57 builder.ins().trapz(value, trap); 58 } else { 59 let ty = builder.func.dfg.value_type(value); 60 let zero = builder.ins().iconst(ty, 0); 61 let cmp = builder.ins().icmp(IntCC::Equal, value, zero); 62 self.conditionally_trap(builder, cmp, trap); 63 } 64 } 65 trapnz(&mut self, builder: &mut FunctionBuilder, value: ir::Value, trap: ir::TrapCode)66 fn trapnz(&mut self, builder: &mut FunctionBuilder, value: ir::Value, trap: ir::TrapCode) { 67 if self.clif_instruction_traps_enabled() { 68 builder.ins().trapnz(value, trap); 69 } else { 70 let ty = builder.func.dfg.value_type(value); 71 let zero = builder.ins().iconst(ty, 0); 72 let cmp = builder.ins().icmp(IntCC::NotEqual, value, zero); 73 self.conditionally_trap(builder, cmp, trap); 74 } 75 } 76 uadd_overflow_trap( &mut self, builder: &mut FunctionBuilder, lhs: ir::Value, rhs: ir::Value, trap: ir::TrapCode, ) -> ir::Value77 fn uadd_overflow_trap( 78 &mut self, 79 builder: &mut FunctionBuilder, 80 lhs: ir::Value, 81 rhs: ir::Value, 82 trap: ir::TrapCode, 83 ) -> ir::Value { 84 if self.clif_instruction_traps_enabled() { 85 builder.ins().uadd_overflow_trap(lhs, rhs, trap) 86 } else { 87 let (ret, overflow) = builder.ins().uadd_overflow(lhs, rhs); 88 self.conditionally_trap(builder, overflow, trap); 89 ret 90 } 91 } 92 93 /// Helper to emit a conditional trap based on `trap_cond`. 94 /// 95 /// This should only be used if `self.clif_instruction_traps_enabled()` is 96 /// false, otherwise native CLIF instructions should be used instead. conditionally_trap( &mut self, builder: &mut FunctionBuilder, trap_cond: ir::Value, trap: ir::TrapCode, )97 fn conditionally_trap( 98 &mut self, 99 builder: &mut FunctionBuilder, 100 trap_cond: ir::Value, 101 trap: ir::TrapCode, 102 ) { 103 assert!(!self.clif_instruction_traps_enabled()); 104 105 let trap_block = builder.create_block(); 106 builder.set_cold_block(trap_block); 107 let continuation_block = builder.create_block(); 108 109 builder 110 .ins() 111 .brif(trap_cond, trap_block, &[], continuation_block, &[]); 112 113 builder.seal_block(trap_block); 114 builder.seal_block(continuation_block); 115 116 builder.switch_to_block(trap_block); 117 self.trap(builder, trap); 118 builder.switch_to_block(continuation_block); 119 } 120 121 /// Returns whether it's acceptable to have CLIF instructions natively trap, 122 /// such as division-by-zero. 123 /// 124 /// This is enabled if `signals_based_traps` is `true` or on 125 /// Pulley unconditionally since Pulley doesn't use hardware-based 126 /// traps in its runtime. However, if guest debugging is enabled, 127 /// then we cannot rely on Pulley traps and still need a libcall 128 /// to gain proper ownership of the store in the runtime's 129 /// debugger hooks. clif_instruction_traps_enabled(&self) -> bool130 fn clif_instruction_traps_enabled(&self) -> bool { 131 let tunables = self.compiler().tunables(); 132 tunables.signals_based_traps || (self.is_pulley() && !tunables.debug_guest) 133 } 134 135 /// Returns whether translation is happening for Pulley bytecode. is_pulley(&self) -> bool136 fn is_pulley(&self) -> bool { 137 self.compiler().isa().triple().is_pulley() 138 } 139 } 140