use crate::TRAP_INTERNAL_ASSERT;
use crate::compiler::Compiler;
use cranelift_codegen::cursor::FuncCursor;
use cranelift_codegen::ir::condcodes::IntCC;
use cranelift_codegen::ir::types::I8;
use cranelift_codegen::ir::{self, InstBuilder};
use cranelift_frontend::FunctionBuilder;
use wasmtime_environ::{BuiltinFunctionIndex, TripleExt};
pub trait TranslateTrap {
fn compiler(&self) -> &Compiler;
fn vmctx_val(&mut self, cursor: &mut FuncCursor<'_>) -> ir::Value;
fn builtin_funcref(
&mut self,
builder: &mut FunctionBuilder<'_>,
index: BuiltinFunctionIndex,
) -> ir::FuncRef;
fn debug_tags(&self, _srcloc: ir::SourceLoc) -> Vec<ir::DebugTag> {
vec![]
}
fn trap(&mut self, builder: &mut FunctionBuilder, trap: ir::TrapCode) {
match (
self.clif_instruction_traps_enabled(),
crate::clif_trap_to_env_trap(trap),
) {
(true, _) | (_, None) => {
builder.ins().trap(trap);
}
(false, Some(trap)) => {
let debug_tags = self.debug_tags(builder.srcloc());
let trap_libcall = self.builtin_funcref(builder, BuiltinFunctionIndex::trap());
let vmctx = self.vmctx_val(&mut builder.cursor());
let trap_code = builder.ins().iconst(I8, i64::from(trap as u8));
builder.ins().call(trap_libcall, &[vmctx, trap_code]);
let raise_libcall = self.builtin_funcref(builder, BuiltinFunctionIndex::raise());
let inst = builder.ins().call(raise_libcall, &[vmctx]);
builder.func.debug_tags.set(inst, debug_tags);
builder.ins().trap(TRAP_INTERNAL_ASSERT);
}
}
}
fn trapz(&mut self, builder: &mut FunctionBuilder, value: ir::Value, trap: ir::TrapCode) {
if self.clif_instruction_traps_enabled() {
builder.ins().trapz(value, trap);
} else {
let ty = builder.func.dfg.value_type(value);
let zero = builder.ins().iconst(ty, 0);
let cmp = builder.ins().icmp(IntCC::Equal, value, zero);
self.conditionally_trap(builder, cmp, trap);
}
}
fn trapnz(&mut self, builder: &mut FunctionBuilder, value: ir::Value, trap: ir::TrapCode) {
if self.clif_instruction_traps_enabled() {
builder.ins().trapnz(value, trap);
} else {
let ty = builder.func.dfg.value_type(value);
let zero = builder.ins().iconst(ty, 0);
let cmp = builder.ins().icmp(IntCC::NotEqual, value, zero);
self.conditionally_trap(builder, cmp, trap);
}
}
fn uadd_overflow_trap(
&mut self,
builder: &mut FunctionBuilder,
lhs: ir::Value,
rhs: ir::Value,
trap: ir::TrapCode,
) -> ir::Value {
if self.clif_instruction_traps_enabled() {
builder.ins().uadd_overflow_trap(lhs, rhs, trap)
} else {
let (ret, overflow) = builder.ins().uadd_overflow(lhs, rhs);
self.conditionally_trap(builder, overflow, trap);
ret
}
}
fn conditionally_trap(
&mut self,
builder: &mut FunctionBuilder,
trap_cond: ir::Value,
trap: ir::TrapCode,
) {
assert!(!self.clif_instruction_traps_enabled());
let trap_block = builder.create_block();
builder.set_cold_block(trap_block);
let continuation_block = builder.create_block();
builder
.ins()
.brif(trap_cond, trap_block, &[], continuation_block, &[]);
builder.seal_block(trap_block);
builder.seal_block(continuation_block);
builder.switch_to_block(trap_block);
self.trap(builder, trap);
builder.switch_to_block(continuation_block);
}
fn clif_instruction_traps_enabled(&self) -> bool {
let tunables = self.compiler().tunables();
tunables.signals_based_traps || (self.is_pulley() && !tunables.debug_guest)
}
fn is_pulley(&self) -> bool {
self.compiler().isa().triple().is_pulley()
}
}