Define a "table" concept.
"Table" is to WebAssembly tables as "Heap" is to WebAssembly linear memories.
This commit is contained in:
@@ -172,6 +172,24 @@ impl Heap {
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a table.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Table(u32);
|
||||
entity_impl!(Table, "table");
|
||||
|
||||
impl Table {
|
||||
/// Create a new table reference from its number.
|
||||
///
|
||||
/// This method is for use by the parser.
|
||||
pub fn with_number(n: u32) -> Option<Self> {
|
||||
if n < u32::MAX {
|
||||
Some(Table(n))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to any of the entities defined in this module.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum AnyEntity {
|
||||
@@ -195,6 +213,8 @@ pub enum AnyEntity {
|
||||
SigRef(SigRef),
|
||||
/// A heap.
|
||||
Heap(Heap),
|
||||
/// A table.
|
||||
Table(Table),
|
||||
}
|
||||
|
||||
impl fmt::Display for AnyEntity {
|
||||
@@ -210,6 +230,7 @@ impl fmt::Display for AnyEntity {
|
||||
AnyEntity::FuncRef(r) => r.fmt(f),
|
||||
AnyEntity::SigRef(r) => r.fmt(f),
|
||||
AnyEntity::Heap(r) => r.fmt(f),
|
||||
AnyEntity::Table(r) => r.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,6 +295,12 @@ impl From<Heap> for AnyEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Table> for AnyEntity {
|
||||
fn from(r: Table) -> Self {
|
||||
AnyEntity::Table(r)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -9,7 +9,7 @@ use ir;
|
||||
use ir::{DataFlowGraph, ExternalName, Layout, Signature};
|
||||
use ir::{
|
||||
Ebb, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Heap, HeapData, JumpTable,
|
||||
JumpTableData, SigRef, StackSlot, StackSlotData,
|
||||
JumpTableData, SigRef, StackSlot, StackSlotData, Table, TableData,
|
||||
};
|
||||
use ir::{EbbOffsets, InstEncodings, JumpTables, SourceLocs, StackSlots, ValueLocations};
|
||||
use isa::{EncInfo, Encoding, Legalize, TargetIsa};
|
||||
@@ -42,6 +42,9 @@ pub struct Function {
|
||||
/// Heaps referenced.
|
||||
pub heaps: PrimaryMap<ir::Heap, ir::HeapData>,
|
||||
|
||||
/// Tables referenced.
|
||||
pub tables: PrimaryMap<ir::Table, ir::TableData>,
|
||||
|
||||
/// Jump tables used in this function.
|
||||
pub jump_tables: JumpTables,
|
||||
|
||||
@@ -82,6 +85,7 @@ impl Function {
|
||||
stack_limit: None,
|
||||
global_values: PrimaryMap::new(),
|
||||
heaps: PrimaryMap::new(),
|
||||
tables: PrimaryMap::new(),
|
||||
jump_tables: PrimaryMap::new(),
|
||||
dfg: DataFlowGraph::new(),
|
||||
layout: Layout::new(),
|
||||
@@ -98,6 +102,7 @@ impl Function {
|
||||
self.stack_slots.clear();
|
||||
self.global_values.clear();
|
||||
self.heaps.clear();
|
||||
self.tables.clear();
|
||||
self.jump_tables.clear();
|
||||
self.dfg.clear();
|
||||
self.layout.clear();
|
||||
@@ -157,6 +162,11 @@ impl Function {
|
||||
self.heaps.push(data)
|
||||
}
|
||||
|
||||
/// Declares a table accessible to the function.
|
||||
pub fn create_table(&mut self, data: TableData) -> Table {
|
||||
self.tables.push(data)
|
||||
}
|
||||
|
||||
/// Return an object that can display this function with correct ISA-specific annotations.
|
||||
pub fn display<'a, I: Into<Option<&'a TargetIsa>>>(&'a self, isa: I) -> DisplayFunction<'a> {
|
||||
DisplayFunction(self, isa.into())
|
||||
|
||||
@@ -18,6 +18,7 @@ mod memflags;
|
||||
mod progpoint;
|
||||
mod sourceloc;
|
||||
pub mod stackslot;
|
||||
mod table;
|
||||
mod trapcode;
|
||||
pub mod types;
|
||||
mod valueloc;
|
||||
@@ -25,7 +26,7 @@ mod valueloc;
|
||||
pub use ir::builder::{InsertBuilder, InstBuilder, InstBuilderBase, InstInserterBase};
|
||||
pub use ir::dfg::{DataFlowGraph, ValueDef};
|
||||
pub use ir::entities::{
|
||||
Ebb, FuncRef, GlobalValue, Heap, Inst, JumpTable, SigRef, StackSlot, Value,
|
||||
Ebb, FuncRef, GlobalValue, Heap, Inst, JumpTable, SigRef, StackSlot, Table, Value,
|
||||
};
|
||||
pub use ir::extfunc::{AbiParam, ArgumentExtension, ArgumentPurpose, ExtFuncData, Signature};
|
||||
pub use ir::extname::ExternalName;
|
||||
@@ -40,6 +41,7 @@ pub use ir::memflags::MemFlags;
|
||||
pub use ir::progpoint::{ExpandedProgramPoint, ProgramOrder, ProgramPoint};
|
||||
pub use ir::sourceloc::SourceLoc;
|
||||
pub use ir::stackslot::{StackSlotData, StackSlotKind, StackSlots};
|
||||
pub use ir::table::TableData;
|
||||
pub use ir::trapcode::TrapCode;
|
||||
pub use ir::types::Type;
|
||||
pub use ir::valueloc::{ArgumentLoc, ValueLoc};
|
||||
|
||||
32
lib/codegen/src/ir/table.rs
Normal file
32
lib/codegen/src/ir/table.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
//! Tables.
|
||||
|
||||
use ir::immediates::Imm64;
|
||||
use ir::GlobalValue;
|
||||
use std::fmt;
|
||||
|
||||
/// Information about a table declaration.
|
||||
#[derive(Clone)]
|
||||
pub struct TableData {
|
||||
/// Global value giving the address of the start of the table.
|
||||
pub base_gv: GlobalValue,
|
||||
|
||||
/// Guaranteed minimum table size in elements. Table accesses before `min_size` don't need
|
||||
/// bounds checking.
|
||||
pub min_size: Imm64,
|
||||
|
||||
/// Global value giving the current bound of the table, in elements.
|
||||
pub bound_gv: GlobalValue,
|
||||
|
||||
/// The size of a table element, in bytes.
|
||||
pub element_size: Imm64,
|
||||
}
|
||||
|
||||
impl fmt::Display for TableData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}, min {}, bound {}, element_size {}",
|
||||
self.base_gv, self.min_size, self.bound_gv, self.element_size
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,13 @@ pub enum TrapCode {
|
||||
|
||||
/// A `heap_addr` instruction detected an out-of-bounds error.
|
||||
///
|
||||
/// Some out-of-bounds heap accesses are detected by a segmentation fault on the heap guard
|
||||
/// pages.
|
||||
/// Note that not all out-of-bounds heap accesses are reported this way;
|
||||
/// some are detected by a segmentation fault on the heap guard pages.
|
||||
HeapOutOfBounds,
|
||||
|
||||
/// A `table_addr` instruction detected an out-of-bounds error.
|
||||
TableOutOfBounds,
|
||||
|
||||
/// Other bounds checking error.
|
||||
OutOfBounds,
|
||||
|
||||
@@ -52,6 +55,7 @@ impl Display for TrapCode {
|
||||
let identifier = match *self {
|
||||
StackOverflow => "stk_ovf",
|
||||
HeapOutOfBounds => "heap_oob",
|
||||
TableOutOfBounds => "table_oob",
|
||||
OutOfBounds => "oob",
|
||||
IndirectCallToNull => "icall_null",
|
||||
BadSignature => "bad_sig",
|
||||
@@ -73,6 +77,7 @@ impl FromStr for TrapCode {
|
||||
match s {
|
||||
"stk_ovf" => Ok(StackOverflow),
|
||||
"heap_oob" => Ok(HeapOutOfBounds),
|
||||
"table_oob" => Ok(TableOutOfBounds),
|
||||
"oob" => Ok(OutOfBounds),
|
||||
"icall_null" => Ok(IndirectCallToNull),
|
||||
"bad_sig" => Ok(BadSignature),
|
||||
@@ -92,9 +97,10 @@ mod tests {
|
||||
use std::string::ToString;
|
||||
|
||||
// Everything but user-defined codes.
|
||||
const CODES: [TrapCode; 8] = [
|
||||
const CODES: [TrapCode; 9] = [
|
||||
TrapCode::StackOverflow,
|
||||
TrapCode::HeapOutOfBounds,
|
||||
TrapCode::TableOutOfBounds,
|
||||
TrapCode::OutOfBounds,
|
||||
TrapCode::IndirectCallToNull,
|
||||
TrapCode::BadSignature,
|
||||
|
||||
@@ -82,7 +82,7 @@ fn dynamic_addr(
|
||||
}
|
||||
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
|
||||
|
||||
offset_addr(inst, heap, addr_ty, offset, offset_ty, pos.func);
|
||||
compute_addr(inst, heap, addr_ty, offset, offset_ty, pos.func);
|
||||
}
|
||||
|
||||
/// Expand a `heap_addr` for a static heap.
|
||||
@@ -134,13 +134,11 @@ fn static_addr(
|
||||
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
|
||||
}
|
||||
|
||||
offset_addr(inst, heap, addr_ty, offset, offset_ty, pos.func);
|
||||
compute_addr(inst, heap, addr_ty, offset, offset_ty, pos.func);
|
||||
}
|
||||
|
||||
/// Emit code for the base address computation of a `heap_addr` instruction.
|
||||
///
|
||||
///
|
||||
fn offset_addr(
|
||||
fn compute_addr(
|
||||
inst: ir::Inst,
|
||||
heap: ir::Heap,
|
||||
addr_ty: ir::Type,
|
||||
|
||||
@@ -26,11 +26,13 @@ mod globalvalue;
|
||||
mod heap;
|
||||
mod libcall;
|
||||
mod split;
|
||||
mod table;
|
||||
|
||||
use self::call::expand_call;
|
||||
use self::globalvalue::expand_global_value;
|
||||
use self::heap::expand_heap_addr;
|
||||
use self::libcall::expand_as_libcall;
|
||||
use self::table::expand_table_addr;
|
||||
|
||||
/// Legalize `inst` for `isa`. Return true if any changes to the code were
|
||||
/// made; return false if the instruction was successfully encoded as is.
|
||||
|
||||
115
lib/codegen/src/legalizer/table.rs
Normal file
115
lib/codegen/src/legalizer/table.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
//! 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 cursor::{Cursor, FuncCursor};
|
||||
use flowgraph::ControlFlowGraph;
|
||||
use ir::condcodes::IntCC;
|
||||
use ir::immediates::Offset32;
|
||||
use ir::{self, InstBuilder};
|
||||
use 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: &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(addr_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_i64: i64 = element_size.into();
|
||||
debug_assert!(element_size_i64 >= 0);
|
||||
let element_size_u64 = element_size_i64 as u64;
|
||||
if element_size_u64 == 1 {
|
||||
offset = index;
|
||||
} else if element_size_u64.is_power_of_two() {
|
||||
offset = pos
|
||||
.ins()
|
||||
.ishl_imm(index, i64::from(element_size_u64.trailing_zeros()));
|
||||
} else {
|
||||
offset = pos.ins().imul_imm(index, element_size);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -349,6 +349,9 @@ impl<'a> Verifier<'a> {
|
||||
HeapAddr { heap, .. } => {
|
||||
self.verify_heap(inst, heap)?;
|
||||
}
|
||||
TableAddr { table, .. } => {
|
||||
self.verify_table(inst, table)?;
|
||||
}
|
||||
RegSpill { dst, .. } => {
|
||||
self.verify_stack_slot(inst, dst)?;
|
||||
}
|
||||
@@ -445,6 +448,14 @@ impl<'a> Verifier<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_table(&self, inst: Inst, table: ir::Table) -> VerifierResult<()> {
|
||||
if !self.func.tables.is_valid(table) {
|
||||
err!(inst, "invalid table {}", table)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_value_list(&self, inst: Inst, l: &ValueList) -> VerifierResult<()> {
|
||||
if !l.is_valid(&self.func.dfg.value_lists) {
|
||||
err!(inst, "invalid value list reference {:?}", l)
|
||||
|
||||
@@ -440,6 +440,7 @@ pub fn write_operands(
|
||||
..
|
||||
} => write!(w, " {}, {}{}", arg, stack_slot, offset),
|
||||
HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
|
||||
TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
|
||||
Load {
|
||||
flags, arg, offset, ..
|
||||
} => write!(w, "{} {}{}", flags, arg, offset),
|
||||
|
||||
Reference in New Issue
Block a user