1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 15:37:46 +00:00

LibWasm+LibWeb: Parse and validate all Wasm SIMD instructions

This commit is contained in:
Ali Mohammad Pur 2023-06-12 13:04:22 +03:30 committed by Ali Mohammad Pur
parent b005691497
commit 2462064fcd
13 changed files with 2474 additions and 78 deletions

View file

@ -11,6 +11,7 @@
#include <AK/MemoryStream.h>
#include <AK/ScopeGuard.h>
#include <AK/ScopeLogger.h>
#include <AK/UFixedBigInt.h>
#include <LibWasm/Types.h>
namespace Wasm {
@ -138,6 +139,8 @@ ParseResult<ValueType> ValueType::parse(Stream& stream)
return ValueType(F32);
case Constants::f64_tag:
return ValueType(F64);
case Constants::v128_tag:
return ValueType(V128);
case Constants::function_reference_tag:
return ValueType(FunctionReference);
case Constants::extern_reference_tag:
@ -677,24 +680,27 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
case Instructions::i64_extend32_s.value():
resulting_instructions.append(Instruction { opcode });
break;
case 0xfc: {
case 0xfc:
case 0xfd: {
// These are multibyte instructions.
auto selector_or_error = stream.read_value<LEB128<u32>>();
if (selector_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
u32 selector = selector_or_error.release_value();
switch (selector) {
case Instructions::i32_trunc_sat_f32_s_second:
case Instructions::i32_trunc_sat_f32_u_second:
case Instructions::i32_trunc_sat_f64_s_second:
case Instructions::i32_trunc_sat_f64_u_second:
case Instructions::i64_trunc_sat_f32_s_second:
case Instructions::i64_trunc_sat_f32_u_second:
case Instructions::i64_trunc_sat_f64_s_second:
case Instructions::i64_trunc_sat_f64_u_second:
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector } });
OpCode full_opcode = static_cast<u64>(opcode.value()) << 56 | selector;
switch (full_opcode.value()) {
case Instructions::i32_trunc_sat_f32_s.value():
case Instructions::i32_trunc_sat_f32_u.value():
case Instructions::i32_trunc_sat_f64_s.value():
case Instructions::i32_trunc_sat_f64_u.value():
case Instructions::i64_trunc_sat_f32_s.value():
case Instructions::i64_trunc_sat_f32_u.value():
case Instructions::i64_trunc_sat_f64_s.value():
case Instructions::i64_trunc_sat_f64_u.value():
resulting_instructions.append(Instruction { full_opcode });
break;
case Instructions::memory_init_second: {
case Instructions::memory_init.value(): {
auto index = GenericIndexParser<DataIndex>::parse(stream);
if (index.is_error())
return index.error();
@ -705,17 +711,17 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
auto unused = unused_or_error.release_value();
if (unused != 0x00)
return ParseError::InvalidImmediate;
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector }, index.release_value() });
resulting_instructions.append(Instruction { full_opcode, index.release_value() });
break;
}
case Instructions::data_drop_second: {
case Instructions::data_drop.value(): {
auto index = GenericIndexParser<DataIndex>::parse(stream);
if (index.is_error())
return index.error();
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector }, index.release_value() });
resulting_instructions.append(Instruction { full_opcode, index.release_value() });
break;
}
case Instructions::memory_copy_second: {
case Instructions::memory_copy.value(): {
for (size_t i = 0; i < 2; ++i) {
auto unused_or_error = stream.read_value<u8>();
if (unused_or_error.is_error())
@ -725,10 +731,10 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
if (unused != 0x00)
return ParseError::InvalidImmediate;
}
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector } });
resulting_instructions.append(Instruction { full_opcode });
break;
}
case Instructions::memory_fill_second: {
case Instructions::memory_fill.value(): {
auto unused_or_error = stream.read_value<u8>();
if (unused_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
@ -736,45 +742,341 @@ ParseResult<Vector<Instruction>> Instruction::parse(Stream& stream, InstructionP
auto unused = unused_or_error.release_value();
if (unused != 0x00)
return ParseError::InvalidImmediate;
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector } });
resulting_instructions.append(Instruction { full_opcode });
break;
}
case Instructions::table_init_second: {
case Instructions::table_init.value(): {
auto element_index = GenericIndexParser<ElementIndex>::parse(stream);
if (element_index.is_error())
return element_index.error();
auto table_index = GenericIndexParser<TableIndex>::parse(stream);
if (table_index.is_error())
return table_index.error();
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector }, TableElementArgs { element_index.release_value(), table_index.release_value() } });
resulting_instructions.append(Instruction { full_opcode, TableElementArgs { element_index.release_value(), table_index.release_value() } });
break;
}
case Instructions::elem_drop_second: {
case Instructions::elem_drop.value(): {
auto element_index = GenericIndexParser<ElementIndex>::parse(stream);
if (element_index.is_error())
return element_index.error();
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector }, element_index.release_value() });
resulting_instructions.append(Instruction { full_opcode, element_index.release_value() });
break;
}
case Instructions::table_copy_second: {
case Instructions::table_copy.value(): {
auto lhs = GenericIndexParser<TableIndex>::parse(stream);
if (lhs.is_error())
return lhs.error();
auto rhs = GenericIndexParser<TableIndex>::parse(stream);
if (rhs.is_error())
return rhs.error();
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector }, TableTableArgs { lhs.release_value(), rhs.release_value() } });
resulting_instructions.append(Instruction { full_opcode, TableTableArgs { lhs.release_value(), rhs.release_value() } });
break;
}
case Instructions::table_grow_second:
case Instructions::table_size_second:
case Instructions::table_fill_second: {
case Instructions::table_grow.value():
case Instructions::table_size.value():
case Instructions::table_fill.value(): {
auto index = GenericIndexParser<TableIndex>::parse(stream);
if (index.is_error())
return index.error();
resulting_instructions.append(Instruction { OpCode { 0xfc00 | selector }, index.release_value() });
resulting_instructions.append(Instruction { full_opcode, index.release_value() });
break;
}
case Instructions::v128_load.value():
case Instructions::v128_load8x8_s.value():
case Instructions::v128_load8x8_u.value():
case Instructions::v128_load16x4_s.value():
case Instructions::v128_load16x4_u.value():
case Instructions::v128_load32x2_s.value():
case Instructions::v128_load32x2_u.value():
case Instructions::v128_load8_splat.value():
case Instructions::v128_load16_splat.value():
case Instructions::v128_load32_splat.value():
case Instructions::v128_load64_splat.value():
case Instructions::v128_store.value(): {
// op (align offset)
auto align_or_error = stream.read_value<LEB128<size_t>>();
if (align_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t align = align_or_error.release_value();
auto offset_or_error = stream.read_value<LEB128<size_t>>();
if (offset_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t offset = offset_or_error.release_value();
resulting_instructions.append(Instruction { full_opcode, MemoryArgument { static_cast<u32>(align), static_cast<u32>(offset) } });
break;
}
case Instructions::v128_load8_lane.value():
case Instructions::v128_load16_lane.value():
case Instructions::v128_load32_lane.value():
case Instructions::v128_load64_lane.value():
case Instructions::v128_store8_lane.value():
case Instructions::v128_store16_lane.value():
case Instructions::v128_store32_lane.value():
case Instructions::v128_store64_lane.value(): {
// op (align offset) (index)
auto align_or_error = stream.read_value<LEB128<size_t>>();
if (align_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t align = align_or_error.release_value();
auto offset_or_error = stream.read_value<LEB128<size_t>>();
if (offset_or_error.is_error())
return with_eof_check(stream, ParseError::ExpectedIndex);
size_t offset = offset_or_error.release_value();
auto index_or_error = stream.read_value<u8>();
if (index_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto index = index_or_error.release_value();
resulting_instructions.append(Instruction { full_opcode, MemoryAndLaneArgument { { static_cast<u32>(align), static_cast<u32>(offset) }, index } });
break;
}
case Instructions::v128_const.value(): {
// op (literal:16)
auto value_or_error = stream.read_value<LittleEndian<u128>>();
if (value_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidImmediate);
resulting_instructions.append(Instruction { full_opcode, value_or_error.release_value() });
break;
}
case Instructions::i8x16_shuffle.value(): {
// op 16x(lane)
u8 lanes[16];
for (size_t i = 0; i < 16; ++i) {
auto value_or_error = stream.read_value<u8>();
if (value_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
lanes[i] = value_or_error.release_value();
}
resulting_instructions.append(Instruction { full_opcode, ShuffleArgument(lanes) });
break;
}
case Instructions::i8x16_extract_lane_s.value():
case Instructions::i8x16_extract_lane_u.value():
case Instructions::i8x16_replace_lane.value():
case Instructions::i16x8_extract_lane_s.value():
case Instructions::i16x8_extract_lane_u.value():
case Instructions::i16x8_replace_lane.value():
case Instructions::i32x4_extract_lane.value():
case Instructions::i32x4_replace_lane.value():
case Instructions::i64x2_extract_lane.value():
case Instructions::i64x2_replace_lane.value():
case Instructions::f32x4_extract_lane.value():
case Instructions::f32x4_replace_lane.value():
case Instructions::f64x2_extract_lane.value():
case Instructions::f64x2_replace_lane.value(): {
// op (lane)
auto lane_or_error = stream.read_value<u8>();
if (lane_or_error.is_error())
return with_eof_check(stream, ParseError::InvalidInput);
auto lane = lane_or_error.release_value();
resulting_instructions.append(Instruction { full_opcode, LaneIndex { lane } });
break;
}
case Instructions::i8x16_swizzle.value():
case Instructions::i8x16_splat.value():
case Instructions::i16x8_splat.value():
case Instructions::i32x4_splat.value():
case Instructions::i64x2_splat.value():
case Instructions::f32x4_splat.value():
case Instructions::f64x2_splat.value():
case Instructions::i8x16_eq.value():
case Instructions::i8x16_ne.value():
case Instructions::i8x16_lt_s.value():
case Instructions::i8x16_lt_u.value():
case Instructions::i8x16_gt_s.value():
case Instructions::i8x16_gt_u.value():
case Instructions::i8x16_le_s.value():
case Instructions::i8x16_le_u.value():
case Instructions::i8x16_ge_s.value():
case Instructions::i8x16_ge_u.value():
case Instructions::i16x8_eq.value():
case Instructions::i16x8_ne.value():
case Instructions::i16x8_lt_s.value():
case Instructions::i16x8_lt_u.value():
case Instructions::i16x8_gt_s.value():
case Instructions::i16x8_gt_u.value():
case Instructions::i16x8_le_s.value():
case Instructions::i16x8_le_u.value():
case Instructions::i16x8_ge_s.value():
case Instructions::i16x8_ge_u.value():
case Instructions::i32x4_eq.value():
case Instructions::i32x4_ne.value():
case Instructions::i32x4_lt_s.value():
case Instructions::i32x4_lt_u.value():
case Instructions::i32x4_gt_s.value():
case Instructions::i32x4_gt_u.value():
case Instructions::i32x4_le_s.value():
case Instructions::i32x4_le_u.value():
case Instructions::i32x4_ge_s.value():
case Instructions::i32x4_ge_u.value():
case Instructions::f32x4_eq.value():
case Instructions::f32x4_ne.value():
case Instructions::f32x4_lt.value():
case Instructions::f32x4_gt.value():
case Instructions::f32x4_le.value():
case Instructions::f32x4_ge.value():
case Instructions::f64x2_eq.value():
case Instructions::f64x2_ne.value():
case Instructions::f64x2_lt.value():
case Instructions::f64x2_gt.value():
case Instructions::f64x2_le.value():
case Instructions::f64x2_ge.value():
case Instructions::v128_not.value():
case Instructions::v128_and.value():
case Instructions::v128_andnot.value():
case Instructions::v128_or.value():
case Instructions::v128_xor.value():
case Instructions::v128_bitselect.value():
case Instructions::v128_any_true.value():
case Instructions::v128_load32_zero.value():
case Instructions::v128_load64_zero.value():
case Instructions::f32x4_demote_f64x2_zero.value():
case Instructions::f64x2_promote_low_f32x4.value():
case Instructions::i8x16_abs.value():
case Instructions::i8x16_neg.value():
case Instructions::i8x16_popcnt.value():
case Instructions::i8x16_all_true.value():
case Instructions::i8x16_bitmask.value():
case Instructions::i8x16_narrow_i16x8_s.value():
case Instructions::i8x16_narrow_i16x8_u.value():
case Instructions::f32x4_ceil.value():
case Instructions::f32x4_floor.value():
case Instructions::f32x4_trunc.value():
case Instructions::f32x4_nearest.value():
case Instructions::i8x16_shl.value():
case Instructions::i8x16_shr_s.value():
case Instructions::i8x16_shr_u.value():
case Instructions::i8x16_add.value():
case Instructions::i8x16_add_sat_s.value():
case Instructions::i8x16_add_sat_u.value():
case Instructions::i8x16_sub.value():
case Instructions::i8x16_sub_sat_s.value():
case Instructions::i8x16_sub_sat_u.value():
case Instructions::f64x2_ceil.value():
case Instructions::f64x2_floor.value():
case Instructions::i8x16_min_s.value():
case Instructions::i8x16_min_u.value():
case Instructions::i8x16_max_s.value():
case Instructions::i8x16_max_u.value():
case Instructions::f64x2_trunc.value():
case Instructions::i8x16_avgr_u.value():
case Instructions::i16x8_extadd_pairwise_i8x16_s.value():
case Instructions::i16x8_extadd_pairwise_i8x16_u.value():
case Instructions::i32x4_extadd_pairwise_i16x8_s.value():
case Instructions::i32x4_extadd_pairwise_i16x8_u.value():
case Instructions::i16x8_abs.value():
case Instructions::i16x8_neg.value():
case Instructions::i16x8_q15mulr_sat_s.value():
case Instructions::i16x8_all_true.value():
case Instructions::i16x8_bitmask.value():
case Instructions::i16x8_narrow_i32x4_s.value():
case Instructions::i16x8_narrow_i32x4_u.value():
case Instructions::i16x8_extend_low_i8x16_s.value():
case Instructions::i16x8_extend_high_i8x16_s.value():
case Instructions::i16x8_extend_low_i8x16_u.value():
case Instructions::i16x8_extend_high_i8x16_u.value():
case Instructions::i16x8_shl.value():
case Instructions::i16x8_shr_s.value():
case Instructions::i16x8_shr_u.value():
case Instructions::i16x8_add.value():
case Instructions::i16x8_add_sat_s.value():
case Instructions::i16x8_add_sat_u.value():
case Instructions::i16x8_sub.value():
case Instructions::i16x8_sub_sat_s.value():
case Instructions::i16x8_sub_sat_u.value():
case Instructions::f64x2_nearest.value():
case Instructions::i16x8_mul.value():
case Instructions::i16x8_min_s.value():
case Instructions::i16x8_min_u.value():
case Instructions::i16x8_max_s.value():
case Instructions::i16x8_max_u.value():
case Instructions::i16x8_avgr_u.value():
case Instructions::i16x8_extmul_low_i8x16_s.value():
case Instructions::i16x8_extmul_high_i8x16_s.value():
case Instructions::i16x8_extmul_low_i8x16_u.value():
case Instructions::i16x8_extmul_high_i8x16_u.value():
case Instructions::i32x4_abs.value():
case Instructions::i32x4_neg.value():
case Instructions::i32x4_all_true.value():
case Instructions::i32x4_bitmask.value():
case Instructions::i32x4_extend_low_i16x8_s.value():
case Instructions::i32x4_extend_high_i16x8_s.value():
case Instructions::i32x4_extend_low_i16x8_u.value():
case Instructions::i32x4_extend_high_i16x8_u.value():
case Instructions::i32x4_shl.value():
case Instructions::i32x4_shr_s.value():
case Instructions::i32x4_shr_u.value():
case Instructions::i32x4_add.value():
case Instructions::i32x4_sub.value():
case Instructions::i32x4_mul.value():
case Instructions::i32x4_min_s.value():
case Instructions::i32x4_min_u.value():
case Instructions::i32x4_max_s.value():
case Instructions::i32x4_max_u.value():
case Instructions::i32x4_dot_i16x8_s.value():
case Instructions::i32x4_extmul_low_i16x8_s.value():
case Instructions::i32x4_extmul_high_i16x8_s.value():
case Instructions::i32x4_extmul_low_i16x8_u.value():
case Instructions::i32x4_extmul_high_i16x8_u.value():
case Instructions::i64x2_abs.value():
case Instructions::i64x2_neg.value():
case Instructions::i64x2_all_true.value():
case Instructions::i64x2_bitmask.value():
case Instructions::i64x2_extend_low_i32x4_s.value():
case Instructions::i64x2_extend_high_i32x4_s.value():
case Instructions::i64x2_extend_low_i32x4_u.value():
case Instructions::i64x2_extend_high_i32x4_u.value():
case Instructions::i64x2_shl.value():
case Instructions::i64x2_shr_s.value():
case Instructions::i64x2_shr_u.value():
case Instructions::i64x2_add.value():
case Instructions::i64x2_sub.value():
case Instructions::i64x2_mul.value():
case Instructions::i64x2_eq.value():
case Instructions::i64x2_ne.value():
case Instructions::i64x2_lt_s.value():
case Instructions::i64x2_gt_s.value():
case Instructions::i64x2_le_s.value():
case Instructions::i64x2_ge_s.value():
case Instructions::i64x2_extmul_low_i32x4_s.value():
case Instructions::i64x2_extmul_high_i32x4_s.value():
case Instructions::i64x2_extmul_low_i32x4_u.value():
case Instructions::i64x2_extmul_high_i32x4_u.value():
case Instructions::f32x4_abs.value():
case Instructions::f32x4_neg.value():
case Instructions::f32x4_sqrt.value():
case Instructions::f32x4_add.value():
case Instructions::f32x4_sub.value():
case Instructions::f32x4_mul.value():
case Instructions::f32x4_div.value():
case Instructions::f32x4_min.value():
case Instructions::f32x4_max.value():
case Instructions::f32x4_pmin.value():
case Instructions::f32x4_pmax.value():
case Instructions::f64x2_abs.value():
case Instructions::f64x2_neg.value():
case Instructions::f64x2_sqrt.value():
case Instructions::f64x2_add.value():
case Instructions::f64x2_sub.value():
case Instructions::f64x2_mul.value():
case Instructions::f64x2_div.value():
case Instructions::f64x2_min.value():
case Instructions::f64x2_max.value():
case Instructions::f64x2_pmin.value():
case Instructions::f64x2_pmax.value():
case Instructions::i32x4_trunc_sat_f32x4_s.value():
case Instructions::i32x4_trunc_sat_f32x4_u.value():
case Instructions::f32x4_convert_i32x4_s.value():
case Instructions::f32x4_convert_i32x4_u.value():
case Instructions::i32x4_trunc_sat_f64x2_s_zero.value():
case Instructions::i32x4_trunc_sat_f64x2_u_zero.value():
case Instructions::f64x2_convert_low_i32x4_s.value():
case Instructions::f64x2_convert_low_i32x4_u.value():
// op
resulting_instructions.append(Instruction { full_opcode });
break;
default:
return ParseError::UnknownInstruction;
}