Inline jump tables in parsed br_table instructions (#5755)
As jump tables are used by at most one br_table instruction, inline their definition in those instructions instead of requiring them to be declared as function-level metadata.
This commit is contained in:
@@ -81,7 +81,7 @@ impl JumpTableData {
|
|||||||
|
|
||||||
impl Display for JumpTableData {
|
impl Display for JumpTableData {
|
||||||
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
||||||
write!(fmt, "jump_table [")?;
|
write!(fmt, "[")?;
|
||||||
match self.table.first() {
|
match self.table.first() {
|
||||||
None => (),
|
None => (),
|
||||||
Some(first) => write!(fmt, "{}", first)?,
|
Some(first) => write!(fmt, "{}", first)?,
|
||||||
@@ -107,7 +107,7 @@ mod tests {
|
|||||||
assert_eq!(jt.as_slice().get(0), None);
|
assert_eq!(jt.as_slice().get(0), None);
|
||||||
assert_eq!(jt.as_slice().get(10), None);
|
assert_eq!(jt.as_slice().get(10), None);
|
||||||
|
|
||||||
assert_eq!(jt.to_string(), "jump_table []");
|
assert_eq!(jt.to_string(), "[]");
|
||||||
|
|
||||||
let v = jt.as_slice();
|
let v = jt.as_slice();
|
||||||
assert_eq!(v, []);
|
assert_eq!(v, []);
|
||||||
@@ -124,7 +124,7 @@ mod tests {
|
|||||||
jt.push_entry(e2);
|
jt.push_entry(e2);
|
||||||
jt.push_entry(e1);
|
jt.push_entry(e1);
|
||||||
|
|
||||||
assert_eq!(jt.to_string(), "jump_table [block1, block2, block1]");
|
assert_eq!(jt.to_string(), "[block1, block2, block1]");
|
||||||
|
|
||||||
let v = jt.as_slice();
|
let v = jt.as_slice();
|
||||||
assert_eq!(v, [e1, e2, e1]);
|
assert_eq!(v, [e1, e2, e1]);
|
||||||
|
|||||||
@@ -446,15 +446,6 @@ impl<'a> Verifier<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_jump_tables(&self, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
|
||||||
for (jt, jt_data) in &self.func.stencil.dfg.jump_tables {
|
|
||||||
for &block in jt_data.iter() {
|
|
||||||
self.verify_block(jt, block, errors)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check that the given block can be encoded as a BB, by checking that only
|
/// Check that the given block can be encoded as a BB, by checking that only
|
||||||
/// branching instructions are ending the block.
|
/// branching instructions are ending the block.
|
||||||
fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
fn encodable_as_bb(&self, block: Block, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
|
||||||
@@ -861,6 +852,9 @@ impl<'a> Verifier<'a> {
|
|||||||
format!("invalid jump table reference {}", j),
|
format!("invalid jump table reference {}", j),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
for &block in self.func.stencil.dfg.jump_tables[j].as_slice() {
|
||||||
|
self.verify_block(inst, block, errors)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1792,7 +1786,6 @@ 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_tables(errors)?;
|
self.verify_tables(errors)?;
|
||||||
self.verify_jump_tables(errors)?;
|
|
||||||
self.typecheck_entry_block_params(errors)?;
|
self.typecheck_entry_block_params(errors)?;
|
||||||
self.check_entry_not_cold(errors)?;
|
self.check_entry_not_cold(errors)?;
|
||||||
self.typecheck_function_signature(errors)?;
|
self.typecheck_function_signature(errors)?;
|
||||||
|
|||||||
@@ -82,11 +82,6 @@ pub trait FuncWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (jt, jt_data) in &func.stencil.dfg.jump_tables {
|
|
||||||
any = true;
|
|
||||||
self.write_entity_definition(w, func, jt.into(), jt_data)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (&cref, cval) in func.dfg.constants.iter() {
|
for (&cref, cval) in func.dfg.constants.iter() {
|
||||||
any = true;
|
any = true;
|
||||||
self.write_entity_definition(w, func, cref.into(), cval)?;
|
self.write_entity_definition(w, func, cref.into(), cval)?;
|
||||||
@@ -377,6 +372,7 @@ fn write_instruction(
|
|||||||
/// Write the operands of `inst` to `w` with a prepended space.
|
/// Write the operands of `inst` to `w` with a prepended space.
|
||||||
pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
|
pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
|
||||||
let pool = &dfg.value_lists;
|
let pool = &dfg.value_lists;
|
||||||
|
let jump_tables = &dfg.jump_tables;
|
||||||
use crate::ir::instructions::InstructionData::*;
|
use crate::ir::instructions::InstructionData::*;
|
||||||
match dfg.insts[inst] {
|
match dfg.insts[inst] {
|
||||||
AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
|
AtomicRmw { op, args, .. } => write!(w, " {} {}, {}", op, args[0], args[1]),
|
||||||
@@ -430,7 +426,7 @@ pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt
|
|||||||
destination,
|
destination,
|
||||||
table,
|
table,
|
||||||
..
|
..
|
||||||
} => write!(w, " {}, {}, {}", arg, destination, table),
|
} => write!(w, " {}, {}, {}", arg, destination, jump_tables[table]),
|
||||||
Call {
|
Call {
|
||||||
func_ref, ref args, ..
|
func_ref, ref args, ..
|
||||||
} => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
|
} => write!(w, " {}({})", func_ref, DisplayValues(args.as_slice(pool))),
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ function u0:0(i64 vmctx, i64) fast {
|
|||||||
gv2 = load.i64 notrap aligned gv1
|
gv2 = load.i64 notrap aligned gv1
|
||||||
sig0 = (i64 vmctx, i64) fast
|
sig0 = (i64 vmctx, i64) fast
|
||||||
fn0 = colocated u0:2 sig0
|
fn0 = colocated u0:2 sig0
|
||||||
jt0 = jump_table [block1]
|
|
||||||
stack_limit = gv2
|
stack_limit = gv2
|
||||||
|
|
||||||
block0(v0: i64, v1: i64):
|
block0(v0: i64, v1: i64):
|
||||||
@@ -33,7 +32,7 @@ function u0:0(i64 vmctx, i64) fast {
|
|||||||
|
|
||||||
block4:
|
block4:
|
||||||
@002e call fn0(v0, v0)
|
@002e call fn0(v0, v0)
|
||||||
@0030 br_table v8, block1, jt0
|
@0030 br_table v8, block1, [block1]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
@0036 return
|
@0036 return
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ function u0:33() system_v {
|
|||||||
ss0 = explicit_slot 32
|
ss0 = explicit_slot 32
|
||||||
sig0 = (i64, i64, i64, i64, i64) -> i64, i64 system_v
|
sig0 = (i64, i64, i64, i64, i64) -> i64, i64 system_v
|
||||||
fn0 = colocated u0:0 sig0
|
fn0 = colocated u0:0 sig0
|
||||||
jt0 = jump_table [block36, block38]
|
|
||||||
block0:
|
block0:
|
||||||
v80 = iconst.i32 0
|
v80 = iconst.i32 0
|
||||||
v91 = iconst.i64 0
|
v91 = iconst.i64 0
|
||||||
@@ -28,7 +27,7 @@ block0:
|
|||||||
v237 = iconst.i64 0
|
v237 = iconst.i64 0
|
||||||
v238, v239 = call fn0(v236, v237, v91, v92, v235) ; v236 = 0, v237 = 0, v91 = 0, v92 = 0, v235 = 0
|
v238, v239 = call fn0(v236, v237, v91, v92, v235) ; v236 = 0, v237 = 0, v91 = 0, v92 = 0, v235 = 0
|
||||||
v97 = iadd v238, v96 ; v96 = 0
|
v97 = iadd v238, v96 ; v96 = 0
|
||||||
br_table v80, block37, jt0 ; v80 = 0
|
br_table v80, block37, [block36, block38] ; v80 = 0
|
||||||
block36:
|
block36:
|
||||||
trap user0
|
trap user0
|
||||||
block37:
|
block37:
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ set unwind_info=false
|
|||||||
target aarch64 use_bti
|
target aarch64 use_bti
|
||||||
|
|
||||||
function %f1(i32) -> i32 {
|
function %f1(i32) -> i32 {
|
||||||
jt0 = jump_table [block1, block2, block3]
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
br_table v0, block4, jt0
|
br_table v0, block4, [block1, block2, block3]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
v1 = iconst.i32 1
|
v1 = iconst.i32 1
|
||||||
@@ -62,12 +60,10 @@ block5(v5: i32):
|
|||||||
; ret
|
; ret
|
||||||
|
|
||||||
function %f2(i64) -> i64 {
|
function %f2(i64) -> i64 {
|
||||||
jt0 = jump_table [block2]
|
|
||||||
|
|
||||||
block0(v0: i64):
|
block0(v0: i64):
|
||||||
v1 = ireduce.i32 v0
|
v1 = ireduce.i32 v0
|
||||||
v2 = load.i64 notrap aligned table v0
|
v2 = load.i64 notrap aligned table v0
|
||||||
br_table v1, block1, jt0
|
br_table v1, block1, [block2]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
return v2
|
return v2
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ set unwind_info=false
|
|||||||
target aarch64
|
target aarch64
|
||||||
|
|
||||||
function %f(i32) -> i32 {
|
function %f(i32) -> i32 {
|
||||||
jt0 = jump_table [block1, block2, block3]
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
br_table v0, block4, jt0
|
br_table v0, block4, [block1, block2, block3]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
v1 = iconst.i32 1
|
v1 = iconst.i32 1
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ test compile precise-output
|
|||||||
target s390x
|
target s390x
|
||||||
|
|
||||||
function %f(i32) -> i32 {
|
function %f(i32) -> i32 {
|
||||||
jt0 = jump_table [block1, block2, block3]
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
br_table v0, block4, jt0
|
br_table v0, block4, [block1, block2, block3]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
v1 = iconst.i32 1
|
v1 = iconst.i32 1
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ function u0:31(i64, i32, i32, i8, i8) -> i32, i32 system_v {
|
|||||||
fn3 = colocated u0:11 sig3
|
fn3 = colocated u0:11 sig3
|
||||||
fn4 = u0:110 sig4
|
fn4 = u0:110 sig4
|
||||||
fn5 = u0:110 sig5
|
fn5 = u0:110 sig5
|
||||||
jt0 = jump_table [block2, block4, block5, block6, block7]
|
|
||||||
|
|
||||||
block0(v0: i64, v1: i32, v2: i32, v3: i8, v4: i8):
|
block0(v0: i64, v1: i32, v2: i32, v3: i8, v4: i8):
|
||||||
v34 -> v0
|
v34 -> v0
|
||||||
@@ -82,7 +81,7 @@ function u0:31(i64, i32, i32, i8, i8) -> i32, i32 system_v {
|
|||||||
@0005 jump block37
|
@0005 jump block37
|
||||||
|
|
||||||
block37:
|
block37:
|
||||||
@0005 br_table v10, block36, jt0
|
@0005 br_table v10, block36, [block2, block4, block5, block6, block7]
|
||||||
|
|
||||||
block2:
|
block2:
|
||||||
@0001 v11 = stack_load.i8 ss3+1
|
@0001 v11 = stack_load.i8 ss3+1
|
||||||
|
|||||||
@@ -181,10 +181,8 @@ block2:
|
|||||||
; ret
|
; ret
|
||||||
|
|
||||||
function %f5(i32) -> i8 {
|
function %f5(i32) -> i8 {
|
||||||
jt0 = jump_table [block1, block2]
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
br_table v0, block1, jt0
|
br_table v0, block1, [block1, block2]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
v1 = iconst.i8 1
|
v1 = iconst.i8 1
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
test compile precise-output
|
|
||||||
target x86_64
|
|
||||||
|
|
||||||
;; From: https://github.com/bytecodealliance/wasmtime/issues/2670
|
|
||||||
|
|
||||||
function %f() system_v {
|
|
||||||
jt0 = jump_table [block1]
|
|
||||||
|
|
||||||
block0:
|
|
||||||
return
|
|
||||||
|
|
||||||
block1:
|
|
||||||
trap unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
; pushq %rbp
|
|
||||||
; movq %rsp, %rbp
|
|
||||||
; block0:
|
|
||||||
; movq %rbp, %rsp
|
|
||||||
; popq %rbp
|
|
||||||
; ret
|
|
||||||
|
|
||||||
@@ -4,11 +4,10 @@ target aarch64
|
|||||||
target x86_64
|
target x86_64
|
||||||
|
|
||||||
function %br_table_opt() {
|
function %br_table_opt() {
|
||||||
jt0 = jump_table [block1, block2]
|
|
||||||
|
|
||||||
block0:
|
block0:
|
||||||
v0 = iconst.i32 1
|
v0 = iconst.i32 1
|
||||||
br_table v0, block2, jt0
|
br_table v0, block2, [block1, block2]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ test licm
|
|||||||
target aarch64
|
target aarch64
|
||||||
|
|
||||||
function %rewrite_jump_table() {
|
function %rewrite_jump_table() {
|
||||||
jt0 = jump_table [block1, block2]
|
|
||||||
|
|
||||||
block0:
|
block0:
|
||||||
v0 = iconst.i32 1
|
v0 = iconst.i32 1
|
||||||
br_table v0, block1, jt0
|
br_table v0, block1, [block1, block2]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
return
|
return
|
||||||
@@ -17,7 +16,6 @@ function %rewrite_jump_table() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
; sameln: function
|
; sameln: function
|
||||||
; nextln: jt0 = jump_table [block1, block3]
|
|
||||||
; check: block3:
|
; check: block3:
|
||||||
; nextln: v4 = iconst.i8 0
|
; nextln: v4 = iconst.i8 0
|
||||||
; nextln: jump block2
|
; nextln: jump block2
|
||||||
|
|||||||
@@ -81,11 +81,8 @@ block1(v92: i32, v93: f32):
|
|||||||
; nextln: }
|
; nextln: }
|
||||||
|
|
||||||
function %jumptable(i32) {
|
function %jumptable(i32) {
|
||||||
jt200 = jump_table []
|
|
||||||
jt2 = jump_table [block10, block40, block20, block30]
|
|
||||||
|
|
||||||
block10(v3: i32):
|
block10(v3: i32):
|
||||||
br_table v3, block50, jt2
|
br_table v3, block50, [block10, block40, block20, block30]
|
||||||
|
|
||||||
block20:
|
block20:
|
||||||
trap user2
|
trap user2
|
||||||
@@ -97,10 +94,8 @@ block50:
|
|||||||
trap user1
|
trap user1
|
||||||
}
|
}
|
||||||
; sameln: function %jumptable(i32) fast {
|
; sameln: function %jumptable(i32) fast {
|
||||||
; check: jt2 = jump_table [block10, block40, block20, block30]
|
|
||||||
; check: jt200 = jump_table []
|
|
||||||
; check: block10(v3: i32):
|
; check: block10(v3: i32):
|
||||||
; nextln: br_table v3, block50, jt2
|
; nextln: br_table v3, block50, [block10, block40, block20, block30]
|
||||||
; nextln:
|
; nextln:
|
||||||
; nextln: block20:
|
; nextln: block20:
|
||||||
; nextln: trap user2
|
; nextln: trap user2
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ target s390x
|
|||||||
target riscv64
|
target riscv64
|
||||||
|
|
||||||
function %br_table_i32(i32) -> i32 {
|
function %br_table_i32(i32) -> i32 {
|
||||||
jt0 = jump_table [block1, block2, block2, block3]
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
br_table v0, block4, jt0
|
br_table v0, block4, [block1, block2, block2, block3]
|
||||||
|
|
||||||
block1:
|
block1:
|
||||||
v1 = iconst.i32 1
|
v1 = iconst.i32 1
|
||||||
@@ -46,17 +44,15 @@ block5(v5: i32):
|
|||||||
; RISC-V had a bug where having a br_table on a cold block would cause a segfault
|
; RISC-V had a bug where having a br_table on a cold block would cause a segfault
|
||||||
; See #5496 for more details.
|
; See #5496 for more details.
|
||||||
function %br_table_cold_block(i32) -> i32 system_v {
|
function %br_table_cold_block(i32) -> i32 system_v {
|
||||||
jt0 = jump_table []
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
jump block1
|
jump block1
|
||||||
|
|
||||||
block1 cold:
|
block1 cold:
|
||||||
br_table v0, block2, jt0
|
br_table v0, block2, []
|
||||||
|
|
||||||
block2:
|
block2:
|
||||||
v1 = iconst.i32 0
|
v1 = iconst.i32 0
|
||||||
return v1
|
return v1
|
||||||
}
|
}
|
||||||
; run: %br_table_cold_block(0) == 0
|
; run: %br_table_cold_block(0) == 0
|
||||||
; run: %br_table_cold_block(1) == 0
|
; run: %br_table_cold_block(1) == 0
|
||||||
|
|||||||
@@ -1,19 +1,15 @@
|
|||||||
test verifier
|
test verifier
|
||||||
|
|
||||||
function %br_invalid_default(i32) {
|
function %br_invalid_default(i32) {
|
||||||
jt0 = jump_table [block1, block1]
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
br_table v0, block2, jt0 ; error: invalid block reference block2
|
br_table v0, block2, [block1, block1] ; error: invalid block reference block2
|
||||||
block1:
|
block1:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
function %br(i32) {
|
function %br(i32) {
|
||||||
jt0 = jump_table [block1, block2] ; error: invalid block reference block2
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
br_table v0, block1, jt0
|
br_table v0, block1, [block1, block3] ; error: invalid block reference block3
|
||||||
block1:
|
block1:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,10 +66,9 @@ function %fn_call_incorrect_arg_type(i64) {
|
|||||||
; TODO: Should we instead just verify that jump tables contain no blocks that take arguments? This
|
; TODO: Should we instead just verify that jump tables contain no blocks that take arguments? This
|
||||||
; error doesn't occur if no instruction uses the jump table.
|
; error doesn't occur if no instruction uses the jump table.
|
||||||
function %jump_table_args() {
|
function %jump_table_args() {
|
||||||
jt1 = jump_table [block1]
|
|
||||||
block0:
|
block0:
|
||||||
v0 = iconst.i32 0
|
v0 = iconst.i32 0
|
||||||
br_table v0, block2, jt1 ; error: takes no arguments, but had target block1 with 1 arguments
|
br_table v0, block2, [block1] ; error: takes no arguments, but had target block1 with 1 arguments
|
||||||
|
|
||||||
block1(v5: i32):
|
block1(v5: i32):
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -45,10 +45,8 @@ block0:
|
|||||||
}
|
}
|
||||||
|
|
||||||
function %br_table(i32) {
|
function %br_table(i32) {
|
||||||
jt0 = jump_table [block3, block1, block2]
|
|
||||||
|
|
||||||
block0(v0: i32):
|
block0(v0: i32):
|
||||||
br_table v0, block4, jt0
|
br_table v0, block4, [block3, block1, block2]
|
||||||
|
|
||||||
block4:
|
block4:
|
||||||
trap heap_oob
|
trap heap_oob
|
||||||
|
|||||||
@@ -985,10 +985,9 @@ mod tests {
|
|||||||
// Here is the pseudo-program we want to translate:
|
// Here is the pseudo-program we want to translate:
|
||||||
//
|
//
|
||||||
// function %f {
|
// function %f {
|
||||||
// jt = jump_table [block2, block1]
|
|
||||||
// block0:
|
// block0:
|
||||||
// x = 1;
|
// x = 1;
|
||||||
// br_table x, block2, jt
|
// br_table x, block2, [block2, block1]
|
||||||
// block1:
|
// block1:
|
||||||
// x = 2
|
// x = 2
|
||||||
// jump block2
|
// jump block2
|
||||||
|
|||||||
@@ -416,12 +416,10 @@ mod tests {
|
|||||||
let func = setup!(0, [0, 1,]);
|
let func = setup!(0, [0, 1,]);
|
||||||
assert_eq_output!(
|
assert_eq_output!(
|
||||||
func,
|
func,
|
||||||
" jt0 = jump_table [block1, block2]
|
"block0:
|
||||||
|
|
||||||
block0:
|
|
||||||
v0 = iconst.i8 0
|
v0 = iconst.i8 0
|
||||||
v1 = uextend.i32 v0 ; v0 = 0
|
v1 = uextend.i32 v0 ; v0 = 0
|
||||||
br_table v1, block0, jt0"
|
br_table v1, block0, [block1, block2]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,10 +443,7 @@ block3:
|
|||||||
let func = setup!(0, [0, 1, 5, 7, 10, 11, 12,]);
|
let func = setup!(0, [0, 1, 5, 7, 10, 11, 12,]);
|
||||||
assert_eq_output!(
|
assert_eq_output!(
|
||||||
func,
|
func,
|
||||||
" jt0 = jump_table [block5, block6, block7]
|
"block0:
|
||||||
jt1 = jump_table [block1, block2]
|
|
||||||
|
|
||||||
block0:
|
|
||||||
v0 = iconst.i8 0
|
v0 = iconst.i8 0
|
||||||
v1 = icmp_imm uge v0, 7 ; v0 = 0
|
v1 = icmp_imm uge v0, 7 ; v0 = 0
|
||||||
brif v1, block9, block8
|
brif v1, block9, block8
|
||||||
@@ -460,7 +455,7 @@ block9:
|
|||||||
block11:
|
block11:
|
||||||
v3 = iadd_imm.i8 v0, -10 ; v0 = 0
|
v3 = iadd_imm.i8 v0, -10 ; v0 = 0
|
||||||
v4 = uextend.i32 v3
|
v4 = uextend.i32 v3
|
||||||
br_table v4, block0, jt0
|
br_table v4, block0, [block5, block6, block7]
|
||||||
|
|
||||||
block10:
|
block10:
|
||||||
v5 = icmp_imm.i8 eq v0, 7 ; v0 = 0
|
v5 = icmp_imm.i8 eq v0, 7 ; v0 = 0
|
||||||
@@ -472,7 +467,7 @@ block8:
|
|||||||
|
|
||||||
block12:
|
block12:
|
||||||
v7 = uextend.i32 v0 ; v0 = 0
|
v7 = uextend.i32 v0 ; v0 = 0
|
||||||
br_table v7, block0, jt1"
|
br_table v7, block0, [block1, block2]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -513,16 +508,14 @@ block3:
|
|||||||
let func = setup!(0, [-1i8 as u8 as u128, 0, 1,]);
|
let func = setup!(0, [-1i8 as u8 as u128, 0, 1,]);
|
||||||
assert_eq_output!(
|
assert_eq_output!(
|
||||||
func,
|
func,
|
||||||
" jt0 = jump_table [block2, block3]
|
"block0:
|
||||||
|
|
||||||
block0:
|
|
||||||
v0 = iconst.i8 0
|
v0 = iconst.i8 0
|
||||||
v1 = icmp_imm eq v0, 255 ; v0 = 0
|
v1 = icmp_imm eq v0, 255 ; v0 = 0
|
||||||
brif v1, block1, block4
|
brif v1, block1, block4
|
||||||
|
|
||||||
block4:
|
block4:
|
||||||
v2 = uextend.i32 v0 ; v0 = 0
|
v2 = uextend.i32 v0 ; v0 = 0
|
||||||
br_table v2, block0, jt0"
|
br_table v2, block0, [block2, block3]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,16 +600,14 @@ block4:
|
|||||||
.to_string();
|
.to_string();
|
||||||
assert_eq_output!(
|
assert_eq_output!(
|
||||||
func,
|
func,
|
||||||
" jt0 = jump_table [block2, block1]
|
"block0:
|
||||||
|
|
||||||
block0:
|
|
||||||
v0 = iconst.i64 0
|
v0 = iconst.i64 0
|
||||||
v1 = icmp_imm ugt v0, 0xffff_ffff ; v0 = 0
|
v1 = icmp_imm ugt v0, 0xffff_ffff ; v0 = 0
|
||||||
brif v1, block3, block4
|
brif v1, block3, block4
|
||||||
|
|
||||||
block4:
|
block4:
|
||||||
v2 = ireduce.i32 v0 ; v0 = 0
|
v2 = ireduce.i32 v0 ; v0 = 0
|
||||||
br_table v2, block3, jt0"
|
br_table v2, block3, [block2, block1]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,9 +636,7 @@ block4:
|
|||||||
.to_string();
|
.to_string();
|
||||||
assert_eq_output!(
|
assert_eq_output!(
|
||||||
func,
|
func,
|
||||||
" jt0 = jump_table [block2, block1]
|
"block0:
|
||||||
|
|
||||||
block0:
|
|
||||||
v0 = iconst.i64 0
|
v0 = iconst.i64 0
|
||||||
v1 = uextend.i128 v0 ; v0 = 0
|
v1 = uextend.i128 v0 ; v0 = 0
|
||||||
v2 = icmp_imm ugt v1, 0xffff_ffff
|
v2 = icmp_imm ugt v1, 0xffff_ffff
|
||||||
@@ -655,7 +644,7 @@ block0:
|
|||||||
|
|
||||||
block4:
|
block4:
|
||||||
v3 = ireduce.i32 v1
|
v3 = ireduce.i32 v1
|
||||||
br_table v3, block3, jt0"
|
br_table v3, block3, [block2, block1]"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ pub enum Token<'a> {
|
|||||||
DynamicStackSlot(u32), // dss4
|
DynamicStackSlot(u32), // dss4
|
||||||
GlobalValue(u32), // gv3
|
GlobalValue(u32), // gv3
|
||||||
Table(u32), // table2
|
Table(u32), // table2
|
||||||
JumpTable(u32), // jt2
|
|
||||||
Constant(u32), // const2
|
Constant(u32), // const2
|
||||||
FuncRef(u32), // fn2
|
FuncRef(u32), // fn2
|
||||||
SigRef(u32), // sig2
|
SigRef(u32), // sig2
|
||||||
@@ -346,7 +345,6 @@ impl<'a> Lexer<'a> {
|
|||||||
"dt" => Some(Token::DynamicType(number)),
|
"dt" => Some(Token::DynamicType(number)),
|
||||||
"gv" => Some(Token::GlobalValue(number)),
|
"gv" => Some(Token::GlobalValue(number)),
|
||||||
"table" => Some(Token::Table(number)),
|
"table" => Some(Token::Table(number)),
|
||||||
"jt" => Some(Token::JumpTable(number)),
|
|
||||||
"const" => Some(Token::Constant(number)),
|
"const" => Some(Token::Constant(number)),
|
||||||
"fn" => Some(Token::FuncRef(number)),
|
"fn" => Some(Token::FuncRef(number)),
|
||||||
"sig" => Some(Token::SigRef(number)),
|
"sig" => Some(Token::SigRef(number)),
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ use cranelift_codegen::ir::{self, UserExternalNameRef};
|
|||||||
use cranelift_codegen::ir::{
|
use cranelift_codegen::ir::{
|
||||||
AbiParam, ArgumentExtension, ArgumentPurpose, Block, Constant, ConstantData, DynamicStackSlot,
|
AbiParam, ArgumentExtension, ArgumentPurpose, Block, Constant, ConstantData, DynamicStackSlot,
|
||||||
DynamicStackSlotData, DynamicTypeData, ExtFuncData, ExternalName, FuncRef, Function,
|
DynamicStackSlotData, DynamicTypeData, ExtFuncData, ExternalName, FuncRef, Function,
|
||||||
GlobalValue, GlobalValueData, JumpTable, JumpTableData, MemFlags, Opcode, SigRef, Signature,
|
GlobalValue, GlobalValueData, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot,
|
||||||
StackSlot, StackSlotData, StackSlotKind, Table, TableData, Type, UserFuncName, Value,
|
StackSlotData, StackSlotKind, Table, TableData, Type, UserFuncName, Value,
|
||||||
};
|
};
|
||||||
use cranelift_codegen::isa::{self, CallConv};
|
use cranelift_codegen::isa::{self, CallConv};
|
||||||
use cranelift_codegen::packed_option::ReservedValue;
|
use cranelift_codegen::packed_option::ReservedValue;
|
||||||
@@ -390,25 +390,6 @@ impl Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate a new jump table.
|
|
||||||
fn add_jt(&mut self, jt: JumpTable, data: JumpTableData, loc: Location) -> ParseResult<()> {
|
|
||||||
self.map.def_jt(jt, loc)?;
|
|
||||||
while self.function.stencil.dfg.jump_tables.next_key().index() <= jt.index() {
|
|
||||||
self.function.create_jump_table(JumpTableData::new());
|
|
||||||
}
|
|
||||||
self.function.stencil.dfg.jump_tables[jt] = data;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve a reference to a jump table.
|
|
||||||
fn check_jt(&self, jt: JumpTable, loc: Location) -> ParseResult<()> {
|
|
||||||
if !self.map.contains_jt(jt) {
|
|
||||||
err!(loc, "undefined jump table {}", jt)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate a new constant.
|
// Allocate a new constant.
|
||||||
fn add_constant(
|
fn add_constant(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -670,17 +651,6 @@ impl<'a> Parser<'a> {
|
|||||||
err!(self.loc, err_msg)
|
err!(self.loc, err_msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match and consume a jump table reference.
|
|
||||||
fn match_jt(&mut self) -> ParseResult<JumpTable> {
|
|
||||||
if let Some(Token::JumpTable(jt)) = self.token() {
|
|
||||||
self.consume();
|
|
||||||
if let Some(jt) = JumpTable::with_number(jt) {
|
|
||||||
return Ok(jt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err!(self.loc, "expected jump table number: jt«n»")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match and consume a constant reference.
|
// Match and consume a constant reference.
|
||||||
fn match_constant(&mut self) -> ParseResult<Constant> {
|
fn match_constant(&mut self) -> ParseResult<Constant> {
|
||||||
if let Some(Token::Constant(c)) = self.token() {
|
if let Some(Token::Constant(c)) = self.token() {
|
||||||
@@ -1484,11 +1454,6 @@ impl<'a> Parser<'a> {
|
|||||||
self.parse_function_decl(ctx)
|
self.parse_function_decl(ctx)
|
||||||
.and_then(|(fn_, dat)| ctx.add_fn(fn_, dat, self.loc))
|
.and_then(|(fn_, dat)| ctx.add_fn(fn_, dat, self.loc))
|
||||||
}
|
}
|
||||||
Some(Token::JumpTable(..)) => {
|
|
||||||
self.start_gathering_comments();
|
|
||||||
self.parse_jump_table_decl()
|
|
||||||
.and_then(|(jt, dat)| ctx.add_jt(jt, dat, self.loc))
|
|
||||||
}
|
|
||||||
Some(Token::Constant(..)) => {
|
Some(Token::Constant(..)) => {
|
||||||
self.start_gathering_comments();
|
self.start_gathering_comments();
|
||||||
self.parse_constant_decl()
|
self.parse_constant_decl()
|
||||||
@@ -1798,18 +1763,15 @@ impl<'a> Parser<'a> {
|
|||||||
Ok((fn_, data))
|
Ok((fn_, data))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a jump table decl.
|
// Parse a jump table literal.
|
||||||
//
|
//
|
||||||
// jump-table-decl ::= * JumpTable(jt) "=" "jump_table" "[" jt-entry {"," jt-entry} "]"
|
// jump-table-lit ::= "[" block {"," block } "]"
|
||||||
fn parse_jump_table_decl(&mut self) -> ParseResult<(JumpTable, JumpTableData)> {
|
// | "[]"
|
||||||
let jt = self.match_jt()?;
|
fn parse_jump_table(&mut self) -> ParseResult<JumpTableData> {
|
||||||
self.match_token(Token::Equal, "expected '=' in jump_table decl")?;
|
|
||||||
self.match_identifier("jump_table", "expected 'jump_table'")?;
|
|
||||||
self.match_token(Token::LBracket, "expected '[' before jump table contents")?;
|
self.match_token(Token::LBracket, "expected '[' before jump table contents")?;
|
||||||
|
|
||||||
let mut data = JumpTableData::new();
|
let mut data = JumpTableData::new();
|
||||||
|
|
||||||
// jump-table-decl ::= JumpTable(jt) "=" "jump_table" "[" * Block(dest) {"," Block(dest)} "]"
|
|
||||||
match self.token() {
|
match self.token() {
|
||||||
Some(Token::Block(dest)) => {
|
Some(Token::Block(dest)) => {
|
||||||
self.consume();
|
self.consume();
|
||||||
@@ -1837,11 +1799,7 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
self.consume();
|
self.consume();
|
||||||
|
|
||||||
// Collect any trailing comments.
|
Ok(data)
|
||||||
self.token();
|
|
||||||
self.claim_gathered_comments(jt);
|
|
||||||
|
|
||||||
Ok((jt, data))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a constant decl.
|
// Parse a constant decl.
|
||||||
@@ -2613,8 +2571,8 @@ impl<'a> Parser<'a> {
|
|||||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
let block_num = self.match_block("expected branch destination block")?;
|
let block_num = self.match_block("expected branch destination block")?;
|
||||||
self.match_token(Token::Comma, "expected ',' between operands")?;
|
self.match_token(Token::Comma, "expected ',' between operands")?;
|
||||||
let table = self.match_jt()?;
|
let table_data = self.parse_jump_table()?;
|
||||||
ctx.check_jt(table, self.loc)?;
|
let table = ctx.function.dfg.jump_tables.push(table_data);
|
||||||
InstructionData::BranchTable {
|
InstructionData::BranchTable {
|
||||||
opcode,
|
opcode,
|
||||||
arg,
|
arg,
|
||||||
@@ -3079,25 +3037,6 @@ mod tests {
|
|||||||
assert!(!is_warning);
|
assert!(!is_warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn duplicate_jt() {
|
|
||||||
let ParseError {
|
|
||||||
location,
|
|
||||||
message,
|
|
||||||
is_warning,
|
|
||||||
} = Parser::new(
|
|
||||||
"function %blocks() system_v {
|
|
||||||
jt0 = jump_table []
|
|
||||||
jt0 = jump_table []",
|
|
||||||
)
|
|
||||||
.parse_function()
|
|
||||||
.unwrap_err();
|
|
||||||
|
|
||||||
assert_eq!(location.line_number, 3);
|
|
||||||
assert_eq!(message, "duplicate entity: jt0");
|
|
||||||
assert!(!is_warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn duplicate_ss() {
|
fn duplicate_ss() {
|
||||||
let ParseError {
|
let ParseError {
|
||||||
@@ -3182,8 +3121,6 @@ mod tests {
|
|||||||
function %comment() system_v { ; decl
|
function %comment() system_v { ; decl
|
||||||
ss10 = explicit_slot 13 ; stackslot.
|
ss10 = explicit_slot 13 ; stackslot.
|
||||||
; Still stackslot.
|
; Still stackslot.
|
||||||
jt10 = jump_table [block0]
|
|
||||||
; Jumptable
|
|
||||||
block0: ; Basic block
|
block0: ; Basic block
|
||||||
trap user42; Instruction
|
trap user42; Instruction
|
||||||
} ; Trailing.
|
} ; Trailing.
|
||||||
@@ -3192,7 +3129,7 @@ mod tests {
|
|||||||
.parse_function()
|
.parse_function()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(func.name.to_string(), "%comment");
|
assert_eq!(func.name.to_string(), "%comment");
|
||||||
assert_eq!(comments.len(), 8); // no 'before' comment.
|
assert_eq!(comments.len(), 7); // no 'before' comment.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
comments[0],
|
comments[0],
|
||||||
Comment {
|
Comment {
|
||||||
@@ -3203,16 +3140,14 @@ mod tests {
|
|||||||
assert_eq!(comments[1].entity.to_string(), "ss10");
|
assert_eq!(comments[1].entity.to_string(), "ss10");
|
||||||
assert_eq!(comments[2].entity.to_string(), "ss10");
|
assert_eq!(comments[2].entity.to_string(), "ss10");
|
||||||
assert_eq!(comments[2].text, "; Still stackslot.");
|
assert_eq!(comments[2].text, "; Still stackslot.");
|
||||||
assert_eq!(comments[3].entity.to_string(), "jt10");
|
assert_eq!(comments[3].entity.to_string(), "block0");
|
||||||
assert_eq!(comments[3].text, "; Jumptable");
|
assert_eq!(comments[3].text, "; Basic block");
|
||||||
assert_eq!(comments[4].entity.to_string(), "block0");
|
|
||||||
assert_eq!(comments[4].text, "; Basic block");
|
|
||||||
|
|
||||||
assert_eq!(comments[5].entity.to_string(), "inst0");
|
assert_eq!(comments[4].entity.to_string(), "inst0");
|
||||||
assert_eq!(comments[5].text, "; Instruction");
|
assert_eq!(comments[4].text, "; Instruction");
|
||||||
|
|
||||||
|
assert_eq!(comments[5].entity, AnyEntity::Function);
|
||||||
assert_eq!(comments[6].entity, AnyEntity::Function);
|
assert_eq!(comments[6].entity, AnyEntity::Function);
|
||||||
assert_eq!(comments[7].entity, AnyEntity::Function);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -227,7 +227,6 @@ mod tests {
|
|||||||
let tf = parse_test(
|
let tf = parse_test(
|
||||||
"function %detail() {
|
"function %detail() {
|
||||||
ss10 = explicit_slot 13
|
ss10 = explicit_slot 13
|
||||||
jt10 = jump_table [block0]
|
|
||||||
block0(v4: i32, v7: i32):
|
block0(v4: i32, v7: i32):
|
||||||
v10 = iadd v4, v7
|
v10 = iadd v4, v7
|
||||||
}",
|
}",
|
||||||
@@ -239,7 +238,6 @@ mod tests {
|
|||||||
assert_eq!(map.lookup_str("v0"), None);
|
assert_eq!(map.lookup_str("v0"), None);
|
||||||
assert_eq!(map.lookup_str("ss1"), None);
|
assert_eq!(map.lookup_str("ss1"), None);
|
||||||
assert_eq!(map.lookup_str("ss10").unwrap().to_string(), "ss10");
|
assert_eq!(map.lookup_str("ss10").unwrap().to_string(), "ss10");
|
||||||
assert_eq!(map.lookup_str("jt10").unwrap().to_string(), "jt10");
|
|
||||||
assert_eq!(map.lookup_str("block0").unwrap().to_string(), "block0");
|
assert_eq!(map.lookup_str("block0").unwrap().to_string(), "block0");
|
||||||
assert_eq!(map.lookup_str("v4").unwrap().to_string(), "v4");
|
assert_eq!(map.lookup_str("v4").unwrap().to_string(), "v4");
|
||||||
assert_eq!(map.lookup_str("v7").unwrap().to_string(), "v7");
|
assert_eq!(map.lookup_str("v7").unwrap().to_string(), "v7");
|
||||||
|
|||||||
Reference in New Issue
Block a user