Add an index_type field to Table.
This parallels the `index_type` field in `Heap`.
This commit is contained in:
@@ -8,7 +8,7 @@ target x86_64
|
|||||||
function %test0(i64 vmctx, i64) -> i64 {
|
function %test0(i64 vmctx, i64) -> i64 {
|
||||||
gv0 = vmctx+12
|
gv0 = vmctx+12
|
||||||
gv1 = vmctx+14
|
gv1 = vmctx+14
|
||||||
table0 = dynamic gv0, min 20, bound gv1, element_size 4
|
table0 = dynamic gv0, min 20, bound gv1, element_size 4, index_type i64
|
||||||
|
|
||||||
ebb0(v0: i64, v1: i64):
|
ebb0(v0: i64, v1: i64):
|
||||||
v2 = table_addr.i64 table0, v1, +3
|
v2 = table_addr.i64 table0, v1, +3
|
||||||
|
|||||||
68
cranelift/filetests/isa/x86/legalize-tables.clif
Normal file
68
cranelift/filetests/isa/x86/legalize-tables.clif
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
test legalizer
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
; Test legalization for various forms of table addresses.
|
||||||
|
|
||||||
|
function %table_addrs(i32, i64, i64 vmctx) {
|
||||||
|
gv0 = vmctx+72
|
||||||
|
gv1 = vmctx+80
|
||||||
|
gv2 = vmctx+88
|
||||||
|
gv3 = deref(gv2): i32
|
||||||
|
|
||||||
|
table0 = dynamic gv0, min 0x1_0000, bound gv3, element_size 1, index_type i32
|
||||||
|
table1 = dynamic gv0, bound gv3, element_size 16, index_type i32
|
||||||
|
table2 = dynamic gv0, min 0x1_0000, bound gv1, element_size 1, index_type i64
|
||||||
|
table3 = dynamic gv0, bound gv1, element_size 16, index_type i64
|
||||||
|
|
||||||
|
; check: table0 = dynamic gv0, min 0x0001_0000, bound gv3, element_size 1, index_type i32
|
||||||
|
; check: table1 = dynamic gv0, min 0, bound gv3, element_size 16, index_type i32
|
||||||
|
; check: table2 = dynamic gv0, min 0x0001_0000, bound gv1, element_size 1, index_type i64
|
||||||
|
; check: table3 = dynamic gv0, min 0, bound gv1, element_size 16, index_type i64
|
||||||
|
|
||||||
|
ebb0(v0: i32, v1: i64, v3: i64):
|
||||||
|
v4 = table_addr.i64 table0, v0, +0
|
||||||
|
; check: v12 = iadd_imm v3, 88
|
||||||
|
; check: v13 = load.i32 notrap aligned v12
|
||||||
|
; check: v8 = iadd_imm v13, 0
|
||||||
|
; check: v9 = icmp uge v0, v8
|
||||||
|
; check: brz v9, ebb1
|
||||||
|
; check: trap table_oob
|
||||||
|
; check: ebb1:
|
||||||
|
; check: v10 = uextend.i64 v0
|
||||||
|
; check: v11 = iadd_imm.i64 v3, 72
|
||||||
|
; check: v4 = iadd v11, v10
|
||||||
|
|
||||||
|
v5 = table_addr.i64 table1, v0, +0
|
||||||
|
; check: v19 = iadd_imm.i64 v3, 88
|
||||||
|
; check: v20 = load.i32 notrap aligned v19
|
||||||
|
; check: v14 = iadd_imm v20, 0
|
||||||
|
; check: v15 = icmp.i32 uge v0, v14
|
||||||
|
; check: brz v15, ebb2
|
||||||
|
; check: trap table_oob
|
||||||
|
; check: ebb2:
|
||||||
|
; check: v16 = uextend.i64 v0
|
||||||
|
; check: v17 = iadd_imm.i64 v3, 72
|
||||||
|
; check: v18 = ishl_imm v16, 4
|
||||||
|
; check: v5 = iadd v17, v18
|
||||||
|
|
||||||
|
v6 = table_addr.i64 table2, v1, +0
|
||||||
|
; check: v21 = iadd_imm.i64 v3, 80
|
||||||
|
; check: v22 = icmp.i64 uge v1, v21
|
||||||
|
; check: brz v22, ebb3
|
||||||
|
; check: trap table_oob
|
||||||
|
; check: ebb3:
|
||||||
|
; check: v23 = iadd_imm.i64 v3, 72
|
||||||
|
; check: v6 = iadd v23, v1
|
||||||
|
|
||||||
|
v7 = table_addr.i64 table3, v1, +0
|
||||||
|
; check: v24 = iadd_imm.i64 v3, 80
|
||||||
|
; check: v25 = icmp.i64 uge v1, v24
|
||||||
|
; check: brz v25, ebb4
|
||||||
|
; check: trap table_oob
|
||||||
|
; check: ebb4:
|
||||||
|
; check: v26 = iadd_imm.i64 v3, 72
|
||||||
|
; check: v27 = ishl_imm.i64 v1, 4
|
||||||
|
; check: v7 = iadd v26, v27
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
46
cranelift/filetests/verifier/table.clif
Normal file
46
cranelift/filetests/verifier/table.clif
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
test verifier
|
||||||
|
target x86_64
|
||||||
|
|
||||||
|
function %table_base_type(i64 vmctx) {
|
||||||
|
gv0 = vmctx+0
|
||||||
|
gv1 = deref(gv0): i32
|
||||||
|
table0 = dynamic gv1, element_size 1, bound gv1, index_type i32 ; error: table base has type i32, which is not the pointer type i64
|
||||||
|
|
||||||
|
ebb0(v0: i64):
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function %invalid_base(i64 vmctx) {
|
||||||
|
gv0 = vmctx+0
|
||||||
|
table0 = dynamic gv1, bound gv0, element_size 1, index_type i64 ; error: invalid base global value gv1
|
||||||
|
|
||||||
|
ebb0(v0: i64):
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function %invalid_bound(i64 vmctx) {
|
||||||
|
gv0 = vmctx+0
|
||||||
|
table0 = dynamic gv0, bound gv1, element_size 1, index_type i64 ; error: invalid bound global value gv1
|
||||||
|
|
||||||
|
ebb0(v0: i64):
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function %table_bound_type(i64 vmctx) {
|
||||||
|
gv0 = vmctx+0
|
||||||
|
gv1 = deref(gv0): i16
|
||||||
|
table0 = dynamic gv0, bound gv1, element_size 1, index_type i32 ; error: table index type i32 differs from the type of its bound, i16
|
||||||
|
|
||||||
|
ebb0(v0: i64):
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function %table_addr_index_type(i64 vmctx, i64) {
|
||||||
|
gv0 = vmctx+0
|
||||||
|
gv1 = deref(gv0): i32
|
||||||
|
table0 = dynamic gv0, element_size 1, bound gv1, index_type i32
|
||||||
|
|
||||||
|
ebb0(v0: i64, v1: i64):
|
||||||
|
v2 = table_addr.i64 table0, v1, +0; error: index type i64 differs from table index type i32
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Tables.
|
//! Tables.
|
||||||
|
|
||||||
use ir::immediates::Imm64;
|
use ir::immediates::Imm64;
|
||||||
use ir::GlobalValue;
|
use ir::{GlobalValue, Type};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
/// Information about a table declaration.
|
/// Information about a table declaration.
|
||||||
@@ -19,14 +19,18 @@ pub struct TableData {
|
|||||||
|
|
||||||
/// The size of a table element, in bytes.
|
/// The size of a table element, in bytes.
|
||||||
pub element_size: Imm64,
|
pub element_size: Imm64,
|
||||||
|
|
||||||
|
/// The index type for the table.
|
||||||
|
pub index_type: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TableData {
|
impl fmt::Display for TableData {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.write_str("dynamic")?;
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"{}, min {}, bound {}, element_size {}",
|
" {}, min {}, bound {}, element_size {}, index_type {}",
|
||||||
self.base_gv, self.min_size, self.bound_gv, self.element_size
|
self.base_gv, self.min_size, self.bound_gv, self.element_size, self.index_type
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ fn dynamic_addr(
|
|||||||
pos.use_srcloc(inst);
|
pos.use_srcloc(inst);
|
||||||
|
|
||||||
// Start with the bounds check. Trap if `index + 1 > bound`.
|
// Start with the bounds check. Trap if `index + 1 > bound`.
|
||||||
let bound = pos.ins().global_value(addr_ty, bound_gv);
|
let bound = pos.ins().global_value(index_ty, bound_gv);
|
||||||
|
|
||||||
// `index > bound - 1` is the same as `index >= bound`.
|
// `index > bound - 1` is the same as `index >= bound`.
|
||||||
let oob = pos
|
let oob = pos
|
||||||
|
|||||||
@@ -433,6 +433,48 @@ impl<'a> Verifier<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn verify_tables(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
||||||
|
if let Some(isa) = self.isa {
|
||||||
|
for (table, table_data) in &self.func.tables {
|
||||||
|
let base = table_data.base_gv;
|
||||||
|
if !self.func.global_values.is_valid(base) {
|
||||||
|
return nonfatal!(errors, table, "invalid base global value {}", base);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pointer_type = isa.pointer_type();
|
||||||
|
let base_type = self.func.global_values[base].global_type(isa);
|
||||||
|
if base_type != pointer_type {
|
||||||
|
report!(
|
||||||
|
errors,
|
||||||
|
table,
|
||||||
|
"table base has type {}, which is not the pointer type {}",
|
||||||
|
base_type,
|
||||||
|
pointer_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bound_gv = table_data.bound_gv;
|
||||||
|
if !self.func.global_values.is_valid(bound_gv) {
|
||||||
|
return nonfatal!(errors, table, "invalid bound global value {}", bound_gv);
|
||||||
|
}
|
||||||
|
|
||||||
|
let index_type = table_data.index_type;
|
||||||
|
let bound_type = self.func.global_values[bound_gv].global_type(isa);
|
||||||
|
if index_type != bound_type {
|
||||||
|
report!(
|
||||||
|
errors,
|
||||||
|
table,
|
||||||
|
"table index type {} differs from the type of its bound, {}",
|
||||||
|
index_type,
|
||||||
|
bound_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn ebb_integrity(
|
fn ebb_integrity(
|
||||||
&self,
|
&self,
|
||||||
ebb: Ebb,
|
ebb: Ebb,
|
||||||
@@ -1385,6 +1427,19 @@ impl<'a> Verifier<'a> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ir::InstructionData::TableAddr { table, arg, .. } => {
|
||||||
|
let index_type = self.func.dfg.value_type(arg);
|
||||||
|
let table_index_type = self.func.tables[table].index_type;
|
||||||
|
if index_type != table_index_type {
|
||||||
|
return nonfatal!(
|
||||||
|
errors,
|
||||||
|
inst,
|
||||||
|
"index type {} differs from table index type {}",
|
||||||
|
index_type,
|
||||||
|
table_index_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
|
ir::InstructionData::UnaryGlobalValue { global_value, .. } => {
|
||||||
if let Some(isa) = self.isa {
|
if let Some(isa) = self.isa {
|
||||||
let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
|
let inst_type = self.func.dfg.value_type(self.func.dfg.first_result(inst));
|
||||||
@@ -1611,6 +1666,7 @@ impl<'a> Verifier<'a> {
|
|||||||
pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
pub fn run(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
||||||
self.verify_global_values(errors)?;
|
self.verify_global_values(errors)?;
|
||||||
self.verify_heaps(errors)?;
|
self.verify_heaps(errors)?;
|
||||||
|
self.verify_tables(errors)?;
|
||||||
self.typecheck_entry_block_params(errors)?;
|
self.typecheck_entry_block_params(errors)?;
|
||||||
|
|
||||||
for ebb in self.func.layout.ebbs() {
|
for ebb in self.func.layout.ebbs() {
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ pub trait FuncWriter {
|
|||||||
self.write_entity_definition(w, func, heap.into(), heap_data)?;
|
self.write_entity_definition(w, func, heap.into(), heap_data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (table, table_data) in &func.tables {
|
||||||
|
any = true;
|
||||||
|
self.write_entity_definition(w, func, table.into(), table_data)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Write out all signatures before functions since function declarations can refer to
|
// Write out all signatures before functions since function declarations can refer to
|
||||||
// signatures.
|
// signatures.
|
||||||
for (sig, sig_data) in &func.dfg.signatures {
|
for (sig, sig_data) in &func.dfg.signatures {
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ impl<'a> Context<'a> {
|
|||||||
min_size: Imm64::new(0),
|
min_size: Imm64::new(0),
|
||||||
bound_gv: GlobalValue::reserved_value(),
|
bound_gv: GlobalValue::reserved_value(),
|
||||||
element_size: Imm64::new(0),
|
element_size: Imm64::new(0),
|
||||||
|
index_type: VOID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.function.tables[table] = data;
|
self.function.tables[table] = data;
|
||||||
@@ -1161,6 +1162,7 @@ impl<'a> Parser<'a> {
|
|||||||
// heap-attr ::= "min" Imm64(bytes)
|
// heap-attr ::= "min" Imm64(bytes)
|
||||||
// | "bound" Imm64(bytes)
|
// | "bound" Imm64(bytes)
|
||||||
// | "guard" Imm64(bytes)
|
// | "guard" Imm64(bytes)
|
||||||
|
// | "index_type" type
|
||||||
//
|
//
|
||||||
fn parse_heap_decl(&mut self) -> ParseResult<(Heap, HeapData)> {
|
fn parse_heap_decl(&mut self) -> ParseResult<(Heap, HeapData)> {
|
||||||
let heap = self.match_heap("expected heap number: heap«n»")?;
|
let heap = self.match_heap("expected heap number: heap«n»")?;
|
||||||
@@ -1230,6 +1232,7 @@ impl<'a> Parser<'a> {
|
|||||||
// table-attr ::= "min" Imm64(bytes)
|
// table-attr ::= "min" Imm64(bytes)
|
||||||
// | "bound" Imm64(bytes)
|
// | "bound" Imm64(bytes)
|
||||||
// | "element_size" Imm64(bytes)
|
// | "element_size" Imm64(bytes)
|
||||||
|
// | "index_type" type
|
||||||
//
|
//
|
||||||
fn parse_table_decl(&mut self) -> ParseResult<(Table, TableData)> {
|
fn parse_table_decl(&mut self) -> ParseResult<(Table, TableData)> {
|
||||||
let table = self.match_table("expected table number: table«n»")?;
|
let table = self.match_table("expected table number: table«n»")?;
|
||||||
@@ -1253,6 +1256,7 @@ impl<'a> Parser<'a> {
|
|||||||
min_size: 0.into(),
|
min_size: 0.into(),
|
||||||
bound_gv: GlobalValue::reserved_value(),
|
bound_gv: GlobalValue::reserved_value(),
|
||||||
element_size: 0.into(),
|
element_size: 0.into(),
|
||||||
|
index_type: ir::types::I32,
|
||||||
};
|
};
|
||||||
|
|
||||||
// table-desc ::= * { "," table-attr }
|
// table-desc ::= * { "," table-attr }
|
||||||
@@ -1270,6 +1274,9 @@ impl<'a> Parser<'a> {
|
|||||||
"element_size" => {
|
"element_size" => {
|
||||||
data.element_size = self.match_imm64("expected integer element size")?;
|
data.element_size = self.match_imm64("expected integer element size")?;
|
||||||
}
|
}
|
||||||
|
"index_type" => {
|
||||||
|
data.index_type = self.match_type("expected index type")?;
|
||||||
|
}
|
||||||
t => return err!(self.loc, "unknown table attribute '{}'", t),
|
t => return err!(self.loc, "unknown table attribute '{}'", t),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ impl<'dummy_environment> FuncEnvironment for DummyFuncEnvironment<'dummy_environ
|
|||||||
min_size: Imm64::new(0),
|
min_size: Imm64::new(0),
|
||||||
bound_gv,
|
bound_gv,
|
||||||
element_size: Imm64::new(i64::from(self.pointer_bytes()) * 2),
|
element_size: Imm64::new(i64::from(self.pointer_bytes()) * 2),
|
||||||
|
index_type: I32,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user