Handle typed funcrefs.

This commit is contained in:
Chris Fallin 2024-03-28 16:36:07 -07:00
parent fcbd32e6b4
commit f0b378a895
6 changed files with 122 additions and 41 deletions

View file

@ -909,6 +909,13 @@ impl<'a> WasmFuncBackend<'a> {
}
Operator::F32x4DemoteF64x2Zero => Some(wasm_encoder::Instruction::F32x4DemoteF64x2Zero),
Operator::F64x2PromoteLowF32x4 => Some(wasm_encoder::Instruction::F64x2PromoteLowF32x4),
Operator::CallRef { sig_index } => {
Some(wasm_encoder::Instruction::CallRef(sig_index.index() as u32))
}
Operator::RefFunc { func_index } => {
Some(wasm_encoder::Instruction::RefFunc(func_index.index() as u32))
}
};
if let Some(inst) = inst {
@ -955,7 +962,7 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<Vec<u8>> {
.func_elements
.as_ref()
.map(|elts| elts.len() as u32)
.unwrap_or(0),
.unwrap_or(table.initial),
maximum: table.max,
})
}
@ -1081,11 +1088,26 @@ pub fn compile(module: &Module<'_>) -> anyhow::Result<Vec<u8>> {
if let Some(elts) = &table_data.func_elements {
for (i, &elt) in elts.iter().enumerate() {
if elt.is_valid() {
elem.active(
Some(table.index() as u32),
&wasm_encoder::ConstExpr::i32_const(i as i32),
wasm_encoder::Elements::Functions(&[elt.index() as u32]),
);
match table_data.ty {
Type::FuncRef => {
elem.active(
Some(table.index() as u32),
&wasm_encoder::ConstExpr::i32_const(i as i32),
wasm_encoder::Elements::Functions(&[elt.index() as u32]),
);
}
Type::TypedFuncRef(..) => {
elem.active(
Some(table.index() as u32),
&wasm_encoder::ConstExpr::i32_const(i as i32),
wasm_encoder::Elements::Expressions(
table_data.ty.into(),
&[wasm_encoder::ConstExpr::ref_func(elt.index() as u32)],
),
);
}
_ => unreachable!(),
}
}
}
}

View file

@ -133,7 +133,11 @@ fn handle_payload<'a>(
ImportKind::Global(global)
}
TypeRef::Table(ty) => {
let table = module.frontend_add_table(ty.element_type.into(), None);
let table = module.frontend_add_table(
ty.element_type.into(),
ty.initial,
ty.maximum,
);
ImportKind::Table(table)
}
TypeRef::Memory(mem) => {
@ -174,7 +178,11 @@ fn handle_payload<'a>(
Payload::TableSection(reader) => {
for table in reader {
let table = table?;
module.frontend_add_table(table.ty.element_type.into(), table.ty.maximum);
module.frontend_add_table(
table.ty.element_type.into(),
table.ty.initial,
table.ty.maximum,
);
}
}
Payload::FunctionSection(reader) => {
@ -318,7 +326,7 @@ fn handle_payload<'a>(
} => {
let table = Table::from(table_index.unwrap_or(0));
let offset = parse_init_expr(&offset_expr)?.unwrap_or(0) as usize;
match element.items {
let funcs = match element.items {
wasmparser::ElementItems::Functions(items) => {
let mut funcs = vec![];
for item in items {
@ -326,35 +334,51 @@ fn handle_payload<'a>(
let func = Func::from(item);
funcs.push(func);
}
let table_items =
module.tables[table].func_elements.as_mut().unwrap();
let new_size =
offset.checked_add(funcs.len()).ok_or_else(|| {
FrontendError::TooLarge(format!(
"Overflowing element offset + length: {} + {}",
offset,
funcs.len()
))
})?;
if new_size > table_items.len() {
static MAX_TABLE: usize = 100_000;
if new_size > MAX_TABLE {
bail!(FrontendError::TooLarge(format!(
"Too many table elements: {:?}",
new_size
)));
funcs
}
wasmparser::ElementItems::Expressions(_, const_exprs) => {
let mut funcs = vec![];
for const_expr in const_exprs {
let const_expr = const_expr?;
let mut func = None;
for op in const_expr.get_operators_reader() {
let op = op?;
match op {
wasmparser::Operator::End => {}
wasmparser::Operator::RefFunc { function_index } => {
func = Some(Func::from(function_index));
}
wasmparser::Operator::RefNull { .. } => {
func = Some(Func::invalid());
}
_ => panic!("Unsupported table-init op: {:?}", op),
}
}
table_items.resize(new_size, Func::invalid());
funcs.push(func.unwrap_or(Func::invalid()));
}
table_items[offset..new_size].copy_from_slice(&funcs[..]);
funcs
}
wasmparser::ElementItems::Expressions(..) => {
bail!(FrontendError::UnsupportedFeature(
"Expression element items".into()
))
};
let table_items = module.tables[table].func_elements.as_mut().unwrap();
let new_size = offset.checked_add(funcs.len()).ok_or_else(|| {
FrontendError::TooLarge(format!(
"Overflowing element offset + length: {} + {}",
offset,
funcs.len()
))
})?;
if new_size > table_items.len() {
static MAX_TABLE: usize = 100_000;
if new_size > MAX_TABLE {
bail!(FrontendError::TooLarge(format!(
"Too many table elements: {:?}",
new_size
)));
}
table_items.resize(new_size, Func::invalid());
}
table_items[offset..new_size].copy_from_slice(&funcs[..]);
}
}
}
@ -1394,7 +1418,9 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> {
| wasmparser::Operator::F64x2ConvertLowI32x4S
| wasmparser::Operator::F64x2ConvertLowI32x4U
| wasmparser::Operator::F32x4DemoteF64x2Zero
| wasmparser::Operator::F64x2PromoteLowF32x4 => {
| wasmparser::Operator::F64x2PromoteLowF32x4
| wasmparser::Operator::CallRef { .. }
| wasmparser::Operator::RefFunc { .. } => {
self.emit(Operator::try_from(&op).unwrap(), loc)?
}

View file

@ -26,7 +26,6 @@ impl From<wasmparser::ValType> for Type {
}
impl From<wasmparser::RefType> for Type {
fn from(ty: wasmparser::RefType) -> Self {
assert!(ty.is_func_ref(), "only funcrefs are supported right now");
match ty.type_index() {
Some(idx) => {
let nullable = ty.is_nullable();

View file

@ -43,6 +43,7 @@ pub struct MemorySegment {
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TableData {
pub ty: Type,
pub initial: u32,
pub max: Option<u32>,
pub func_elements: Option<Vec<Func>>,
}
@ -170,15 +171,12 @@ impl<'a> Module<'a> {
}
impl<'a> Module<'a> {
pub(crate) fn frontend_add_table(&mut self, ty: Type, max: Option<u32>) -> Table {
let func_elements = if ty == Type::FuncRef {
Some(vec![])
} else {
None
};
pub(crate) fn frontend_add_table(&mut self, ty: Type, initial: u32, max: Option<u32>) -> Table {
let func_elements = Some(vec![]);
self.tables.push(TableData {
ty,
func_elements,
initial,
max,
})
}

View file

@ -1,5 +1,6 @@
//! Metadata on operators.
use crate::entity::EntityRef;
use crate::ir::{Module, Type, Value};
use crate::Operator;
use anyhow::Result;
@ -475,6 +476,13 @@ pub fn op_inputs(
Operator::F64x2ConvertLowI32x4U => Ok(Cow::Borrowed(&[Type::V128])),
Operator::F32x4DemoteF64x2Zero => Ok(Cow::Borrowed(&[Type::V128])),
Operator::F64x2PromoteLowF32x4 => Ok(Cow::Borrowed(&[Type::V128])),
Operator::CallRef { sig_index } => {
let mut params = module.signatures[*sig_index].params.to_vec();
params.push(Type::TypedFuncRef(true, sig_index.index() as u32));
Ok(params.into())
}
Operator::RefFunc { .. } => Ok(Cow::Borrowed(&[])),
}
}
@ -933,6 +941,14 @@ pub fn op_outputs(
Operator::F64x2ConvertLowI32x4U => Ok(Cow::Borrowed(&[Type::V128])),
Operator::F32x4DemoteF64x2Zero => Ok(Cow::Borrowed(&[Type::V128])),
Operator::F64x2PromoteLowF32x4 => Ok(Cow::Borrowed(&[Type::V128])),
Operator::CallRef { sig_index } => {
Ok(Vec::from(module.signatures[*sig_index].returns.clone()).into())
}
Operator::RefFunc { func_index } => {
let ty = module.funcs[*func_index].sig();
Ok(vec![Type::TypedFuncRef(true, ty.index() as u32)].into())
}
}
}
@ -1397,6 +1413,9 @@ impl Operator {
Operator::F64x2ConvertLowI32x4U => &[],
Operator::F32x4DemoteF64x2Zero => &[],
Operator::F64x2PromoteLowF32x4 => &[],
Operator::CallRef { .. } => &[All],
Operator::RefFunc { .. } => &[],
}
}
@ -1885,6 +1904,9 @@ impl std::fmt::Display for Operator {
Operator::F64x2ConvertLowI32x4U => write!(f, "f64x2convertlowi32x4u")?,
Operator::F32x4DemoteF64x2Zero => write!(f, "f32x4demotef64x2zero")?,
Operator::F64x2PromoteLowF32x4 => write!(f, "f64x2promotelowf32x4")?,
Operator::CallRef { sig_index } => write!(f, "call_ref<{}>", sig_index)?,
Operator::RefFunc { func_index } => write!(f, "ref_func<{}>", func_index)?,
}
Ok(())

View file

@ -630,6 +630,13 @@ pub enum Operator {
F64x2ConvertLowI32x4U,
F32x4DemoteF64x2Zero,
F64x2PromoteLowF32x4,
CallRef {
sig_index: Signature,
},
RefFunc {
func_index: Func,
},
}
#[test]
@ -1259,6 +1266,13 @@ impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator {
&wasmparser::Operator::F32x4DemoteF64x2Zero => Ok(Operator::F32x4DemoteF64x2Zero),
&wasmparser::Operator::F64x2PromoteLowF32x4 => Ok(Operator::F64x2PromoteLowF32x4),
&wasmparser::Operator::CallRef { type_index } => Ok(Operator::CallRef {
sig_index: Signature::from(type_index),
}),
&wasmparser::Operator::RefFunc { function_index } => Ok(Operator::RefFunc {
func_index: Func::from(function_index),
}),
_ => Err(()),
}
}