Path: blob/main/crates/cranelift/src/translate/table.rs
1692 views
use crate::func_environ::FuncEnvironment;1use cranelift_codegen::cursor::FuncCursor;2use cranelift_codegen::ir::{self, InstBuilder, condcodes::IntCC, immediates::Imm64};3use cranelift_codegen::isa::TargetIsa;4use cranelift_frontend::FunctionBuilder;56/// Size of a WebAssembly table, in elements.7#[derive(Clone)]8pub enum TableSize {9/// Non-resizable table.10Static {11/// Non-resizable tables have a constant size known at compile time.12bound: u64,13},14/// Resizable table.15Dynamic {16/// Resizable tables declare a Cranelift global value to load the17/// current size from.18bound_gv: ir::GlobalValue,19},20}2122impl TableSize {23/// Get a CLIF value representing the current bounds of this table.24pub fn bound(&self, isa: &dyn TargetIsa, mut pos: FuncCursor, index_ty: ir::Type) -> ir::Value {25match *self {26// Instead of `i64::try_from(bound)`, here we just want to directly interpret `bound` as an i64.27TableSize::Static { bound } => pos.ins().iconst(index_ty, Imm64::new(bound as i64)),28TableSize::Dynamic { bound_gv } => {29let ty = pos.func.global_values[bound_gv].global_type(isa);30let gv = pos.ins().global_value(ty, bound_gv);31if index_ty == ty {32gv33} else if index_ty.bytes() < ty.bytes() {34pos.ins().ireduce(index_ty, gv)35} else {36pos.ins().uextend(index_ty, gv)37}38}39}40}41}4243/// An implementation of a WebAssembly table.44#[derive(Clone)]45pub struct TableData {46/// Global value giving the address of the start of the table.47pub base_gv: ir::GlobalValue,4849/// The size of the table, in elements.50pub bound: TableSize,5152/// The size of a table element, in bytes.53pub element_size: u32,54}5556impl TableData {57/// Return a CLIF value containing a native pointer to the beginning of the58/// given index within this table.59pub fn prepare_table_addr(60&self,61env: &mut FuncEnvironment<'_>,62pos: &mut FunctionBuilder,63mut index: ir::Value,64) -> (ir::Value, ir::MemFlags) {65let index_ty = pos.func.dfg.value_type(index);66let addr_ty = env.pointer_type();67let spectre_mitigations_enabled =68env.isa().flags().enable_table_access_spectre_mitigation()69&& env.clif_memory_traps_enabled();7071// Start with the bounds check. Trap if `index + 1 > bound`.72let bound = self.bound.bound(env.isa(), pos.cursor(), index_ty);7374// `index > bound - 1` is the same as `index >= bound`.75let oob = pos76.ins()77.icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);7879if !spectre_mitigations_enabled {80env.trapnz(pos, oob, crate::TRAP_TABLE_OUT_OF_BOUNDS);81}8283// Convert `index` to `addr_ty`.84if addr_ty.bytes() > index_ty.bytes() {85index = pos.ins().uextend(addr_ty, index);86} else if addr_ty.bytes() < index_ty.bytes() {87index = pos.ins().ireduce(addr_ty, index);88}8990// Add the table base address base91let base = pos.ins().global_value(addr_ty, self.base_gv);9293let element_size = self.element_size;94let offset = if element_size == 1 {95index96} else if element_size.is_power_of_two() {97pos.ins()98.ishl_imm(index, i64::from(element_size.trailing_zeros()))99} else {100pos.ins().imul_imm(index, element_size as i64)101};102103let element_addr = pos.ins().iadd(base, offset);104105let base_flags = ir::MemFlags::new()106.with_aligned()107.with_alias_region(Some(ir::AliasRegion::Table));108if spectre_mitigations_enabled {109// Short-circuit the computed table element address to a null pointer110// when out-of-bounds. The consumer of this address will trap when111// trying to access it.112let zero = pos.ins().iconst(addr_ty, 0);113(114pos.ins().select_spectre_guard(oob, zero, element_addr),115base_flags.with_trap_code(Some(crate::TRAP_TABLE_OUT_OF_BOUNDS)),116)117} else {118(element_addr, base_flags.with_trap_code(None))119}120}121}122123124