As discussed in the 2022/10/19 meeting, this PR removes many of the branch and select instructions that used iflags, in favor if using brz/brnz and select in their place. Additionally, it reworks selectif_spectre_guard to take an i8 input instead of an iflags input. For reference, the removed instructions are: br_icmp, brif, brff, trueif, trueff, and selectif.
115 lines
3.5 KiB
Rust
115 lines
3.5 KiB
Rust
//! Legalization of tables.
|
|
//!
|
|
//! This module exports the `expand_table_addr` function which transforms a `table_addr`
|
|
//! instruction into code that depends on the kind of table referenced.
|
|
|
|
use crate::cursor::{Cursor, FuncCursor};
|
|
use crate::ir::condcodes::IntCC;
|
|
use crate::ir::immediates::Offset32;
|
|
use crate::ir::{self, InstBuilder};
|
|
use crate::isa::TargetIsa;
|
|
|
|
/// Expand a `table_addr` instruction according to the definition of the table.
|
|
pub fn expand_table_addr(
|
|
isa: &dyn TargetIsa,
|
|
inst: ir::Inst,
|
|
func: &mut ir::Function,
|
|
table: ir::Table,
|
|
index: ir::Value,
|
|
element_offset: Offset32,
|
|
) {
|
|
let bound_gv = func.tables[table].bound_gv;
|
|
let index_ty = func.dfg.value_type(index);
|
|
let addr_ty = func.dfg.value_type(func.dfg.first_result(inst));
|
|
let mut pos = FuncCursor::new(func).at_inst(inst);
|
|
pos.use_srcloc(inst);
|
|
|
|
// Start with the bounds check. Trap if `index + 1 > bound`.
|
|
let bound = pos.ins().global_value(index_ty, bound_gv);
|
|
|
|
// `index > bound - 1` is the same as `index >= bound`.
|
|
let oob = pos
|
|
.ins()
|
|
.icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
|
|
pos.ins().trapnz(oob, ir::TrapCode::TableOutOfBounds);
|
|
|
|
// If Spectre mitigations are enabled, we will use a comparison to
|
|
// short-circuit the computed table element address to the start
|
|
// of the table on the misspeculation path when out-of-bounds.
|
|
let spectre_oob_cmp = if isa.flags().enable_table_access_spectre_mitigation() {
|
|
Some((index, bound))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
compute_addr(
|
|
inst,
|
|
table,
|
|
addr_ty,
|
|
index,
|
|
index_ty,
|
|
element_offset,
|
|
pos.func,
|
|
spectre_oob_cmp,
|
|
);
|
|
}
|
|
|
|
/// Emit code for the base address computation of a `table_addr` instruction.
|
|
fn compute_addr(
|
|
inst: ir::Inst,
|
|
table: ir::Table,
|
|
addr_ty: ir::Type,
|
|
mut index: ir::Value,
|
|
index_ty: ir::Type,
|
|
element_offset: Offset32,
|
|
func: &mut ir::Function,
|
|
spectre_oob_cmp: Option<(ir::Value, ir::Value)>,
|
|
) {
|
|
let mut pos = FuncCursor::new(func).at_inst(inst);
|
|
pos.use_srcloc(inst);
|
|
|
|
// Convert `index` to `addr_ty`.
|
|
if index_ty != addr_ty {
|
|
index = pos.ins().uextend(addr_ty, index);
|
|
}
|
|
|
|
// Add the table base address base
|
|
let base_gv = pos.func.tables[table].base_gv;
|
|
let base = pos.ins().global_value(addr_ty, base_gv);
|
|
|
|
let element_size = pos.func.tables[table].element_size;
|
|
let mut offset;
|
|
let element_size: u64 = element_size.into();
|
|
if element_size == 1 {
|
|
offset = index;
|
|
} else if element_size.is_power_of_two() {
|
|
offset = pos
|
|
.ins()
|
|
.ishl_imm(index, i64::from(element_size.trailing_zeros()));
|
|
} else {
|
|
offset = pos.ins().imul_imm(index, element_size as i64);
|
|
}
|
|
|
|
let element_addr = if element_offset == Offset32::new(0) {
|
|
pos.ins().iadd(base, offset)
|
|
} else {
|
|
let imm: i64 = element_offset.into();
|
|
offset = pos.ins().iadd(base, offset);
|
|
pos.ins().iadd_imm(offset, imm)
|
|
};
|
|
|
|
let element_addr = if let Some((index, bound)) = spectre_oob_cmp {
|
|
let cond = pos
|
|
.ins()
|
|
.icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
|
|
// If out-of-bounds, choose the table base on the misspeculation path.
|
|
pos.ins().select_spectre_guard(cond, base, element_addr)
|
|
} else {
|
|
element_addr
|
|
};
|
|
let new_inst = pos.func.dfg.value_def(element_addr).inst().unwrap();
|
|
|
|
pos.func.dfg.replace_with_aliases(inst, new_inst);
|
|
pos.remove_inst();
|
|
}
|