cranelift_codegen/legalizer/
table.rs

1//! Legalization of tables.
2//!
3//! This module exports the `expand_table_addr` function which transforms a `table_addr`
4//! instruction into code that depends on the kind of table referenced.
5
6use crate::cursor::{Cursor, FuncCursor};
7use crate::ir::condcodes::IntCC;
8use crate::ir::immediates::Offset32;
9use crate::ir::{self, InstBuilder};
10use crate::isa::TargetIsa;
11
12/// Expand a `table_addr` instruction according to the definition of the table.
13pub fn expand_table_addr(
14    isa: &dyn TargetIsa,
15    inst: ir::Inst,
16    func: &mut ir::Function,
17    table: ir::Table,
18    index: ir::Value,
19    element_offset: Offset32,
20) {
21    let bound_gv = func.tables[table].bound_gv;
22    let index_ty = func.dfg.value_type(index);
23    let addr_ty = func.dfg.value_type(func.dfg.first_result(inst));
24    let mut pos = FuncCursor::new(func).at_inst(inst);
25    pos.use_srcloc(inst);
26
27    // Start with the bounds check. Trap if `index + 1 > bound`.
28    let bound = pos.ins().global_value(index_ty, bound_gv);
29
30    // `index > bound - 1` is the same as `index >= bound`.
31    let oob = pos
32        .ins()
33        .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
34    pos.ins().trapnz(oob, ir::TrapCode::TableOutOfBounds);
35
36    // If Spectre mitigations are enabled, we will use a comparison to
37    // short-circuit the computed table element address to the start
38    // of the table on the misspeculation path when out-of-bounds.
39    let spectre_oob_cmp = if isa.flags().enable_table_access_spectre_mitigation() {
40        Some((index, bound))
41    } else {
42        None
43    };
44
45    compute_addr(
46        inst,
47        table,
48        addr_ty,
49        index,
50        index_ty,
51        element_offset,
52        pos.func,
53        spectre_oob_cmp,
54    );
55}
56
57/// Emit code for the base address computation of a `table_addr` instruction.
58fn compute_addr(
59    inst: ir::Inst,
60    table: ir::Table,
61    addr_ty: ir::Type,
62    mut index: ir::Value,
63    index_ty: ir::Type,
64    element_offset: Offset32,
65    func: &mut ir::Function,
66    spectre_oob_cmp: Option<(ir::Value, ir::Value)>,
67) {
68    let mut pos = FuncCursor::new(func).at_inst(inst);
69    pos.use_srcloc(inst);
70
71    // Convert `index` to `addr_ty`.
72    if index_ty != addr_ty {
73        index = pos.ins().uextend(addr_ty, index);
74    }
75
76    // Add the table base address base
77    let base_gv = pos.func.tables[table].base_gv;
78    let base = pos.ins().global_value(addr_ty, base_gv);
79
80    let element_size = pos.func.tables[table].element_size;
81    let mut offset;
82    let element_size: u64 = element_size.into();
83    if element_size == 1 {
84        offset = index;
85    } else if element_size.is_power_of_two() {
86        offset = pos
87            .ins()
88            .ishl_imm(index, i64::from(element_size.trailing_zeros()));
89    } else {
90        offset = pos.ins().imul_imm(index, element_size as i64);
91    }
92
93    let element_addr = if element_offset == Offset32::new(0) {
94        pos.ins().iadd(base, offset)
95    } else {
96        let imm: i64 = element_offset.into();
97        offset = pos.ins().iadd(base, offset);
98        pos.ins().iadd_imm(offset, imm)
99    };
100
101    let element_addr = if let Some((index, bound)) = spectre_oob_cmp {
102        let cond = pos
103            .ins()
104            .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
105        // If out-of-bounds, choose the table base on the misspeculation path.
106        pos.ins().select_spectre_guard(cond, base, element_addr)
107    } else {
108        element_addr
109    };
110    let new_inst = pos.func.dfg.value_def(element_addr).inst().unwrap();
111
112    pos.func.dfg.replace_with_aliases(inst, new_inst);
113    pos.remove_inst();
114}