Define a "table" concept.
"Table" is to WebAssembly tables as "Heap" is to WebAssembly linear memories.
This commit is contained in:
27
cranelift/filetests/isa/x86/legalize-table.clif
Normal file
27
cranelift/filetests/isa/x86/legalize-table.clif
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
; Test legalization of tables
|
||||||
|
test legalizer
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
; regex: V=v\d+
|
||||||
|
; regex: EBB=ebb\d+
|
||||||
|
|
||||||
|
function %test0(i64 vmctx, i64) -> i64 {
|
||||||
|
gv0 = vmctx+12
|
||||||
|
gv1 = vmctx+14
|
||||||
|
table0 = dynamic gv0, min 20, bound gv1, element_size 4
|
||||||
|
|
||||||
|
ebb0(v0: i64, v1: i64):
|
||||||
|
v2 = table_addr.i64 table0, v1, +3
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
|
||||||
|
; check: $(bound=$V) = iadd_imm $(input=$V), 14
|
||||||
|
; nextln: $(cond=$V) = icmp uge $(limit=$V), $bound
|
||||||
|
; nextln: brz $cond, ebb1
|
||||||
|
; nextln: trap table_oob
|
||||||
|
; nextln:
|
||||||
|
; nextln: ebb1:
|
||||||
|
; nextln: $(base=$V) = iadd_imm.i64 $(vmctx=$V), 12
|
||||||
|
; nextln: $(scaled=$V) = ishl_imm.i64 $(index=$V), 2
|
||||||
|
; nextln: $(elem_addr=$V) = iadd $base, $scaled
|
||||||
|
; nextln: $(field_addr=$V) = iadd_imm $elem_addr, 3
|
||||||
@@ -33,3 +33,6 @@ jump_table = EntityRefKind(
|
|||||||
|
|
||||||
#: A reference to a heap declared in the function preamble.
|
#: A reference to a heap declared in the function preamble.
|
||||||
heap = EntityRefKind('heap', 'A heap.')
|
heap = EntityRefKind('heap', 'A heap.')
|
||||||
|
|
||||||
|
#: A reference to a table declared in the function preamble.
|
||||||
|
table = EntityRefKind('table', 'A table.')
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from cdsl.operands import VALUE, VARIABLE_ARGS
|
|||||||
from .immediates import imm64, uimm8, uimm32, ieee32, ieee64, offset32
|
from .immediates import imm64, uimm8, uimm32, ieee32, ieee64, offset32
|
||||||
from .immediates import boolean, intcc, floatcc, memflags, regunit, trapcode
|
from .immediates import boolean, intcc, floatcc, memflags, regunit, trapcode
|
||||||
from . import entities
|
from . import entities
|
||||||
from .entities import ebb, sig_ref, func_ref, stack_slot, heap
|
from .entities import ebb, sig_ref, func_ref, stack_slot, heap, table
|
||||||
|
|
||||||
Unary = InstructionFormat(VALUE)
|
Unary = InstructionFormat(VALUE)
|
||||||
UnaryImm = InstructionFormat(imm64)
|
UnaryImm = InstructionFormat(imm64)
|
||||||
@@ -67,6 +67,9 @@ StackStore = InstructionFormat(VALUE, stack_slot, offset32)
|
|||||||
# Accessing a WebAssembly heap.
|
# Accessing a WebAssembly heap.
|
||||||
HeapAddr = InstructionFormat(heap, VALUE, uimm32)
|
HeapAddr = InstructionFormat(heap, VALUE, uimm32)
|
||||||
|
|
||||||
|
# Accessing a WebAssembly table.
|
||||||
|
TableAddr = InstructionFormat(table, VALUE, offset32)
|
||||||
|
|
||||||
RegMove = InstructionFormat(VALUE, ('src', regunit), ('dst', regunit))
|
RegMove = InstructionFormat(VALUE, ('src', regunit), ('dst', regunit))
|
||||||
CopySpecial = InstructionFormat(('src', regunit), ('dst', regunit))
|
CopySpecial = InstructionFormat(('src', regunit), ('dst', regunit))
|
||||||
RegSpill = InstructionFormat(
|
RegSpill = InstructionFormat(
|
||||||
|
|||||||
@@ -221,6 +221,11 @@ call_indirect = Instruction(
|
|||||||
|
|
||||||
Call the function pointed to by `callee` with the given arguments. The
|
Call the function pointed to by `callee` with the given arguments. The
|
||||||
called function must match the specified signature.
|
called function must match the specified signature.
|
||||||
|
|
||||||
|
Note that this is different from WebAssembly's ``call_indirect``; the
|
||||||
|
callee is a native address, rather than a table index. For WebAssembly,
|
||||||
|
:inst:`table_addr` and :inst:`load` are used to obtain a native address
|
||||||
|
from a table.
|
||||||
""",
|
""",
|
||||||
ins=(SIG, callee, args), outs=rvals, is_call=True)
|
ins=(SIG, callee, args), outs=rvals, is_call=True)
|
||||||
|
|
||||||
@@ -531,6 +536,33 @@ heap_addr = Instruction(
|
|||||||
""",
|
""",
|
||||||
ins=(H, p, Size), outs=addr)
|
ins=(H, p, Size), outs=addr)
|
||||||
|
|
||||||
|
#
|
||||||
|
# WebAssembly bounds-checked table accesses.
|
||||||
|
#
|
||||||
|
|
||||||
|
TableOffset = TypeVar('TableOffset', 'An unsigned table offset', ints=(32, 64))
|
||||||
|
|
||||||
|
T = Operand('T', entities.table)
|
||||||
|
p = Operand('p', TableOffset)
|
||||||
|
Offset = Operand('Offset', offset32, 'Byte offset from element address')
|
||||||
|
|
||||||
|
table_addr = Instruction(
|
||||||
|
'table_addr', r"""
|
||||||
|
Bounds check and compute absolute address of a table entry.
|
||||||
|
|
||||||
|
Verify that the offset ``p`` is in bounds for the table T, and generate
|
||||||
|
an absolute address that is safe to dereference.
|
||||||
|
|
||||||
|
``Offset`` must be less than the size of a table element.
|
||||||
|
|
||||||
|
1. If ``p`` is not greater than the table bound, return an absolute
|
||||||
|
address corresponding to a byte offset of ``p`` from the table's
|
||||||
|
base address.
|
||||||
|
2. If ``p`` is greater than the table bound, generate a trap.
|
||||||
|
""",
|
||||||
|
ins=(T, p, Offset), outs=addr)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Materializing constants.
|
# Materializing constants.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ expand_flags = XFormGroup('expand_flags', """
|
|||||||
# Custom expansions for memory objects.
|
# Custom expansions for memory objects.
|
||||||
expand.custom_legalize(insts.global_value, 'expand_global_value')
|
expand.custom_legalize(insts.global_value, 'expand_global_value')
|
||||||
expand.custom_legalize(insts.heap_addr, 'expand_heap_addr')
|
expand.custom_legalize(insts.heap_addr, 'expand_heap_addr')
|
||||||
|
expand.custom_legalize(insts.table_addr, 'expand_table_addr')
|
||||||
|
|
||||||
# Custom expansions for calls.
|
# Custom expansions for calls.
|
||||||
expand.custom_legalize(insts.call, 'expand_call')
|
expand.custom_legalize(insts.call, 'expand_call')
|
||||||
|
|||||||
@@ -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.
|
/// A reference to any of the entities defined in this module.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum AnyEntity {
|
pub enum AnyEntity {
|
||||||
@@ -195,6 +213,8 @@ pub enum AnyEntity {
|
|||||||
SigRef(SigRef),
|
SigRef(SigRef),
|
||||||
/// A heap.
|
/// A heap.
|
||||||
Heap(Heap),
|
Heap(Heap),
|
||||||
|
/// A table.
|
||||||
|
Table(Table),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for AnyEntity {
|
impl fmt::Display for AnyEntity {
|
||||||
@@ -210,6 +230,7 @@ impl fmt::Display for AnyEntity {
|
|||||||
AnyEntity::FuncRef(r) => r.fmt(f),
|
AnyEntity::FuncRef(r) => r.fmt(f),
|
||||||
AnyEntity::SigRef(r) => r.fmt(f),
|
AnyEntity::SigRef(r) => r.fmt(f),
|
||||||
AnyEntity::Heap(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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use ir;
|
|||||||
use ir::{DataFlowGraph, ExternalName, Layout, Signature};
|
use ir::{DataFlowGraph, ExternalName, Layout, Signature};
|
||||||
use ir::{
|
use ir::{
|
||||||
Ebb, ExtFuncData, FuncRef, GlobalValue, GlobalValueData, Heap, HeapData, JumpTable,
|
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 ir::{EbbOffsets, InstEncodings, JumpTables, SourceLocs, StackSlots, ValueLocations};
|
||||||
use isa::{EncInfo, Encoding, Legalize, TargetIsa};
|
use isa::{EncInfo, Encoding, Legalize, TargetIsa};
|
||||||
@@ -42,6 +42,9 @@ pub struct Function {
|
|||||||
/// Heaps referenced.
|
/// Heaps referenced.
|
||||||
pub heaps: PrimaryMap<ir::Heap, ir::HeapData>,
|
pub heaps: PrimaryMap<ir::Heap, ir::HeapData>,
|
||||||
|
|
||||||
|
/// Tables referenced.
|
||||||
|
pub tables: PrimaryMap<ir::Table, ir::TableData>,
|
||||||
|
|
||||||
/// Jump tables used in this function.
|
/// Jump tables used in this function.
|
||||||
pub jump_tables: JumpTables,
|
pub jump_tables: JumpTables,
|
||||||
|
|
||||||
@@ -82,6 +85,7 @@ impl Function {
|
|||||||
stack_limit: None,
|
stack_limit: None,
|
||||||
global_values: PrimaryMap::new(),
|
global_values: PrimaryMap::new(),
|
||||||
heaps: PrimaryMap::new(),
|
heaps: PrimaryMap::new(),
|
||||||
|
tables: PrimaryMap::new(),
|
||||||
jump_tables: PrimaryMap::new(),
|
jump_tables: PrimaryMap::new(),
|
||||||
dfg: DataFlowGraph::new(),
|
dfg: DataFlowGraph::new(),
|
||||||
layout: Layout::new(),
|
layout: Layout::new(),
|
||||||
@@ -98,6 +102,7 @@ impl Function {
|
|||||||
self.stack_slots.clear();
|
self.stack_slots.clear();
|
||||||
self.global_values.clear();
|
self.global_values.clear();
|
||||||
self.heaps.clear();
|
self.heaps.clear();
|
||||||
|
self.tables.clear();
|
||||||
self.jump_tables.clear();
|
self.jump_tables.clear();
|
||||||
self.dfg.clear();
|
self.dfg.clear();
|
||||||
self.layout.clear();
|
self.layout.clear();
|
||||||
@@ -157,6 +162,11 @@ impl Function {
|
|||||||
self.heaps.push(data)
|
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.
|
/// 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> {
|
pub fn display<'a, I: Into<Option<&'a TargetIsa>>>(&'a self, isa: I) -> DisplayFunction<'a> {
|
||||||
DisplayFunction(self, isa.into())
|
DisplayFunction(self, isa.into())
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ mod memflags;
|
|||||||
mod progpoint;
|
mod progpoint;
|
||||||
mod sourceloc;
|
mod sourceloc;
|
||||||
pub mod stackslot;
|
pub mod stackslot;
|
||||||
|
mod table;
|
||||||
mod trapcode;
|
mod trapcode;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
mod valueloc;
|
mod valueloc;
|
||||||
@@ -25,7 +26,7 @@ mod valueloc;
|
|||||||
pub use ir::builder::{InsertBuilder, InstBuilder, InstBuilderBase, InstInserterBase};
|
pub use ir::builder::{InsertBuilder, InstBuilder, InstBuilderBase, InstInserterBase};
|
||||||
pub use ir::dfg::{DataFlowGraph, ValueDef};
|
pub use ir::dfg::{DataFlowGraph, ValueDef};
|
||||||
pub use ir::entities::{
|
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::extfunc::{AbiParam, ArgumentExtension, ArgumentPurpose, ExtFuncData, Signature};
|
||||||
pub use ir::extname::ExternalName;
|
pub use ir::extname::ExternalName;
|
||||||
@@ -40,6 +41,7 @@ pub use ir::memflags::MemFlags;
|
|||||||
pub use ir::progpoint::{ExpandedProgramPoint, ProgramOrder, ProgramPoint};
|
pub use ir::progpoint::{ExpandedProgramPoint, ProgramOrder, ProgramPoint};
|
||||||
pub use ir::sourceloc::SourceLoc;
|
pub use ir::sourceloc::SourceLoc;
|
||||||
pub use ir::stackslot::{StackSlotData, StackSlotKind, StackSlots};
|
pub use ir::stackslot::{StackSlotData, StackSlotKind, StackSlots};
|
||||||
|
pub use ir::table::TableData;
|
||||||
pub use ir::trapcode::TrapCode;
|
pub use ir::trapcode::TrapCode;
|
||||||
pub use ir::types::Type;
|
pub use ir::types::Type;
|
||||||
pub use ir::valueloc::{ArgumentLoc, ValueLoc};
|
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.
|
/// 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
|
/// Note that not all out-of-bounds heap accesses are reported this way;
|
||||||
/// pages.
|
/// some are detected by a segmentation fault on the heap guard pages.
|
||||||
HeapOutOfBounds,
|
HeapOutOfBounds,
|
||||||
|
|
||||||
|
/// A `table_addr` instruction detected an out-of-bounds error.
|
||||||
|
TableOutOfBounds,
|
||||||
|
|
||||||
/// Other bounds checking error.
|
/// Other bounds checking error.
|
||||||
OutOfBounds,
|
OutOfBounds,
|
||||||
|
|
||||||
@@ -52,6 +55,7 @@ impl Display for TrapCode {
|
|||||||
let identifier = match *self {
|
let identifier = match *self {
|
||||||
StackOverflow => "stk_ovf",
|
StackOverflow => "stk_ovf",
|
||||||
HeapOutOfBounds => "heap_oob",
|
HeapOutOfBounds => "heap_oob",
|
||||||
|
TableOutOfBounds => "table_oob",
|
||||||
OutOfBounds => "oob",
|
OutOfBounds => "oob",
|
||||||
IndirectCallToNull => "icall_null",
|
IndirectCallToNull => "icall_null",
|
||||||
BadSignature => "bad_sig",
|
BadSignature => "bad_sig",
|
||||||
@@ -73,6 +77,7 @@ impl FromStr for TrapCode {
|
|||||||
match s {
|
match s {
|
||||||
"stk_ovf" => Ok(StackOverflow),
|
"stk_ovf" => Ok(StackOverflow),
|
||||||
"heap_oob" => Ok(HeapOutOfBounds),
|
"heap_oob" => Ok(HeapOutOfBounds),
|
||||||
|
"table_oob" => Ok(TableOutOfBounds),
|
||||||
"oob" => Ok(OutOfBounds),
|
"oob" => Ok(OutOfBounds),
|
||||||
"icall_null" => Ok(IndirectCallToNull),
|
"icall_null" => Ok(IndirectCallToNull),
|
||||||
"bad_sig" => Ok(BadSignature),
|
"bad_sig" => Ok(BadSignature),
|
||||||
@@ -92,9 +97,10 @@ mod tests {
|
|||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
// Everything but user-defined codes.
|
// Everything but user-defined codes.
|
||||||
const CODES: [TrapCode; 8] = [
|
const CODES: [TrapCode; 9] = [
|
||||||
TrapCode::StackOverflow,
|
TrapCode::StackOverflow,
|
||||||
TrapCode::HeapOutOfBounds,
|
TrapCode::HeapOutOfBounds,
|
||||||
|
TrapCode::TableOutOfBounds,
|
||||||
TrapCode::OutOfBounds,
|
TrapCode::OutOfBounds,
|
||||||
TrapCode::IndirectCallToNull,
|
TrapCode::IndirectCallToNull,
|
||||||
TrapCode::BadSignature,
|
TrapCode::BadSignature,
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ fn dynamic_addr(
|
|||||||
}
|
}
|
||||||
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
|
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.
|
/// Expand a `heap_addr` for a static heap.
|
||||||
@@ -134,13 +134,11 @@ fn static_addr(
|
|||||||
pos.ins().trapnz(oob, ir::TrapCode::HeapOutOfBounds);
|
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.
|
/// Emit code for the base address computation of a `heap_addr` instruction.
|
||||||
///
|
fn compute_addr(
|
||||||
///
|
|
||||||
fn offset_addr(
|
|
||||||
inst: ir::Inst,
|
inst: ir::Inst,
|
||||||
heap: ir::Heap,
|
heap: ir::Heap,
|
||||||
addr_ty: ir::Type,
|
addr_ty: ir::Type,
|
||||||
|
|||||||
@@ -26,11 +26,13 @@ mod globalvalue;
|
|||||||
mod heap;
|
mod heap;
|
||||||
mod libcall;
|
mod libcall;
|
||||||
mod split;
|
mod split;
|
||||||
|
mod table;
|
||||||
|
|
||||||
use self::call::expand_call;
|
use self::call::expand_call;
|
||||||
use self::globalvalue::expand_global_value;
|
use self::globalvalue::expand_global_value;
|
||||||
use self::heap::expand_heap_addr;
|
use self::heap::expand_heap_addr;
|
||||||
use self::libcall::expand_as_libcall;
|
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
|
/// Legalize `inst` for `isa`. Return true if any changes to the code were
|
||||||
/// made; return false if the instruction was successfully encoded as is.
|
/// 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, .. } => {
|
HeapAddr { heap, .. } => {
|
||||||
self.verify_heap(inst, heap)?;
|
self.verify_heap(inst, heap)?;
|
||||||
}
|
}
|
||||||
|
TableAddr { table, .. } => {
|
||||||
|
self.verify_table(inst, table)?;
|
||||||
|
}
|
||||||
RegSpill { dst, .. } => {
|
RegSpill { dst, .. } => {
|
||||||
self.verify_stack_slot(inst, 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<()> {
|
fn verify_value_list(&self, inst: Inst, l: &ValueList) -> VerifierResult<()> {
|
||||||
if !l.is_valid(&self.func.dfg.value_lists) {
|
if !l.is_valid(&self.func.dfg.value_lists) {
|
||||||
err!(inst, "invalid value list reference {:?}", l)
|
err!(inst, "invalid value list reference {:?}", l)
|
||||||
|
|||||||
@@ -440,6 +440,7 @@ pub fn write_operands(
|
|||||||
..
|
..
|
||||||
} => write!(w, " {}, {}{}", arg, stack_slot, offset),
|
} => write!(w, " {}, {}{}", arg, stack_slot, offset),
|
||||||
HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
|
HeapAddr { heap, arg, imm, .. } => write!(w, " {}, {}, {}", heap, arg, imm),
|
||||||
|
TableAddr { table, arg, .. } => write!(w, " {}, {}", table, arg),
|
||||||
Load {
|
Load {
|
||||||
flags, arg, offset, ..
|
flags, arg, offset, ..
|
||||||
} => write!(w, "{} {}{}", flags, arg, offset),
|
} => write!(w, "{} {}{}", flags, arg, offset),
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ pub enum Token<'a> {
|
|||||||
StackSlot(u32), // ss3
|
StackSlot(u32), // ss3
|
||||||
GlobalValue(u32), // gv3
|
GlobalValue(u32), // gv3
|
||||||
Heap(u32), // heap2
|
Heap(u32), // heap2
|
||||||
|
Table(u32), // table2
|
||||||
JumpTable(u32), // jt2
|
JumpTable(u32), // jt2
|
||||||
FuncRef(u32), // fn2
|
FuncRef(u32), // fn2
|
||||||
SigRef(u32), // sig2
|
SigRef(u32), // sig2
|
||||||
@@ -340,6 +341,7 @@ impl<'a> Lexer<'a> {
|
|||||||
"ss" => Some(Token::StackSlot(number)),
|
"ss" => Some(Token::StackSlot(number)),
|
||||||
"gv" => Some(Token::GlobalValue(number)),
|
"gv" => Some(Token::GlobalValue(number)),
|
||||||
"heap" => Some(Token::Heap(number)),
|
"heap" => Some(Token::Heap(number)),
|
||||||
|
"table" => Some(Token::Table(number)),
|
||||||
"jt" => Some(Token::JumpTable(number)),
|
"jt" => Some(Token::JumpTable(number)),
|
||||||
"fn" => Some(Token::FuncRef(number)),
|
"fn" => Some(Token::FuncRef(number)),
|
||||||
"sig" => Some(Token::SigRef(number)),
|
"sig" => Some(Token::SigRef(number)),
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ use cranelift_codegen::ir::types::VOID;
|
|||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
AbiParam, ArgumentExtension, ArgumentLoc, Ebb, ExtFuncData, ExternalName, FuncRef, Function,
|
AbiParam, ArgumentExtension, ArgumentLoc, Ebb, ExtFuncData, ExternalName, FuncRef, Function,
|
||||||
GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle, JumpTable, JumpTableData, MemFlags,
|
GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle, JumpTable, JumpTableData, MemFlags,
|
||||||
Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, Type, Value, ValueLoc,
|
Opcode, SigRef, Signature, StackSlot, StackSlotData, StackSlotKind, Table, TableData, Type,
|
||||||
|
Value, ValueLoc,
|
||||||
};
|
};
|
||||||
use cranelift_codegen::isa::{self, Encoding, RegUnit, TargetIsa};
|
use cranelift_codegen::isa::{self, Encoding, RegUnit, TargetIsa};
|
||||||
use cranelift_codegen::packed_option::ReservedValue;
|
use cranelift_codegen::packed_option::ReservedValue;
|
||||||
@@ -188,6 +189,29 @@ impl<'a> Context<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allocate a table slot.
|
||||||
|
fn add_table(&mut self, table: Table, data: TableData, loc: Location) -> ParseResult<()> {
|
||||||
|
while self.function.tables.next_key().index() <= table.index() {
|
||||||
|
self.function.create_table(TableData {
|
||||||
|
base_gv: GlobalValue::reserved_value(),
|
||||||
|
min_size: Imm64::new(0),
|
||||||
|
bound_gv: GlobalValue::reserved_value(),
|
||||||
|
element_size: Imm64::new(0),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.function.tables[table] = data;
|
||||||
|
self.map.def_table(table, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve a reference to a table.
|
||||||
|
fn check_table(&self, table: Table, loc: &Location) -> ParseResult<()> {
|
||||||
|
if !self.map.contains_table(table) {
|
||||||
|
err!(loc, "undefined table {}", table)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate a new signature.
|
// Allocate a new signature.
|
||||||
fn add_sig(&mut self, sig: SigRef, data: Signature, loc: Location) -> ParseResult<()> {
|
fn add_sig(&mut self, sig: SigRef, data: Signature, loc: Location) -> ParseResult<()> {
|
||||||
self.map.def_sig(sig, loc)?;
|
self.map.def_sig(sig, loc)?;
|
||||||
@@ -447,6 +471,17 @@ impl<'a> Parser<'a> {
|
|||||||
err!(self.loc, err_msg)
|
err!(self.loc, err_msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match and consume a table reference.
|
||||||
|
fn match_table(&mut self, err_msg: &str) -> ParseResult<Table> {
|
||||||
|
if let Some(Token::Table(table)) = self.token() {
|
||||||
|
self.consume();
|
||||||
|
if let Some(table) = Table::with_number(table) {
|
||||||
|
return Ok(table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err!(self.loc, err_msg)
|
||||||
|
}
|
||||||
|
|
||||||
// Match and consume a jump table reference.
|
// Match and consume a jump table reference.
|
||||||
fn match_jt(&mut self) -> ParseResult<JumpTable> {
|
fn match_jt(&mut self) -> ParseResult<JumpTable> {
|
||||||
if let Some(Token::JumpTable(jt)) = self.token() {
|
if let Some(Token::JumpTable(jt)) = self.token() {
|
||||||
@@ -1019,6 +1054,11 @@ impl<'a> Parser<'a> {
|
|||||||
self.parse_heap_decl()
|
self.parse_heap_decl()
|
||||||
.and_then(|(heap, dat)| ctx.add_heap(heap, dat, self.loc))
|
.and_then(|(heap, dat)| ctx.add_heap(heap, dat, self.loc))
|
||||||
}
|
}
|
||||||
|
Some(Token::Table(..)) => {
|
||||||
|
self.start_gathering_comments();
|
||||||
|
self.parse_table_decl()
|
||||||
|
.and_then(|(table, dat)| ctx.add_table(table, dat, self.loc))
|
||||||
|
}
|
||||||
Some(Token::SigRef(..)) => {
|
Some(Token::SigRef(..)) => {
|
||||||
self.start_gathering_comments();
|
self.start_gathering_comments();
|
||||||
self.parse_signature_decl(ctx.unique_isa)
|
self.parse_signature_decl(ctx.unique_isa)
|
||||||
@@ -1129,7 +1169,7 @@ impl<'a> Parser<'a> {
|
|||||||
// heap-style ::= "static" | "dynamic"
|
// heap-style ::= "static" | "dynamic"
|
||||||
// heap-base ::= GlobalValue(base)
|
// heap-base ::= GlobalValue(base)
|
||||||
// heap-attr ::= "min" Imm64(bytes)
|
// heap-attr ::= "min" Imm64(bytes)
|
||||||
// | "max" Imm64(bytes)
|
// | "bound" Imm64(bytes)
|
||||||
// | "guard" Imm64(bytes)
|
// | "guard" Imm64(bytes)
|
||||||
//
|
//
|
||||||
fn parse_heap_decl(&mut self) -> ParseResult<(Heap, HeapData)> {
|
fn parse_heap_decl(&mut self) -> ParseResult<(Heap, HeapData)> {
|
||||||
@@ -1187,6 +1227,66 @@ impl<'a> Parser<'a> {
|
|||||||
Ok((heap, data))
|
Ok((heap, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse a table decl.
|
||||||
|
//
|
||||||
|
// table-decl ::= * Table(table) "=" table-desc
|
||||||
|
// table-desc ::= table-style table-base { "," table-attr }
|
||||||
|
// table-style ::= "dynamic"
|
||||||
|
// table-base ::= GlobalValue(base)
|
||||||
|
// table-attr ::= "min" Imm64(bytes)
|
||||||
|
// | "bound" Imm64(bytes)
|
||||||
|
// | "element_size" Imm64(bytes)
|
||||||
|
//
|
||||||
|
fn parse_table_decl(&mut self) -> ParseResult<(Table, TableData)> {
|
||||||
|
let table = self.match_table("expected table number: table«n»")?;
|
||||||
|
self.match_token(Token::Equal, "expected '=' in table declaration")?;
|
||||||
|
|
||||||
|
let style_name = self.match_any_identifier("expected 'static' or 'dynamic'")?;
|
||||||
|
|
||||||
|
// table-desc ::= table-style * table-base { "," table-attr }
|
||||||
|
// table-base ::= * GlobalValue(base)
|
||||||
|
let base = match self.token() {
|
||||||
|
Some(Token::GlobalValue(base_num)) => match GlobalValue::with_number(base_num) {
|
||||||
|
Some(gv) => gv,
|
||||||
|
None => return err!(self.loc, "invalid global value number for table base"),
|
||||||
|
},
|
||||||
|
_ => return err!(self.loc, "expected table base"),
|
||||||
|
};
|
||||||
|
self.consume();
|
||||||
|
|
||||||
|
let mut data = TableData {
|
||||||
|
base_gv: base,
|
||||||
|
min_size: 0.into(),
|
||||||
|
bound_gv: GlobalValue::reserved_value(),
|
||||||
|
element_size: 0.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// table-desc ::= * { "," table-attr }
|
||||||
|
while self.optional(Token::Comma) {
|
||||||
|
match self.match_any_identifier("expected table attribute name")? {
|
||||||
|
"min" => {
|
||||||
|
data.min_size = self.match_imm64("expected integer min size")?;
|
||||||
|
}
|
||||||
|
"bound" => {
|
||||||
|
data.bound_gv = match style_name {
|
||||||
|
"dynamic" => self.match_gv("expected gv bound")?,
|
||||||
|
t => return err!(self.loc, "unknown table style '{}'", t),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
"element_size" => {
|
||||||
|
data.element_size = self.match_imm64("expected integer element size")?;
|
||||||
|
}
|
||||||
|
t => return err!(self.loc, "unknown table attribute '{}'", t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect any trailing comments.
|
||||||
|
self.token();
|
||||||
|
self.claim_gathered_comments(table);
|
||||||
|
|
||||||
|
Ok((table, data))
|
||||||
|
}
|
||||||
|
|
||||||
// Parse a signature decl.
|
// Parse a signature decl.
|
||||||
//
|
//
|
||||||
// signature-decl ::= SigRef(sigref) "=" signature
|
// signature-decl ::= SigRef(sigref) "=" signature
|
||||||
@@ -2155,6 +2255,20 @@ impl<'a> Parser<'a> {
|
|||||||
imm,
|
imm,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
InstructionFormat::TableAddr => {
|
||||||
|
let table = self.match_table("expected table identifier")?;
|
||||||
|
ctx.check_table(table, &self.loc)?;
|
||||||
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
|
let arg = self.match_value("expected SSA value table address")?;
|
||||||
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
|
let offset = self.optional_offset32()?;
|
||||||
|
InstructionData::TableAddr {
|
||||||
|
opcode,
|
||||||
|
table,
|
||||||
|
arg,
|
||||||
|
offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
InstructionFormat::Load => {
|
InstructionFormat::Load => {
|
||||||
let flags = self.optional_memflags();
|
let flags = self.optional_memflags();
|
||||||
let addr = self.match_value("expected SSA value address")?;
|
let addr = self.match_value("expected SSA value address")?;
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
//! to parser clients.
|
//! to parser clients.
|
||||||
|
|
||||||
use cranelift_codegen::ir::entities::AnyEntity;
|
use cranelift_codegen::ir::entities::AnyEntity;
|
||||||
use cranelift_codegen::ir::{Ebb, FuncRef, GlobalValue, Heap, JumpTable, SigRef, StackSlot, Value};
|
use cranelift_codegen::ir::{
|
||||||
|
Ebb, FuncRef, GlobalValue, Heap, JumpTable, SigRef, StackSlot, Table, Value,
|
||||||
|
};
|
||||||
use error::{Location, ParseResult};
|
use error::{Location, ParseResult};
|
||||||
use lexer::split_entity_name;
|
use lexer::split_entity_name;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -46,6 +48,11 @@ impl SourceMap {
|
|||||||
self.locations.contains_key(&heap.into())
|
self.locations.contains_key(&heap.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Look up a table entity.
|
||||||
|
pub fn contains_table(&self, table: Table) -> bool {
|
||||||
|
self.locations.contains_key(&table.into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Look up a signature entity.
|
/// Look up a signature entity.
|
||||||
pub fn contains_sig(&self, sig: SigRef) -> bool {
|
pub fn contains_sig(&self, sig: SigRef) -> bool {
|
||||||
self.locations.contains_key(&sig.into())
|
self.locations.contains_key(&sig.into())
|
||||||
@@ -100,6 +107,13 @@ impl SourceMap {
|
|||||||
Some(heap.into())
|
Some(heap.into())
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
"table" => Table::with_number(num).and_then(|table| {
|
||||||
|
if !self.contains_table(table) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(table.into())
|
||||||
|
}
|
||||||
|
}),
|
||||||
"sig" => SigRef::with_number(num).and_then(|sig| {
|
"sig" => SigRef::with_number(num).and_then(|sig| {
|
||||||
if !self.contains_sig(sig) {
|
if !self.contains_sig(sig) {
|
||||||
None
|
None
|
||||||
@@ -164,6 +178,11 @@ impl SourceMap {
|
|||||||
self.def_entity(entity.into(), loc)
|
self.def_entity(entity.into(), loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Define the table `entity`.
|
||||||
|
pub fn def_table(&mut self, entity: Table, loc: Location) -> ParseResult<()> {
|
||||||
|
self.def_entity(entity.into(), loc)
|
||||||
|
}
|
||||||
|
|
||||||
/// Define the signature `entity`.
|
/// Define the signature `entity`.
|
||||||
pub fn def_sig(&mut self, entity: SigRef, loc: Location) -> ParseResult<()> {
|
pub fn def_sig(&mut self, entity: SigRef, loc: Location) -> ParseResult<()> {
|
||||||
self.def_entity(entity.into(), loc)
|
self.def_entity(entity.into(), loc)
|
||||||
|
|||||||
@@ -176,6 +176,12 @@ pub enum SerInstData {
|
|||||||
heap: String,
|
heap: String,
|
||||||
imm: String,
|
imm: String,
|
||||||
},
|
},
|
||||||
|
TableAddr {
|
||||||
|
opcode: String,
|
||||||
|
arg: String,
|
||||||
|
table: String,
|
||||||
|
offset: String,
|
||||||
|
},
|
||||||
RegMove {
|
RegMove {
|
||||||
opcode: String,
|
opcode: String,
|
||||||
arg: String,
|
arg: String,
|
||||||
@@ -572,6 +578,17 @@ pub fn get_inst_data(inst_index: Inst, func: &Function) -> SerInstData {
|
|||||||
heap: heap.to_string(),
|
heap: heap.to_string(),
|
||||||
imm: imm.to_string(),
|
imm: imm.to_string(),
|
||||||
},
|
},
|
||||||
|
InstructionData::TableAddr {
|
||||||
|
opcode,
|
||||||
|
arg,
|
||||||
|
table,
|
||||||
|
offset,
|
||||||
|
} => SerInstData::TableAddr {
|
||||||
|
opcode: opcode.to_string(),
|
||||||
|
arg: arg.to_string(),
|
||||||
|
table: table.to_string(),
|
||||||
|
offset: offset.to_string(),
|
||||||
|
},
|
||||||
InstructionData::RegMove {
|
InstructionData::RegMove {
|
||||||
opcode,
|
opcode,
|
||||||
arg,
|
arg,
|
||||||
|
|||||||
@@ -377,10 +377,12 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
|||||||
// `index` is the index of the function's signature and `table_index` is the index of
|
// `index` is the index of the function's signature and `table_index` is the index of
|
||||||
// the table to search the function in.
|
// the table to search the function in.
|
||||||
let (sigref, num_args) = state.get_indirect_sig(builder.func, index, environ);
|
let (sigref, num_args) = state.get_indirect_sig(builder.func, index, environ);
|
||||||
|
let table = state.get_table(builder.func, table_index, environ);
|
||||||
let callee = state.pop1();
|
let callee = state.pop1();
|
||||||
let call = environ.translate_call_indirect(
|
let call = environ.translate_call_indirect(
|
||||||
builder.cursor(),
|
builder.cursor(),
|
||||||
table_index as TableIndex,
|
table_index as TableIndex,
|
||||||
|
table,
|
||||||
index as SignatureIndex,
|
index as SignatureIndex,
|
||||||
sigref,
|
sigref,
|
||||||
callee,
|
callee,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! "Dummy" environment for testing wasm translation.
|
//! "Dummy" environment for testing wasm translation.
|
||||||
|
|
||||||
use cranelift_codegen::cursor::FuncCursor;
|
use cranelift_codegen::cursor::FuncCursor;
|
||||||
|
use cranelift_codegen::ir::immediates::Imm64;
|
||||||
use cranelift_codegen::ir::types::*;
|
use cranelift_codegen::ir::types::*;
|
||||||
use cranelift_codegen::ir::{self, InstBuilder};
|
use cranelift_codegen::ir::{self, InstBuilder};
|
||||||
use cranelift_codegen::settings;
|
use cranelift_codegen::settings;
|
||||||
@@ -169,7 +170,11 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
|||||||
|
|
||||||
fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
|
fn make_heap(&mut self, func: &mut ir::Function, _index: MemoryIndex) -> ir::Heap {
|
||||||
// Create a static heap whose base address is stored at `vmctx+0`.
|
// Create a static heap whose base address is stored at `vmctx+0`.
|
||||||
let gv = func.create_global_value(ir::GlobalValueData::VMContext { offset: 0.into() });
|
let addr = func.create_global_value(ir::GlobalValueData::VMContext { offset: 0.into() });
|
||||||
|
let gv = func.create_global_value(ir::GlobalValueData::Deref {
|
||||||
|
base: addr,
|
||||||
|
offset: 0.into(),
|
||||||
|
});
|
||||||
|
|
||||||
func.create_heap(ir::HeapData {
|
func.create_heap(ir::HeapData {
|
||||||
base: gv,
|
base: gv,
|
||||||
@@ -181,6 +186,29 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_table(&mut self, func: &mut ir::Function, _index: TableIndex) -> ir::Table {
|
||||||
|
// Create a table whose base address is stored at `vmctx+0`.
|
||||||
|
let base_gv_addr =
|
||||||
|
func.create_global_value(ir::GlobalValueData::VMContext { offset: 0.into() });
|
||||||
|
let base_gv = func.create_global_value(ir::GlobalValueData::Deref {
|
||||||
|
base: base_gv_addr,
|
||||||
|
offset: 0.into(),
|
||||||
|
});
|
||||||
|
let bound_gv_addr =
|
||||||
|
func.create_global_value(ir::GlobalValueData::VMContext { offset: 0.into() });
|
||||||
|
let bound_gv = func.create_global_value(ir::GlobalValueData::Deref {
|
||||||
|
base: bound_gv_addr,
|
||||||
|
offset: 0.into(),
|
||||||
|
});
|
||||||
|
|
||||||
|
func.create_table(ir::TableData {
|
||||||
|
base_gv,
|
||||||
|
min_size: Imm64::new(0),
|
||||||
|
bound_gv,
|
||||||
|
element_size: Imm64::new(i64::from(self.pointer_bytes()) * 2),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
|
fn make_indirect_sig(&mut self, func: &mut ir::Function, index: SignatureIndex) -> ir::SigRef {
|
||||||
// A real implementation would probably change the calling convention and add `vmctx` and
|
// A real implementation would probably change the calling convention and add `vmctx` and
|
||||||
// signature index arguments.
|
// signature index arguments.
|
||||||
@@ -204,6 +232,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
|||||||
&mut self,
|
&mut self,
|
||||||
mut pos: FuncCursor,
|
mut pos: FuncCursor,
|
||||||
_table_index: TableIndex,
|
_table_index: TableIndex,
|
||||||
|
_table: ir::Table,
|
||||||
_sig_index: SignatureIndex,
|
_sig_index: SignatureIndex,
|
||||||
sig_ref: ir::SigRef,
|
sig_ref: ir::SigRef,
|
||||||
callee: ir::Value,
|
callee: ir::Value,
|
||||||
|
|||||||
@@ -89,6 +89,11 @@ pub trait FuncEnvironment {
|
|||||||
ir::Type::int(u16::from(self.triple().pointer_width().unwrap().bits())).unwrap()
|
ir::Type::int(u16::from(self.triple().pointer_width().unwrap().bits())).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the size of a native pointer, in bytes.
|
||||||
|
fn pointer_bytes(&self) -> u8 {
|
||||||
|
self.triple().pointer_width().unwrap().bytes()
|
||||||
|
}
|
||||||
|
|
||||||
/// Set up the necessary preamble definitions in `func` to access the global variable
|
/// Set up the necessary preamble definitions in `func` to access the global variable
|
||||||
/// identified by `index`.
|
/// identified by `index`.
|
||||||
///
|
///
|
||||||
@@ -104,6 +109,12 @@ pub trait FuncEnvironment {
|
|||||||
/// The index space covers both imported and locally declared memories.
|
/// The index space covers both imported and locally declared memories.
|
||||||
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap;
|
fn make_heap(&mut self, func: &mut ir::Function, index: MemoryIndex) -> ir::Heap;
|
||||||
|
|
||||||
|
/// Set up the necessary preamble definitions in `func` to access the table identified
|
||||||
|
/// by `index`.
|
||||||
|
///
|
||||||
|
/// The index space covers both imported and locally declared tables.
|
||||||
|
fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> ir::Table;
|
||||||
|
|
||||||
/// Set up a signature definition in the preamble of `func` that can be used for an indirect
|
/// Set up a signature definition in the preamble of `func` that can be used for an indirect
|
||||||
/// call with signature `index`.
|
/// call with signature `index`.
|
||||||
///
|
///
|
||||||
@@ -141,6 +152,7 @@ pub trait FuncEnvironment {
|
|||||||
&mut self,
|
&mut self,
|
||||||
pos: FuncCursor,
|
pos: FuncCursor,
|
||||||
table_index: TableIndex,
|
table_index: TableIndex,
|
||||||
|
table: ir::Table,
|
||||||
sig_index: SignatureIndex,
|
sig_index: SignatureIndex,
|
||||||
sig_ref: ir::SigRef,
|
sig_ref: ir::SigRef,
|
||||||
callee: ir::Value,
|
callee: ir::Value,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use cranelift_codegen::ir::{self, Ebb, Inst, Value};
|
|||||||
use environ::{FuncEnvironment, GlobalVariable};
|
use environ::{FuncEnvironment, GlobalVariable};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
use translation_utils::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex};
|
use translation_utils::{FunctionIndex, GlobalIndex, MemoryIndex, SignatureIndex, TableIndex};
|
||||||
|
|
||||||
/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
|
/// A control stack frame can be an `if`, a `block` or a `loop`, each one having the following
|
||||||
/// fields:
|
/// fields:
|
||||||
@@ -140,6 +140,9 @@ pub struct TranslationState {
|
|||||||
// Map of heaps that have been created by `FuncEnvironment::make_heap`.
|
// Map of heaps that have been created by `FuncEnvironment::make_heap`.
|
||||||
heaps: HashMap<MemoryIndex, ir::Heap>,
|
heaps: HashMap<MemoryIndex, ir::Heap>,
|
||||||
|
|
||||||
|
// Map of tables that have been created by `FuncEnvironment::make_table`.
|
||||||
|
tables: HashMap<TableIndex, ir::Table>,
|
||||||
|
|
||||||
// Map of indirect call signatures that have been created by
|
// Map of indirect call signatures that have been created by
|
||||||
// `FuncEnvironment::make_indirect_sig()`.
|
// `FuncEnvironment::make_indirect_sig()`.
|
||||||
// Stores both the signature reference and the number of WebAssembly arguments
|
// Stores both the signature reference and the number of WebAssembly arguments
|
||||||
@@ -159,6 +162,7 @@ impl TranslationState {
|
|||||||
reachable: true,
|
reachable: true,
|
||||||
globals: HashMap::new(),
|
globals: HashMap::new(),
|
||||||
heaps: HashMap::new(),
|
heaps: HashMap::new(),
|
||||||
|
tables: HashMap::new(),
|
||||||
signatures: HashMap::new(),
|
signatures: HashMap::new(),
|
||||||
functions: HashMap::new(),
|
functions: HashMap::new(),
|
||||||
}
|
}
|
||||||
@@ -303,6 +307,21 @@ impl TranslationState {
|
|||||||
.or_insert_with(|| environ.make_heap(func, index))
|
.or_insert_with(|| environ.make_heap(func, index))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the `Table` reference that should be used to access table `index`.
|
||||||
|
/// Create the reference if necessary.
|
||||||
|
pub fn get_table<FE: FuncEnvironment + ?Sized>(
|
||||||
|
&mut self,
|
||||||
|
func: &mut ir::Function,
|
||||||
|
index: u32,
|
||||||
|
environ: &mut FE,
|
||||||
|
) -> ir::Table {
|
||||||
|
let index = index as TableIndex;
|
||||||
|
*self
|
||||||
|
.tables
|
||||||
|
.entry(index)
|
||||||
|
.or_insert_with(|| environ.make_table(func, index))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the `SigRef` reference that should be used to make an indirect call with signature
|
/// Get the `SigRef` reference that should be used to make an indirect call with signature
|
||||||
/// `index`. Also return the number of WebAssembly arguments in the signature.
|
/// `index`. Also return the number of WebAssembly arguments in the signature.
|
||||||
///
|
///
|
||||||
|
|||||||
Reference in New Issue
Block a user