wasm: simplify ElementSection

This commit is contained in:
Brian Carroll 2022-02-27 23:52:25 +00:00
parent 7958158d89
commit 4e7c1fe5e1

View file

@ -760,114 +760,33 @@ impl<'a> Serialize for ExportSection<'a> {
*
*******************************************************************/
/*
https://webassembly.github.io/spec/core/binary/modules.html#binary-elemsec
Note: Wasm MVP only had variant 0x00, and tables only contained functions.
byte fields syntax
-------------------------------------------------------------------------------------------------------------------------------------
0x00 e:expr y:vec(funcidx) {type funcref, init ((ref.func y) end), mode active {table 0, offset e}}
0x01 et:elemkind y:vec(funcidx) {type et, init ((ref.func y) end), mode passive}
0x02 x:tableidx e:expr et:elemkind y:vec(funcidx) {type et, init ((ref.func y) end), mode active {table x, offset e}}
0x03 et:elemkind y:vec(funcidx) {type et, init ((ref.func y) end), mode declarative}
0x04 e:expr el:vec(expr) {type funcref, init el, mode active {table 0, offset e}}
0x05 et:reftype el:vec(expr) {type et, init el, mode passive}
0x06 x:tableidx e:expr et:reftype el:vec(expr) {type et, init el, mode active {table x, offset e}}
0x07 et:reftype el:vec(expr) {type et, init el , mode declarative}
The initial byte can be interpreted as a bitfield
Bit 0 indicates a passive or declarative segment
Bit 1 indicates the presence of an explicit table index for an active segment and otherwise distinguishes passive from declarative segments
Bit 2 indicates the use of element type and element expressions instead of element kind and element indices.
Active means "element is loaded into the table on module instantiation"
Passive means it's loaded by explicit instructions in the code section
Declarative is a declaration that a reference will be taken at runtime
Sigh. This is only the post-MVP version of Wasm! Some day it'll end up as convoluted as x86, wait and see.
*/
#[repr(u8)]
#[derive(Debug)]
#[allow(dead_code)]
enum ElementKind {
FuncRef = 0,
}
#[repr(u8)]
#[allow(dead_code)]
enum ElementSegmentFormatId {
/// Currently only supporting the original Wasm MVP format since it's the only one in wide use.
/// There are newer formats for other table types, with complex encodings to preserve backward compatibility
/// (Already going down the same path as x86!)
ActiveImplicitTableIndex = 0x00,
PassiveKindAndIndex = 0x01,
ActiveExplicitTableKindAndIndex = 0x02,
DeclarativeKindAndIndex = 0x03,
ActiveImplicitTableTypeAndExpr = 0x04,
PassiveTypeAndExpr = 0x05,
ActiveExplicitTableTypeAndExpr = 0x06,
DeclarativeTypeAndExpr = 0x07,
}
// A representation based on the (convoluted) binary format.
// NOTE: If we ever need a more intuitive format, we can base it on the syntax doc
// https://webassembly.github.io/spec/core/syntax/modules.html#syntax-elem
#[derive(Debug)]
#[allow(dead_code)]
enum ElementSegment<'a> {
/// This is the only variant we currently use. It is from the original Wasm MVP.
/// The rest are dead code but kept in case we need them later.
ActiveImplicitTableIndex {
offset: ConstExpr,
fn_indices: Vec<'a, u32>,
},
PassiveKindAndIndex {
kind: ElementKind,
fn_indices: Vec<'a, u32>,
},
ActiveExplicitTableKindAndIndex {
table_idx: u32,
offset: ConstExpr,
kind: ElementKind,
fn_indices: Vec<'a, u32>,
},
DeclarativeKindAndIndex {
kind: ElementKind,
fn_indices: Vec<'a, u32>,
},
ActiveImplicitTableTypeAndExpr {
offset: ConstExpr,
init: Vec<'a, ConstExpr>,
},
PassiveTypeAndExpr {
elem_type: RefType,
init: Vec<'a, ConstExpr>,
},
ActiveExplicitTableTypeAndExpr {
table_idx: u32,
offset: ConstExpr,
elem_type: RefType,
init: Vec<'a, ConstExpr>,
},
DeclarativeTypeAndExpr {
elem_type: RefType,
init: Vec<'a, ConstExpr>,
},
struct ElementSegment<'a> {
offset: ConstExpr,
fn_indices: Vec<'a, u32>,
}
impl<'a> ElementSegment<'a> {
fn parse(arena: &'a Bump, bytes: &[u8], cursor: &mut usize) -> Self {
// In practice we only need the original MVP format
let format_id = bytes[*cursor];
assert!(format_id == ElementSegmentFormatId::ActiveImplicitTableIndex as u8);
debug_assert!(format_id == ElementSegmentFormatId::ActiveImplicitTableIndex as u8);
*cursor += 1;
// The table index offset is encoded as a ConstExpr, but only I32 makes sense
let const_expr_opcode = bytes[*cursor];
assert!(const_expr_opcode == OpCode::I32CONST as u8);
debug_assert!(const_expr_opcode == OpCode::I32CONST as u8);
*cursor += 1;
let offset = parse_u32_or_panic(bytes, cursor);
assert!(bytes[*cursor] == OpCode::END as u8);
debug_assert!(bytes[*cursor] == OpCode::END as u8);
*cursor += 1;
let num_elems = parse_u32_or_panic(bytes, cursor);
@ -878,7 +797,7 @@ impl<'a> ElementSegment<'a> {
fn_indices.push(fn_idx);
}
ElementSegment::ActiveImplicitTableIndex {
ElementSegment {
offset: ConstExpr::I32(offset as i32),
fn_indices,
}
@ -889,25 +808,16 @@ impl<'a> ElementSegment<'a> {
let constexpr_opcode = 1;
let constexpr_value = MAX_SIZE_ENCODED_U32;
let vec_len = MAX_SIZE_ENCODED_U32;
let vec_contents = MAX_SIZE_ENCODED_U32
* if let ElementSegment::ActiveImplicitTableIndex { fn_indices, .. } = &self {
fn_indices.len()
} else {
internal_error!("Unsupported ElementSegment {:?}", self)
};
let vec_contents = MAX_SIZE_ENCODED_U32 * self.fn_indices.len();
variant_id + constexpr_opcode + constexpr_value + vec_len + vec_contents
}
}
impl<'a> Serialize for ElementSegment<'a> {
fn serialize<T: SerialBuffer>(&self, buffer: &mut T) {
if let ElementSegment::ActiveImplicitTableIndex { offset, fn_indices } = &self {
buffer.append_u8(ElementSegmentFormatId::ActiveImplicitTableIndex as u8);
offset.serialize(buffer);
fn_indices.serialize(buffer);
} else {
internal_error!("Unsupported ElementSegment {:?}", self)
}
buffer.append_u8(ElementSegmentFormatId::ActiveImplicitTableIndex as u8);
self.offset.serialize(buffer);
self.fn_indices.serialize(buffer);
}
}
@ -940,11 +850,7 @@ impl<'a> ElementSection<'a> {
pub fn indirect_callees(&self, arena: &'a Bump) -> Vec<'a, u32> {
let mut result = bumpalo::vec![in arena];
for segment in self.segments.iter() {
if let ElementSegment::ActiveImplicitTableIndex { fn_indices, .. } = segment {
result.extend_from_slice(fn_indices);
} else {
internal_error!("Unsupported ElementSegment {:?}", self)
}
result.extend_from_slice(&segment.fn_indices);
}
result
}