Skip to content

Commit 31dca17

Browse files
authored
Implement the table64 extension to the memory64 proposal (#1531)
* Fix new gc local.tee test Fix validation of `local.tee` to push the local's type, not the actual type. * Update the spec testsuite submodule * Implement the table64 extension to the memory64 proposal This commit implements validation, fuzzing, printing, etc, for the extension of the memory64 proposal that adds 64-bit tables. These tables work similarly to memories where the index type is statically listed and is required for all various operations. The bulk of the changes here were to wasm-smith to generate modules with these new table instructions. * Fix wasm-smith processing `local.tee` * Fix construction of `table.init` instructions
1 parent 1ca522a commit 31dca17

488 files changed

Lines changed: 267627 additions & 1338 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/fuzz-stats/src/bin/failed-instantiations.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ impl State {
9999
let mut config = wasm_smith::Config::arbitrary(&mut u)?;
100100
config.allow_start_export = false;
101101

102+
// NB: just added "table64" support to this and wasmtime doens't
103+
// implement that yet
104+
config.memory64_enabled = false;
105+
102106
// Wasmtime doesn't support these proposals yet.
103107
config.gc_enabled = false;
104108

crates/wasm-encoder/src/core/elements.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId};
1717
/// element_type: RefType::FUNCREF,
1818
/// minimum: 128,
1919
/// maximum: None,
20+
/// table64: false,
2021
/// });
2122
///
2223
/// let mut elements = ElementSection::new();

crates/wasm-encoder/src/core/tables.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId};
1+
use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId, ValType};
22

33
/// An encoder for the table section.
44
///
@@ -14,6 +14,7 @@ use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId};
1414
/// element_type: RefType::FUNCREF,
1515
/// minimum: 128,
1616
/// maximum: None,
17+
/// table64: false,
1718
/// });
1819
///
1920
/// let mut module = Module::new();
@@ -80,10 +81,23 @@ impl Section for TableSection {
8081
pub struct TableType {
8182
/// The table's element type.
8283
pub element_type: RefType,
84+
/// Whether or not this is a 64-bit table.
85+
pub table64: bool,
8386
/// Minimum size, in elements, of this table
84-
pub minimum: u32,
87+
pub minimum: u64,
8588
/// Maximum size, in elements, of this table
86-
pub maximum: Option<u32>,
89+
pub maximum: Option<u64>,
90+
}
91+
92+
impl TableType {
93+
/// Returns the type used to index this table.
94+
pub fn index_type(&self) -> ValType {
95+
if self.table64 {
96+
ValType::I64
97+
} else {
98+
ValType::I32
99+
}
100+
}
87101
}
88102

89103
impl Encode for TableType {
@@ -92,6 +106,9 @@ impl Encode for TableType {
92106
if self.maximum.is_some() {
93107
flags |= 0b001;
94108
}
109+
if self.table64 {
110+
flags |= 0b100;
111+
}
95112

96113
self.element_type.encode(sink);
97114
sink.push(flags);
@@ -111,6 +128,7 @@ impl TryFrom<wasmparser::TableType> for TableType {
111128
element_type: table_ty.element_type.try_into()?,
112129
minimum: table_ty.initial,
113130
maximum: table_ty.maximum,
131+
table64: table_ty.table64,
114132
})
115133
}
116134
}

crates/wasm-mutate/src/mutators/translate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ pub fn table_type(
155155
element_type: t.translate_refty(&ty.element_type)?,
156156
minimum: ty.initial,
157157
maximum: ty.maximum,
158+
table64: ty.table64,
158159
})
159160
}
160161

crates/wasm-smith/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ define_config! {
404404

405405
/// The maximum, elements, of any table's initial or maximum
406406
/// size. Defaults to 1 million.
407-
pub max_table_elements: u32 = 1_000_000,
407+
pub max_table_elements: u64 = 1_000_000,
408408

409409
/// The maximum number of tables to use. Defaults to 1.
410410
///

crates/wasm-smith/src/core.rs

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,45 +1887,60 @@ impl Module {
18871887

18881888
fn arbitrary_elems(&mut self, u: &mut Unstructured) -> Result<()> {
18891889
// Create a helper closure to choose an arbitrary offset.
1890-
let mut offset_global_choices = vec![];
1890+
let mut global_i32 = vec![];
1891+
let mut global_i64 = vec![];
18911892
if !self.config.disallow_traps {
18921893
for i in self.globals_for_const_expr(ValType::I32) {
1893-
offset_global_choices.push(i);
1894+
global_i32.push(i);
1895+
}
1896+
for i in self.globals_for_const_expr(ValType::I64) {
1897+
global_i64.push(i);
18941898
}
18951899
}
18961900
let disallow_traps = self.config.disallow_traps;
1897-
let arbitrary_active_elem = |u: &mut Unstructured,
1898-
min_mem_size: u32,
1899-
table: Option<u32>,
1900-
table_ty: &TableType| {
1901-
let (offset, max_size_hint) = if !offset_global_choices.is_empty() && u.arbitrary()? {
1902-
let g = u.choose(&offset_global_choices)?;
1903-
(Offset::Global(*g), None)
1904-
} else {
1905-
let max_mem_size = if disallow_traps {
1906-
table_ty.minimum
1901+
let arbitrary_active_elem =
1902+
|u: &mut Unstructured, min_mem_size: u64, table: Option<u32>, table_ty: &TableType| {
1903+
let global_choices = if table_ty.table64 {
1904+
&global_i64
19071905
} else {
1908-
u32::MAX
1906+
&global_i32
19091907
};
1910-
let offset =
1911-
arbitrary_offset(u, min_mem_size.into(), max_mem_size.into(), 0)? as u32;
1912-
let max_size_hint = if disallow_traps
1913-
|| (offset <= min_mem_size && u.int_in_range(0..=CHANCE_OFFSET_INBOUNDS)? != 0)
1914-
{
1915-
Some(min_mem_size - offset)
1908+
let (offset, max_size_hint) = if !global_choices.is_empty() && u.arbitrary()? {
1909+
let g = u.choose(&global_choices)?;
1910+
(Offset::Global(*g), None)
19161911
} else {
1917-
None
1912+
let max_mem_size = if disallow_traps {
1913+
table_ty.minimum
1914+
} else if table_ty.table64 {
1915+
u64::MAX
1916+
} else {
1917+
u64::from(u32::MAX)
1918+
};
1919+
let offset = arbitrary_offset(u, min_mem_size, max_mem_size, 0)?;
1920+
let max_size_hint = if disallow_traps
1921+
|| (offset <= min_mem_size
1922+
&& u.int_in_range(0..=CHANCE_OFFSET_INBOUNDS)? != 0)
1923+
{
1924+
Some(min_mem_size - offset)
1925+
} else {
1926+
None
1927+
};
1928+
1929+
let offset = if table_ty.table64 {
1930+
Offset::Const64(offset as i64)
1931+
} else {
1932+
Offset::Const32(offset as i32)
1933+
};
1934+
(offset, max_size_hint)
19181935
};
1919-
(Offset::Const32(offset as i32), max_size_hint)
1936+
Ok((ElementKind::Active { table, offset }, max_size_hint))
19201937
};
1921-
Ok((ElementKind::Active { table, offset }, max_size_hint))
1922-
};
19231938

19241939
// Generate a list of candidates for "kinds" of elements segments. For
19251940
// example we can have an active segment for any existing table or
19261941
// passive/declared segments if the right wasm features are enabled.
19271942
type GenElemSegment<'a> =
1928-
dyn Fn(&mut Unstructured) -> Result<(ElementKind, Option<u32>)> + 'a;
1943+
dyn Fn(&mut Unstructured) -> Result<(ElementKind, Option<u64>)> + 'a;
19291944
let mut choices: Vec<Box<GenElemSegment>> = Vec::new();
19301945

19311946
// Bulk memory enables passive/declared segments, and note that the
@@ -2400,26 +2415,6 @@ impl Module {
24002415
}
24012416
}
24022417

2403-
pub(crate) fn arbitrary_limits32(
2404-
u: &mut Unstructured,
2405-
min_minimum: Option<u32>,
2406-
max_minimum: u32,
2407-
max_required: bool,
2408-
max_inbounds: u32,
2409-
) -> Result<(u32, Option<u32>)> {
2410-
let (min, max) = arbitrary_limits64(
2411-
u,
2412-
min_minimum.map(Into::into),
2413-
max_minimum.into(),
2414-
max_required,
2415-
max_inbounds.into(),
2416-
)?;
2417-
Ok((
2418-
u32::try_from(min).unwrap(),
2419-
max.map(|i| u32::try_from(i).unwrap()),
2420-
))
2421-
}
2422-
24232418
pub(crate) fn arbitrary_limits64(
24242419
u: &mut Unstructured,
24252420
min_minimum: Option<u64>,
@@ -2485,12 +2480,13 @@ pub(crate) fn arbitrary_table_type(
24852480
config: &Config,
24862481
module: Option<&Module>,
24872482
) -> Result<TableType> {
2483+
let table64 = config.memory64_enabled && u.arbitrary()?;
24882484
// We don't want to generate tables that are too large on average, so
24892485
// keep the "inbounds" limit here a bit smaller.
24902486
let max_inbounds = 10_000;
24912487
let min_elements = if config.disallow_traps { Some(1) } else { None };
24922488
let max_elements = min_elements.unwrap_or(0).max(config.max_table_elements);
2493-
let (minimum, maximum) = arbitrary_limits32(
2489+
let (minimum, maximum) = arbitrary_limits64(
24942490
u,
24952491
min_elements,
24962492
max_elements,
@@ -2508,6 +2504,7 @@ pub(crate) fn arbitrary_table_type(
25082504
element_type,
25092505
minimum,
25102506
maximum,
2507+
table64,
25112508
})
25122509
}
25132510

0 commit comments

Comments
 (0)