wasmer_compiler_cranelift/
table.rs

1use cranelift_codegen::cursor::FuncCursor;
2use cranelift_codegen::ir::{self, InstBuilder, condcodes::IntCC, immediates::Imm64};
3use cranelift_frontend::FunctionBuilder;
4
5/// Size of a WebAssembly table, in elements.
6#[derive(Clone, Debug)]
7pub enum TableSize {
8    /// Non-resizable table.
9    Static {
10        /// Non-resizable tables have a constant size known at compile time.
11        bound: u32,
12    },
13    /// Resizable table.
14    Dynamic {
15        /// Resizable tables declare a Cranelift global value to load the
16        /// current size from.
17        bound_gv: ir::GlobalValue,
18    },
19}
20
21impl TableSize {
22    /// Get a CLIF value representing the current bounds of this table.
23    pub fn bound(&self, mut pos: FuncCursor, index_ty: ir::Type) -> ir::Value {
24        match *self {
25            Self::Static { bound } => pos.ins().iconst(index_ty, Imm64::new(i64::from(bound))),
26            Self::Dynamic { bound_gv } => pos.ins().global_value(index_ty, bound_gv),
27        }
28    }
29}
30
31/// An implementation of a WebAssembly table.
32#[derive(Clone, Debug)]
33pub struct TableData {
34    /// Global value giving the address of the start of the table.
35    pub base_gv: ir::GlobalValue,
36
37    /// Constant offset to apply to `base_gv` to get the start of the table.
38    pub base_offset: i32,
39
40    /// The size of the table, in elements.
41    pub bound: TableSize,
42
43    /// The size of a table element, in bytes.
44    pub element_size: u32,
45
46    /// Whether table entries are inline `VMCallerCheckedAnyfunc` values.
47    pub inline_anyfunc: bool,
48}
49
50impl TableData {
51    /// Return a CLIF value containing a native pointer to the beginning of the
52    /// given index within this table.
53    pub fn prepare_table_addr(
54        &self,
55        pos: &mut FunctionBuilder,
56        mut index: ir::Value,
57        addr_ty: ir::Type,
58        enable_table_access_spectre_mitigation: bool,
59    ) -> (ir::Value, ir::MemFlags) {
60        let index_ty = pos.func.dfg.value_type(index);
61
62        // Start with the bounds check. Trap if `index + 1 > bound`.
63        let bound = self.bound.bound(pos.cursor(), index_ty);
64
65        // `index > bound - 1` is the same as `index >= bound`.
66        let oob = pos
67            .ins()
68            .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
69
70        if !enable_table_access_spectre_mitigation {
71            pos.ins().trapnz(oob, crate::TRAP_TABLE_OUT_OF_BOUNDS);
72        }
73
74        // Convert `index` to `addr_ty`.
75        if index_ty != addr_ty {
76            index = pos.ins().uextend(addr_ty, index);
77        }
78
79        // Add the table base address base
80        let mut base = pos.ins().global_value(addr_ty, self.base_gv);
81        if self.base_offset != 0 {
82            base = pos.ins().iadd_imm(base, i64::from(self.base_offset));
83        }
84
85        let element_size = self.element_size;
86        let offset = if element_size == 1 {
87            index
88        } else if element_size.is_power_of_two() {
89            pos.ins()
90                .ishl_imm(index, i64::from(element_size.trailing_zeros()))
91        } else {
92            pos.ins().imul_imm(index, element_size as i64)
93        };
94
95        let element_addr = pos.ins().iadd(base, offset);
96
97        let base_flags = ir::MemFlags::new()
98            .with_aligned()
99            .with_alias_region(Some(ir::AliasRegion::Table));
100        if enable_table_access_spectre_mitigation {
101            // Short-circuit the computed table element address to a null pointer
102            // when out-of-bounds. The consumer of this address will trap when
103            // trying to access it.
104            let zero = pos.ins().iconst(addr_ty, 0);
105            (
106                pos.ins().select_spectre_guard(oob, zero, element_addr),
107                base_flags.with_trap_code(Some(crate::TRAP_TABLE_OUT_OF_BOUNDS)),
108            )
109        } else {
110            (element_addr, base_flags.with_trap_code(None))
111        }
112    }
113}