1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! 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::flowgraph::ControlFlowGraph;
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(
    inst: ir::Inst,
    func: &mut ir::Function,
    _cfg: &mut ControlFlowGraph,
    _isa: &dyn TargetIsa,
) {
    // Unpack the instruction.
    let (table, index, element_offset) = match func.dfg[inst] {
        ir::InstructionData::TableAddr {
            opcode,
            table,
            arg,
            offset,
        } => {
            debug_assert_eq!(opcode, ir::Opcode::TableAddr);
            (table, arg, offset)
        }
        _ => panic!("Wanted table_addr: {}", func.dfg.display_inst(inst, None)),
    };

    dynamic_addr(inst, table, index, element_offset, func);
}

/// Expand a `table_addr` for a dynamic table.
fn dynamic_addr(
    inst: ir::Inst,
    table: ir::Table,
    index: ir::Value,
    element_offset: Offset32,
    func: &mut ir::Function,
) {
    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);

    compute_addr(
        inst,
        table,
        addr_ty,
        index,
        index_ty,
        element_offset,
        pos.func,
    );
}

/// 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,
) {
    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);
    }

    if element_offset == Offset32::new(0) {
        pos.func.dfg.replace(inst).iadd(base, offset);
    } else {
        let imm: i64 = element_offset.into();
        offset = pos.ins().iadd(base, offset);
        pos.func.dfg.replace(inst).iadd_imm(offset, imm);
    }
}