Make the table_ops test case generator use globals as well
This will make it generate `table.set`s that come from `global.get`s and `global.get`s that come from `table.set`s. Ultimately, it should give us much more fuzzing coverage of `externref` globals, their barriers, and passing `externref`s into and out of Wasm to get or set globals.
This commit is contained in:
@@ -3,8 +3,8 @@
|
|||||||
use arbitrary::Arbitrary;
|
use arbitrary::Arbitrary;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use wasm_encoder::{
|
use wasm_encoder::{
|
||||||
CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, ImportSection,
|
CodeSection, EntityType, Export, ExportSection, Function, FunctionSection, GlobalSection,
|
||||||
Instruction, Module, TableSection, TableType, TypeSection, ValType,
|
ImportSection, Instruction, Module, TableSection, TableType, TypeSection, ValType,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A description of a Wasm module that makes a series of `externref` table
|
/// A description of a Wasm module that makes a series of `externref` table
|
||||||
@@ -12,11 +12,13 @@ use wasm_encoder::{
|
|||||||
#[derive(Arbitrary, Debug)]
|
#[derive(Arbitrary, Debug)]
|
||||||
pub struct TableOps {
|
pub struct TableOps {
|
||||||
num_params: u8,
|
num_params: u8,
|
||||||
|
num_globals: u8,
|
||||||
table_size: u32,
|
table_size: u32,
|
||||||
ops: Vec<TableOp>,
|
ops: Vec<TableOp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const NUM_PARAMS_RANGE: Range<u8> = 1..10;
|
const NUM_PARAMS_RANGE: Range<u8> = 1..10;
|
||||||
|
const NUM_GLOBALS_RANGE: Range<u8> = 1..10;
|
||||||
const TABLE_SIZE_RANGE: Range<u32> = 1..100;
|
const TABLE_SIZE_RANGE: Range<u32> = 1..100;
|
||||||
const MAX_OPS: usize = 100;
|
const MAX_OPS: usize = 100;
|
||||||
|
|
||||||
@@ -28,6 +30,13 @@ impl TableOps {
|
|||||||
num_params
|
num_params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the number of globals this module has.
|
||||||
|
pub fn num_globals(&self) -> u8 {
|
||||||
|
let num_globals = std::cmp::max(self.num_globals, NUM_GLOBALS_RANGE.start);
|
||||||
|
let num_globals = std::cmp::min(num_globals, NUM_GLOBALS_RANGE.end);
|
||||||
|
num_globals
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the size of the table that this module uses.
|
/// Get the size of the table that this module uses.
|
||||||
pub fn table_size(&self) -> u32 {
|
pub fn table_size(&self) -> u32 {
|
||||||
let table_size = std::cmp::max(self.table_size, TABLE_SIZE_RANGE.start);
|
let table_size = std::cmp::max(self.table_size, TABLE_SIZE_RANGE.start);
|
||||||
@@ -98,6 +107,18 @@ impl TableOps {
|
|||||||
maximum: None,
|
maximum: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Define our globals.
|
||||||
|
let mut globals = GlobalSection::new();
|
||||||
|
for _ in 0..self.num_globals() {
|
||||||
|
globals.global(
|
||||||
|
wasm_encoder::GlobalType {
|
||||||
|
val_type: wasm_encoder::ValType::ExternRef,
|
||||||
|
mutable: true,
|
||||||
|
},
|
||||||
|
Instruction::RefNull(wasm_encoder::ValType::ExternRef),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Define the "run" function export.
|
// Define the "run" function export.
|
||||||
let mut functions = FunctionSection::new();
|
let mut functions = FunctionSection::new();
|
||||||
functions.function(1);
|
functions.function(1);
|
||||||
@@ -111,7 +132,12 @@ impl TableOps {
|
|||||||
|
|
||||||
func.instruction(Instruction::Loop(wasm_encoder::BlockType::Empty));
|
func.instruction(Instruction::Loop(wasm_encoder::BlockType::Empty));
|
||||||
for op in self.ops.iter().take(MAX_OPS) {
|
for op in self.ops.iter().take(MAX_OPS) {
|
||||||
op.insert(&mut func, self.num_params() as u32, self.table_size());
|
op.insert(
|
||||||
|
&mut func,
|
||||||
|
self.num_params() as u32,
|
||||||
|
self.table_size(),
|
||||||
|
self.num_globals() as u32,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
func.instruction(Instruction::Br(0));
|
func.instruction(Instruction::Br(0));
|
||||||
func.instruction(Instruction::End);
|
func.instruction(Instruction::End);
|
||||||
@@ -125,6 +151,7 @@ impl TableOps {
|
|||||||
.section(&imports)
|
.section(&imports)
|
||||||
.section(&functions)
|
.section(&functions)
|
||||||
.section(&tables)
|
.section(&tables)
|
||||||
|
.section(&globals)
|
||||||
.section(&exports)
|
.section(&exports)
|
||||||
.section(&code);
|
.section(&code);
|
||||||
|
|
||||||
@@ -132,32 +159,56 @@ impl TableOps {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Arbitrary, Debug)]
|
#[derive(Arbitrary, Copy, Clone, Debug)]
|
||||||
pub(crate) enum TableOp {
|
pub(crate) enum TableOp {
|
||||||
// `call $gc; drop; drop; drop;`
|
// `call $gc; drop; drop; drop;`
|
||||||
Gc,
|
Gc,
|
||||||
|
|
||||||
// `(drop (table.get x))`
|
// `(drop (table.get x))`
|
||||||
Get(i32),
|
Get(i32),
|
||||||
|
|
||||||
|
// `(drop (global.get i))`
|
||||||
|
GetGlobal(u32),
|
||||||
|
|
||||||
// `(table.set x (local.get y))`
|
// `(table.set x (local.get y))`
|
||||||
SetFromParam(i32, u32),
|
SetFromParam(i32, u32),
|
||||||
|
|
||||||
// `(table.set x (table.get y))`
|
// `(table.set x (table.get y))`
|
||||||
SetFromGet(i32, i32),
|
SetFromGet(i32, i32),
|
||||||
|
|
||||||
// `call $make_refs; table.set x; table.set y; table.set z`
|
// `call $make_refs; table.set x; table.set y; table.set z`
|
||||||
SetFromMake(i32, i32, i32),
|
SetFromMake(i32, i32, i32),
|
||||||
|
|
||||||
|
// `(global.set x (local.get y))`
|
||||||
|
SetGlobalFromParam(u32, u32),
|
||||||
|
|
||||||
|
// `(global.set x (table.get y))`
|
||||||
|
SetGlobalFromGet(u32, i32),
|
||||||
|
|
||||||
|
// `call $make_refs; global.set x; global.set y; global.set z`
|
||||||
|
SetGlobalFromMake(u32, u32, u32),
|
||||||
|
|
||||||
// `call $make_refs; drop; drop; drop;`
|
// `call $make_refs; drop; drop; drop;`
|
||||||
Make,
|
Make,
|
||||||
|
|
||||||
// `local.get x; local.get y; local.get z; call $take_refs`
|
// `local.get x; local.get y; local.get z; call $take_refs`
|
||||||
TakeFromParams(u32, u32, u32),
|
TakeFromParams(u32, u32, u32),
|
||||||
|
|
||||||
// `table.get x; table.get y; table.get z; call $take_refs`
|
// `table.get x; table.get y; table.get z; call $take_refs`
|
||||||
TakeFromGet(i32, i32, i32),
|
TakeFromGet(i32, i32, i32),
|
||||||
|
|
||||||
|
// `global.get x; global.get y; global.get z; call $take_refs`
|
||||||
|
TakeFromGlobalGet(u32, u32, u32),
|
||||||
|
|
||||||
// `call $make_refs; call $take_refs`
|
// `call $make_refs; call $take_refs`
|
||||||
TakeFromMake,
|
TakeFromMake,
|
||||||
|
|
||||||
// `call $gc; call $take_refs`
|
// `call $gc; call $take_refs`
|
||||||
TakeFromGc,
|
TakeFromGc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableOp {
|
impl TableOp {
|
||||||
fn insert(&self, func: &mut Function, num_params: u32, table_size: u32) {
|
fn insert(self, func: &mut Function, num_params: u32, table_size: u32, num_globals: u32) {
|
||||||
assert!(num_params > 0);
|
assert!(num_params > 0);
|
||||||
assert!(table_size > 0);
|
assert!(table_size > 0);
|
||||||
|
|
||||||
@@ -165,49 +216,53 @@ impl TableOp {
|
|||||||
// but still rare.
|
// but still rare.
|
||||||
let table_mod = table_size as i32 + 1;
|
let table_mod = table_size as i32 + 1;
|
||||||
|
|
||||||
|
let gc_func_idx = 0;
|
||||||
|
let take_refs_func_idx = 1;
|
||||||
|
let make_refs_func_idx = 2;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::Gc => {
|
Self::Gc => {
|
||||||
func.instruction(Instruction::Call(0));
|
func.instruction(Instruction::Call(gc_func_idx));
|
||||||
func.instruction(Instruction::Drop);
|
func.instruction(Instruction::Drop);
|
||||||
func.instruction(Instruction::Drop);
|
func.instruction(Instruction::Drop);
|
||||||
func.instruction(Instruction::Drop);
|
func.instruction(Instruction::Drop);
|
||||||
}
|
}
|
||||||
Self::Get(x) => {
|
Self::Get(x) => {
|
||||||
func.instruction(Instruction::I32Const(*x % table_mod));
|
func.instruction(Instruction::I32Const(x % table_mod));
|
||||||
func.instruction(Instruction::TableGet { table: 0 });
|
func.instruction(Instruction::TableGet { table: 0 });
|
||||||
func.instruction(Instruction::Drop);
|
func.instruction(Instruction::Drop);
|
||||||
}
|
}
|
||||||
Self::SetFromParam(x, y) => {
|
Self::SetFromParam(x, y) => {
|
||||||
func.instruction(Instruction::I32Const(*x % table_mod));
|
func.instruction(Instruction::I32Const(x % table_mod));
|
||||||
func.instruction(Instruction::LocalGet(*y % num_params));
|
func.instruction(Instruction::LocalGet(y % num_params));
|
||||||
func.instruction(Instruction::TableSet { table: 0 });
|
func.instruction(Instruction::TableSet { table: 0 });
|
||||||
}
|
}
|
||||||
Self::SetFromGet(x, y) => {
|
Self::SetFromGet(x, y) => {
|
||||||
func.instruction(Instruction::I32Const(*x % table_mod));
|
func.instruction(Instruction::I32Const(x % table_mod));
|
||||||
func.instruction(Instruction::I32Const(*y % table_mod));
|
func.instruction(Instruction::I32Const(y % table_mod));
|
||||||
func.instruction(Instruction::TableGet { table: 0 });
|
func.instruction(Instruction::TableGet { table: 0 });
|
||||||
func.instruction(Instruction::TableSet { table: 0 });
|
func.instruction(Instruction::TableSet { table: 0 });
|
||||||
}
|
}
|
||||||
Self::SetFromMake(x, y, z) => {
|
Self::SetFromMake(x, y, z) => {
|
||||||
func.instruction(Instruction::Call(2));
|
func.instruction(Instruction::Call(make_refs_func_idx));
|
||||||
|
|
||||||
func.instruction(Instruction::LocalSet(num_params));
|
func.instruction(Instruction::LocalSet(num_params));
|
||||||
func.instruction(Instruction::I32Const(*x % table_mod));
|
func.instruction(Instruction::I32Const(x % table_mod));
|
||||||
func.instruction(Instruction::LocalGet(num_params));
|
func.instruction(Instruction::LocalGet(num_params));
|
||||||
func.instruction(Instruction::TableSet { table: 0 });
|
func.instruction(Instruction::TableSet { table: 0 });
|
||||||
|
|
||||||
func.instruction(Instruction::LocalSet(num_params));
|
func.instruction(Instruction::LocalSet(num_params));
|
||||||
func.instruction(Instruction::I32Const(*y % table_mod));
|
func.instruction(Instruction::I32Const(y % table_mod));
|
||||||
func.instruction(Instruction::LocalGet(num_params));
|
func.instruction(Instruction::LocalGet(num_params));
|
||||||
func.instruction(Instruction::TableSet { table: 0 });
|
func.instruction(Instruction::TableSet { table: 0 });
|
||||||
|
|
||||||
func.instruction(Instruction::LocalSet(num_params));
|
func.instruction(Instruction::LocalSet(num_params));
|
||||||
func.instruction(Instruction::I32Const(*z % table_mod));
|
func.instruction(Instruction::I32Const(z % table_mod));
|
||||||
func.instruction(Instruction::LocalGet(num_params));
|
func.instruction(Instruction::LocalGet(num_params));
|
||||||
func.instruction(Instruction::TableSet { table: 0 });
|
func.instruction(Instruction::TableSet { table: 0 });
|
||||||
}
|
}
|
||||||
TableOp::Make => {
|
TableOp::Make => {
|
||||||
func.instruction(Instruction::Call(2));
|
func.instruction(Instruction::Call(make_refs_func_idx));
|
||||||
func.instruction(Instruction::Drop);
|
func.instruction(Instruction::Drop);
|
||||||
func.instruction(Instruction::Drop);
|
func.instruction(Instruction::Drop);
|
||||||
func.instruction(Instruction::Drop);
|
func.instruction(Instruction::Drop);
|
||||||
@@ -216,27 +271,52 @@ impl TableOp {
|
|||||||
func.instruction(Instruction::LocalGet(x % num_params));
|
func.instruction(Instruction::LocalGet(x % num_params));
|
||||||
func.instruction(Instruction::LocalGet(y % num_params));
|
func.instruction(Instruction::LocalGet(y % num_params));
|
||||||
func.instruction(Instruction::LocalGet(z % num_params));
|
func.instruction(Instruction::LocalGet(z % num_params));
|
||||||
func.instruction(Instruction::Call(1));
|
func.instruction(Instruction::Call(take_refs_func_idx));
|
||||||
}
|
}
|
||||||
TableOp::TakeFromGet(x, y, z) => {
|
TableOp::TakeFromGet(x, y, z) => {
|
||||||
func.instruction(Instruction::I32Const(*x % table_mod));
|
func.instruction(Instruction::I32Const(x % table_mod));
|
||||||
func.instruction(Instruction::TableGet { table: 0 });
|
func.instruction(Instruction::TableGet { table: 0 });
|
||||||
|
|
||||||
func.instruction(Instruction::I32Const(*y % table_mod));
|
func.instruction(Instruction::I32Const(y % table_mod));
|
||||||
func.instruction(Instruction::TableGet { table: 0 });
|
func.instruction(Instruction::TableGet { table: 0 });
|
||||||
|
|
||||||
func.instruction(Instruction::I32Const(*z % table_mod));
|
func.instruction(Instruction::I32Const(z % table_mod));
|
||||||
func.instruction(Instruction::TableGet { table: 0 });
|
func.instruction(Instruction::TableGet { table: 0 });
|
||||||
|
|
||||||
func.instruction(Instruction::Call(1));
|
func.instruction(Instruction::Call(take_refs_func_idx));
|
||||||
}
|
}
|
||||||
TableOp::TakeFromMake => {
|
TableOp::TakeFromMake => {
|
||||||
func.instruction(Instruction::Call(2));
|
func.instruction(Instruction::Call(make_refs_func_idx));
|
||||||
func.instruction(Instruction::Call(1));
|
func.instruction(Instruction::Call(take_refs_func_idx));
|
||||||
}
|
}
|
||||||
Self::TakeFromGc => {
|
Self::TakeFromGc => {
|
||||||
func.instruction(Instruction::Call(0));
|
func.instruction(Instruction::Call(gc_func_idx));
|
||||||
func.instruction(Instruction::Call(1));
|
func.instruction(Instruction::Call(take_refs_func_idx));
|
||||||
|
}
|
||||||
|
TableOp::GetGlobal(x) => {
|
||||||
|
func.instruction(Instruction::GlobalGet(x % num_globals));
|
||||||
|
func.instruction(Instruction::Drop);
|
||||||
|
}
|
||||||
|
TableOp::SetGlobalFromParam(global, param) => {
|
||||||
|
func.instruction(Instruction::LocalGet(param % num_params));
|
||||||
|
func.instruction(Instruction::GlobalSet(global % num_globals));
|
||||||
|
}
|
||||||
|
TableOp::SetGlobalFromGet(global, x) => {
|
||||||
|
func.instruction(Instruction::I32Const(x));
|
||||||
|
func.instruction(Instruction::TableGet { table: 0 });
|
||||||
|
func.instruction(Instruction::GlobalSet(global % num_globals));
|
||||||
|
}
|
||||||
|
TableOp::SetGlobalFromMake(x, y, z) => {
|
||||||
|
func.instruction(Instruction::Call(make_refs_func_idx));
|
||||||
|
func.instruction(Instruction::GlobalSet(x % num_globals));
|
||||||
|
func.instruction(Instruction::GlobalSet(y % num_globals));
|
||||||
|
func.instruction(Instruction::GlobalSet(z % num_globals));
|
||||||
|
}
|
||||||
|
TableOp::TakeFromGlobalGet(x, y, z) => {
|
||||||
|
func.instruction(Instruction::GlobalGet(x % num_globals));
|
||||||
|
func.instruction(Instruction::GlobalGet(y % num_globals));
|
||||||
|
func.instruction(Instruction::GlobalGet(z % num_globals));
|
||||||
|
func.instruction(Instruction::Call(take_refs_func_idx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,6 +330,7 @@ mod tests {
|
|||||||
fn test_wat_string() {
|
fn test_wat_string() {
|
||||||
let ops = TableOps {
|
let ops = TableOps {
|
||||||
num_params: 5,
|
num_params: 5,
|
||||||
|
num_globals: 1,
|
||||||
table_size: 20,
|
table_size: 20,
|
||||||
ops: vec![
|
ops: vec![
|
||||||
TableOp::Gc,
|
TableOp::Gc,
|
||||||
@@ -261,6 +342,11 @@ mod tests {
|
|||||||
TableOp::TakeFromParams(8, 9, 10),
|
TableOp::TakeFromParams(8, 9, 10),
|
||||||
TableOp::TakeFromGet(11, 12, 13),
|
TableOp::TakeFromGet(11, 12, 13),
|
||||||
TableOp::TakeFromMake,
|
TableOp::TakeFromMake,
|
||||||
|
TableOp::GetGlobal(14),
|
||||||
|
TableOp::SetGlobalFromParam(15, 16),
|
||||||
|
TableOp::SetGlobalFromGet(17, 18),
|
||||||
|
TableOp::SetGlobalFromMake(19, 20, 21),
|
||||||
|
TableOp::TakeFromGlobalGet(22, 23, 24),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -320,9 +406,25 @@ mod tests {
|
|||||||
call 1
|
call 1
|
||||||
call 2
|
call 2
|
||||||
call 1
|
call 1
|
||||||
|
global.get 0
|
||||||
|
drop
|
||||||
|
local.get 1
|
||||||
|
global.set 0
|
||||||
|
i32.const 18
|
||||||
|
table.get 0
|
||||||
|
global.set 0
|
||||||
|
call 2
|
||||||
|
global.set 0
|
||||||
|
global.set 0
|
||||||
|
global.set 0
|
||||||
|
global.get 0
|
||||||
|
global.get 0
|
||||||
|
global.get 0
|
||||||
|
call 1
|
||||||
br 0 (;@1;)
|
br 0 (;@1;)
|
||||||
end)
|
end)
|
||||||
(table (;0;) 20 externref)
|
(table (;0;) 20 externref)
|
||||||
|
(global (;0;) (mut externref) (ref.null extern))
|
||||||
(export "run" (func 3)))
|
(export "run" (func 3)))
|
||||||
"#;
|
"#;
|
||||||
eprintln!("expected WAT = {}", expected);
|
eprintln!("expected WAT = {}", expected);
|
||||||
|
|||||||
Reference in New Issue
Block a user