/* * Copyright (c) 2021-2025, Ali Mohammad Pur * Copyright (c) 2023, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace AK::SIMD; // ASAN allocates frames for all the not-explicitly-tail-called functions, // which blows out the stack. So disable direct threading when ASAN is enabled. // We use the explicit annotation on clang, so allow it there. #if defined(HAS_ADDRESS_SANITIZER) && !defined(AK_COMPILER_CLANG) constexpr static auto should_try_to_use_direct_threading = false; #else constexpr static auto should_try_to_use_direct_threading = true; #endif namespace Wasm { template struct ConvertToRaw { T operator()(T value) { return LittleEndian(value); } }; template<> struct ConvertToRaw { u32 operator()(float value) const { return bit_cast>(value); } }; template<> struct ConvertToRaw { u64 operator()(double value) const { return bit_cast>(value); } }; #define TRAP_IF_NOT(x, ...) \ do { \ if (trap_if_not(x, #x##sv __VA_OPT__(, ) __VA_ARGS__)) { \ dbgln_if(WASM_TRACE_DEBUG, "Trapped because {} failed, at line {}", #x, __LINE__); \ return true; \ } \ } while (false) #define TRAP_IN_LOOP_IF_NOT(x, ...) \ do { \ if (interpreter.trap_if_not(x, #x##sv __VA_OPT__(, ) __VA_ARGS__)) { \ dbgln_if(WASM_TRACE_DEBUG, "Trapped because {} failed, at line {}", #x, __LINE__); \ return Outcome::Return; \ } \ } while (false) void BytecodeInterpreter::interpret(Configuration& configuration) { m_trap = Empty {}; auto& expression = configuration.frame().expression(); auto const should_limit_instruction_count = configuration.should_limit_instruction_count(); if (!expression.compiled_instructions.dispatches.is_empty()) { if (expression.compiled_instructions.direct) { if (should_limit_instruction_count) return interpret_impl(configuration, expression); return interpret_impl(configuration, expression); } return interpret_impl(configuration, expression); } if (should_limit_instruction_count) return interpret_impl(configuration, expression); return interpret_impl(configuration, expression); } enum class Outcome : u64 { // 0..Constants::max_allowed_executed_instructions_per_call -> next IP. Continue = Constants::max_allowed_executed_instructions_per_call + 1, Return, }; template struct InstructionHandler { }; #define HANDLER_PARAMS(S) \ S(BytecodeInterpreter&, interpreter), \ S(Configuration&, configuration), \ S(Instruction const*, instruction), \ S(SourcesAndDestination, addresses), \ S(u64, current_ip_value), \ S(Dispatch const*, cc) #define DECOMPOSE_PARAMS(t, n) [[maybe_unused]] t n #define DECOMPOSE_PARAMS_NAME_ONLY(t, n) n #define DECOMPOSE_PARAMS_TYPE_ONLY(t, ...) t #define HANDLE_INSTRUCTION(name, ...) \ template<> \ struct InstructionHandler { \ template \ static Outcome operator()(HANDLER_PARAMS(DECOMPOSE_PARAMS)); \ }; \ template \ Outcome InstructionHandler::operator()(HANDLER_PARAMS(DECOMPOSE_PARAMS)) #ifdef AK_COMPILER_CLANG # define TAILCALL [[clang::musttail]] #else # define TAILCALL #endif struct Continue { static Outcome operator()(BytecodeInterpreter& interpreter, Configuration& configuration, Instruction const*, SourcesAndDestination addresses, u64 current_ip_value, Dispatch const* cc) { current_ip_value++; addresses.sources_and_destination = cc[current_ip_value].sources_and_destination; auto const instruction = cc[current_ip_value].instruction; auto const handler = bit_cast(cc[current_ip_value].handler_ptr); TAILCALL return handler(interpreter, configuration, instruction, addresses, current_ip_value, cc); } }; struct Skip { static Outcome operator()(BytecodeInterpreter&, Configuration&, Instruction const*, SourcesAndDestination, u64 ip, Dispatch const*) { return bit_cast(ip); } }; #define continue_(...) Continue::operator()(__VA_ARGS__) HANDLE_INSTRUCTION(synthetic_end_expression) { return Outcome::Return; } HANDLE_INSTRUCTION(f64_reinterpret_i64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_extend8_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_extend16_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_extend8_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_extend16_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_extend32_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_trunc_sat_f32_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_trunc_sat_f32_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_trunc_sat_f64_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_trunc_sat_f64_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_trunc_sat_f32_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_trunc_sat_f32_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_trunc_sat_f64_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_trunc_sat_f64_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_const) { configuration.push_to_destination(Value(instruction->arguments().get()), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load8x8_s) { if (interpreter.load_and_push_mxn<8, 8, MakeSigned>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load8x8_u) { if (interpreter.load_and_push_mxn<8, 8, MakeUnsigned>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load16x4_s) { if (interpreter.load_and_push_mxn<16, 4, MakeSigned>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load16x4_u) { if (interpreter.load_and_push_mxn<16, 4, MakeUnsigned>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load32x2_s) { if (interpreter.load_and_push_mxn<32, 2, MakeSigned>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load32x2_u) { if (interpreter.load_and_push_mxn<32, 2, MakeUnsigned>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load8_splat) { if (interpreter.load_and_push_m_splat<8>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load16_splat) { if (interpreter.load_and_push_m_splat<16>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load32_splat) { if (interpreter.load_and_push_m_splat<32>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load64_splat) { if (interpreter.load_and_push_m_splat<64>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_splat) { interpreter.pop_and_push_m_splat<8, NativeIntegralType>(configuration, *instruction, addresses); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_splat) { interpreter.pop_and_push_m_splat<16, NativeIntegralType>(configuration, *instruction, addresses); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_splat) { interpreter.pop_and_push_m_splat<32, NativeIntegralType>(configuration, *instruction, addresses); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_splat) { interpreter.pop_and_push_m_splat<64, NativeIntegralType>(configuration, *instruction, addresses); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_splat) { interpreter.pop_and_push_m_splat<32, NativeFloatingType>(configuration, *instruction, addresses); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_splat) { interpreter.pop_and_push_m_splat<64, NativeFloatingType>(configuration, *instruction, addresses); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_shuffle) { auto& arg = instruction->arguments().get(); auto b = interpreter.pop_vector(configuration, 0, addresses); auto a = interpreter.pop_vector(configuration, 1, addresses); using VectorType = Native128ByteVectorOf; VectorType result; for (size_t i = 0; i < 16; ++i) if (arg.lanes[i] < 16) result[i] = a[arg.lanes[i]]; else result[i] = b[arg.lanes[i] - 16]; configuration.push_to_destination(Value(bit_cast(result)), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_store) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_ge) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_clz) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_ctz) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_popcnt) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_add) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_sub) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_mul) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_divs) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_divu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_rems) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_remu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_and) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_or) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_xor) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_shl) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_shrs) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_shru) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_rotl) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_rotr) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_clz) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_ctz) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_popcnt) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_add) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_sub) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_mul) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_divs) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_divu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_rems) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_remu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_and) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_or) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_xor) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_shl) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_shrs) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_shru) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_rotl) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_rotr) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_abs) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_neg) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_ceil) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_floor) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_trunc) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_nearest) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_sqrt) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_add) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_sub) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_mul) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_div) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_min) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_max) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_copysign) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_abs) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_neg) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_ceil) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_floor) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_trunc) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_nearest) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_sqrt) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_add) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_sub) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_mul) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_div) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_min) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_max) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_copysign) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_wrap_i64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_trunc_sf32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_trunc_uf32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_trunc_sf64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_trunc_uf64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_trunc_sf32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_trunc_uf32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_trunc_sf64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_trunc_uf64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_extend_si32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_extend_ui32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_convert_si32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_convert_ui32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_convert_si64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_convert_ui64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_demote_f64) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_convert_si32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_convert_ui32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_convert_si64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_convert_ui64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_promote_f32) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_reinterpret_f32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_reinterpret_f64) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_reinterpret_i32) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(local_get) { configuration.push_to_destination(configuration.local(instruction->local_index()), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_const) { configuration.push_to_destination(Value(instruction->arguments().unsafe_get()), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_i32_add2local) { configuration.push_to_destination(Value(static_cast(Operators::Add {}(configuration.local(instruction->local_index()).to(), configuration.local(instruction->arguments().get()).to()))), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_i32_addconstlocal) { configuration.push_to_destination(Value(static_cast(Operators::Add {}(configuration.local(instruction->local_index()).to(), instruction->arguments().unsafe_get()))), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_i32_andconstlocal) { configuration.push_to_destination(Value(Operators::BitAnd {}(configuration.local(instruction->local_index()).to(), instruction->arguments().unsafe_get())), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_i32_storelocal) { if (interpreter.store_value(configuration, *instruction, ConvertToRaw {}(configuration.local(instruction->local_index()).to()), 0, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_i64_storelocal) { if (interpreter.store_value(configuration, *instruction, ConvertToRaw {}(configuration.local(instruction->local_index()).to()), 0, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_local_seti32_const) { configuration.local(instruction->local_index()) = Value(instruction->arguments().unsafe_get()); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_call_00) { auto regs_copy = configuration.regs; auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; configuration.regs = regs_copy; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_call_01) { auto regs_copy = configuration.regs; auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; configuration.regs = regs_copy; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_call_10) { auto regs_copy = configuration.regs; auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; configuration.regs = regs_copy; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_call_11) { auto regs_copy = configuration.regs; auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; configuration.regs = regs_copy; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_call_20) { auto regs_copy = configuration.regs; auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; configuration.regs = regs_copy; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_call_21) { auto regs_copy = configuration.regs; auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; configuration.regs = regs_copy; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_call_30) { auto regs_copy = configuration.regs; auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; configuration.regs = regs_copy; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(synthetic_call_31) { auto regs_copy = configuration.regs; auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "[{}] call(#{} -> {})", current_ip_value, index.value(), address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; configuration.regs = regs_copy; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(unreachable) { interpreter.set_trap("Unreachable"sv); return Outcome::Return; } HANDLE_INSTRUCTION(nop) { TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(local_set) { // bounds checked by verifier. configuration.local(instruction->local_index()) = configuration.take_source(0, addresses.sources); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_const) { configuration.push_to_destination(Value(instruction->arguments().unsafe_get()), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_const) { configuration.push_to_destination(Value(instruction->arguments().unsafe_get()), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_const) { configuration.push_to_destination(Value(instruction->arguments().unsafe_get()), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(block) { size_t arity = 0; size_t param_arity = 0; auto& args = instruction->arguments().unsafe_get(); if (args.block_type.kind() != BlockType::Empty) [[unlikely]] { switch (args.block_type.kind()) { case BlockType::Type: arity = 1; break; case BlockType::Index: { auto& type = configuration.frame().module().types()[args.block_type.type_index().value()]; arity = type.results().size(); param_arity = type.parameters().size(); break; } case BlockType::Empty: VERIFY_NOT_REACHED(); } } configuration.label_stack().append(Label(arity, args.end_ip, configuration.value_stack().size() - param_arity)); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(loop) { auto& args = instruction->arguments().get(); size_t arity = 0; if (args.block_type.kind() == BlockType::Index) { auto& type = configuration.frame().module().types()[args.block_type.type_index().value()]; arity = type.parameters().size(); } configuration.label_stack().append(Label(arity, current_ip_value + 1, configuration.value_stack().size() - arity)); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(if_) { size_t arity = 0; size_t param_arity = 0; auto& args = instruction->arguments().unsafe_get(); switch (args.block_type.kind()) { case BlockType::Empty: break; case BlockType::Type: arity = 1; break; case BlockType::Index: { auto& type = configuration.frame().module().types()[args.block_type.type_index().value()]; arity = type.results().size(); param_arity = type.parameters().size(); } } auto value = configuration.take_source(0, addresses.sources).to(); auto end_label = Label(arity, args.end_ip.value(), configuration.value_stack().size() - param_arity); if (value == 0) { if (args.else_ip.has_value()) { current_ip_value = args.else_ip->value() - 1; configuration.label_stack().append(end_label); } else { current_ip_value = args.end_ip.value(); } } else { configuration.label_stack().append(end_label); } TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(structured_end) { configuration.label_stack().take_last(); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(structured_else) { auto label = configuration.label_stack().take_last(); // Jump to the end label current_ip_value = label.continuation().value() - 1; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(return_) { configuration.label_stack().shrink(configuration.frame().label_index() + 1, true); return Outcome::Return; } HANDLE_INSTRUCTION(br) { current_ip_value = interpreter.branch_to_label(configuration, instruction->arguments().get()).value(); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(br_if) { // bounds checked by verifier. auto cond = configuration.take_source(0, addresses.sources).to(); if (cond == 0) TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); current_ip_value = interpreter.branch_to_label(configuration, instruction->arguments().get()).value(); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(br_table) { auto& args = instruction->arguments().get(); auto i = configuration.take_source(0, addresses.sources).to(); if (i >= args.labels.size()) { current_ip_value = interpreter.branch_to_label(configuration, args.default_).value(); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } current_ip_value = interpreter.branch_to_label(configuration, args.labels[i]).value(); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(call) { auto index = instruction->arguments().get(); auto address = configuration.frame().module().functions()[index.value()]; dbgln_if(WASM_TRACE_DEBUG, "call({})", address.value()); if (interpreter.call_address(configuration, address)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(call_indirect) { auto& args = instruction->arguments().get(); auto table_address = configuration.frame().module().tables()[args.table.value()]; auto table_instance = configuration.store().get(table_address); // bounds checked by verifier. auto index = configuration.take_source(0, addresses.sources).to(); TRAP_IN_LOOP_IF_NOT(index >= 0); TRAP_IN_LOOP_IF_NOT(static_cast(index) < table_instance->elements().size()); auto& element = table_instance->elements()[index]; TRAP_IN_LOOP_IF_NOT(element.ref().has()); auto address = element.ref().get().address; auto const& type_actual = configuration.store().get(address)->visit([](auto& f) -> decltype(auto) { return f.type(); }); auto const& type_expected = configuration.frame().module().types()[args.type.value()]; TRAP_IN_LOOP_IF_NOT(type_actual.parameters().size() == type_expected.parameters().size()); TRAP_IN_LOOP_IF_NOT(type_actual.results().size() == type_expected.results().size()); TRAP_IN_LOOP_IF_NOT(type_actual.parameters() == type_expected.parameters()); TRAP_IN_LOOP_IF_NOT(type_actual.results() == type_expected.results()); dbgln_if(WASM_TRACE_DEBUG, "call_indirect({} -> {})", index, address.value()); if (interpreter.call_address(configuration, address, BytecodeInterpreter::CallAddressSource::IndirectCall)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_load) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_load) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_load) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_load) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_load8_s) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_load8_u) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_load16_s) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_load16_u) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_load8_s) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_load8_u) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_load16_s) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_load16_u) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_load32_s) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_load32_u) { if (interpreter.load_and_push(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_store) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_store) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_store) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_store) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_store8) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_store16) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_store8) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_store16) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_store32) { if (interpreter.pop_and_store(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(local_tee) { auto value = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto local_index = instruction->local_index(); dbgln_if(WASM_TRACE_DEBUG, "stack:peek -> locals({})", local_index.value()); configuration.frame().locals()[local_index.value()] = value; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(global_get) { auto global_index = instruction->arguments().get(); // This check here is for const expressions. In non-const expressions, // a validation error would have been thrown. TRAP_IN_LOOP_IF_NOT(global_index < configuration.frame().module().globals().size()); auto address = configuration.frame().module().globals()[global_index.value()]; dbgln_if(WASM_TRACE_DEBUG, "global({}) -> stack", address.value()); auto global = configuration.store().get(address); configuration.push_to_destination(global->value(), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(global_set) { auto global_index = instruction->arguments().get(); auto address = configuration.frame().module().globals()[global_index.value()]; // bounds checked by verifier. auto value = configuration.take_source(0, addresses.sources); dbgln_if(WASM_TRACE_DEBUG, "stack -> global({})", address.value()); auto global = configuration.store().get(address); global->set_value(value); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(memory_size) { auto& args = instruction->arguments().get(); auto address = configuration.frame().module().memories().data()[args.memory_index.value()]; auto instance = configuration.store().get(address); auto pages = instance->size() / Constants::page_size; dbgln_if(WASM_TRACE_DEBUG, "memory.size -> stack({})", pages); configuration.push_to_destination(Value(static_cast(pages)), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(memory_grow) { auto& args = instruction->arguments().get(); auto address = configuration.frame().module().memories().data()[args.memory_index.value()]; auto instance = configuration.store().get(address); i32 old_pages = instance->size() / Constants::page_size; auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto new_pages = entry.to(); dbgln_if(WASM_TRACE_DEBUG, "memory.grow({}), previously {} pages...", new_pages, old_pages); if (instance->grow(new_pages * Constants::page_size)) entry = Value(old_pages); else entry = Value(-1); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(memory_fill) { auto& args = instruction->arguments().get(); auto address = configuration.frame().module().memories().data()[args.memory_index.value()]; auto instance = configuration.store().get(address); // bounds checked by verifier. auto count = configuration.take_source(0, addresses.sources).to(); u8 value = static_cast(configuration.take_source(1, addresses.sources).to()); auto destination_offset = configuration.take_source(2, addresses.sources).to(); Checked checked_end = destination_offset; checked_end += count; TRAP_IN_LOOP_IF_NOT(!checked_end.has_overflow() && static_cast(checked_end.value()) <= instance->data().size()); if (count == 0) TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); Instruction::MemoryArgument memarg { 0, 0, args.memory_index }; for (u32 i = 0; i < count; ++i) { if (interpreter.store_to_memory(configuration, memarg, { &value, sizeof(value) }, destination_offset + i)) return Outcome::Return; } TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(memory_copy) { auto& args = instruction->arguments().get(); auto source_address = configuration.frame().module().memories().data()[args.src_index.value()]; auto destination_address = configuration.frame().module().memories().data()[args.dst_index.value()]; auto source_instance = configuration.store().get(source_address); auto destination_instance = configuration.store().get(destination_address); // bounds checked by verifier. auto count = configuration.take_source(0, addresses.sources).to(); auto source_offset = configuration.take_source(1, addresses.sources).to(); auto destination_offset = configuration.take_source(2, addresses.sources).to(); Checked source_position = source_offset; source_position.saturating_add(count); Checked destination_position = destination_offset; destination_position.saturating_add(count); TRAP_IN_LOOP_IF_NOT(source_position <= source_instance->data().size()); TRAP_IN_LOOP_IF_NOT(destination_position <= destination_instance->data().size()); if (count == 0) TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); Instruction::MemoryArgument memarg { 0, 0, args.dst_index }; if (destination_offset <= source_offset) { for (auto i = 0; i < count; ++i) { auto value = source_instance->data()[source_offset + i]; if (interpreter.store_to_memory(configuration, memarg, { &value, sizeof(value) }, destination_offset + i)) return Outcome::Return; } } else { for (auto i = count - 1; i >= 0; --i) { auto value = source_instance->data()[source_offset + i]; if (interpreter.store_to_memory(configuration, memarg, { &value, sizeof(value) }, destination_offset + i)) return Outcome::Return; } } TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(memory_init) { auto& args = instruction->arguments().get(); auto& data_address = configuration.frame().module().datas()[args.data_index.value()]; auto& data = *configuration.store().get(data_address); auto memory_address = configuration.frame().module().memories().data()[args.memory_index.value()]; auto memory = configuration.store().unsafe_get(memory_address); // bounds checked by verifier. auto count = configuration.take_source(0, addresses.sources).to(); auto source_offset = configuration.take_source(1, addresses.sources).to(); auto destination_offset = configuration.take_source(2, addresses.sources).to(); Checked source_position = source_offset; source_position.saturating_add(count); Checked destination_position = destination_offset; destination_position.saturating_add(count); TRAP_IN_LOOP_IF_NOT(source_position <= data.data().size()); TRAP_IN_LOOP_IF_NOT(destination_position <= memory->data().size()); if (count == 0) TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); Instruction::MemoryArgument memarg { 0, 0, args.memory_index }; for (size_t i = 0; i < (size_t)count; ++i) { auto value = data.data()[source_offset + i]; if (interpreter.store_to_memory(configuration, memarg, { &value, sizeof(value) }, destination_offset + i)) return Outcome::Return; } TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(data_drop) { auto data_index = instruction->arguments().get(); auto data_address = configuration.frame().module().datas()[data_index.value()]; *configuration.store().get(data_address) = DataInstance({}); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(elem_drop) { auto elem_index = instruction->arguments().get(); auto address = configuration.frame().module().elements()[elem_index.value()]; auto elem = configuration.store().get(address); *configuration.store().get(address) = ElementInstance(elem->type(), {}); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(table_init) { auto& args = instruction->arguments().get(); auto table_address = configuration.frame().module().tables()[args.table_index.value()]; auto table = configuration.store().get(table_address); auto element_address = configuration.frame().module().elements()[args.element_index.value()]; auto element = configuration.store().get(element_address); // bounds checked by verifier. auto count = configuration.take_source(0, addresses.sources).to(); auto source_offset = configuration.take_source(1, addresses.sources).to(); auto destination_offset = configuration.take_source(2, addresses.sources).to(); Checked checked_source_offset = source_offset; Checked checked_destination_offset = destination_offset; checked_source_offset += count; checked_destination_offset += count; TRAP_IN_LOOP_IF_NOT(!checked_source_offset.has_overflow() && checked_source_offset <= (u32)element->references().size()); TRAP_IN_LOOP_IF_NOT(!checked_destination_offset.has_overflow() && checked_destination_offset <= (u32)table->elements().size()); for (u32 i = 0; i < count; ++i) table->elements()[destination_offset + i] = element->references()[source_offset + i]; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(table_copy) { auto& args = instruction->arguments().get(); auto source_address = configuration.frame().module().tables()[args.rhs.value()]; auto destination_address = configuration.frame().module().tables()[args.lhs.value()]; auto source_instance = configuration.store().get(source_address); auto destination_instance = configuration.store().get(destination_address); // bounds checked by verifier. auto count = configuration.take_source(0, addresses.sources).to(); auto source_offset = configuration.take_source(1, addresses.sources).to(); auto destination_offset = configuration.take_source(2, addresses.sources).to(); Checked source_position = source_offset; source_position.saturating_add(count); Checked destination_position = destination_offset; destination_position.saturating_add(count); TRAP_IN_LOOP_IF_NOT(source_position <= source_instance->elements().size()); TRAP_IN_LOOP_IF_NOT(destination_position <= destination_instance->elements().size()); if (count == 0) TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); if (destination_offset <= source_offset) { for (u32 i = 0; i < count; ++i) { auto value = source_instance->elements()[source_offset + i]; destination_instance->elements()[destination_offset + i] = value; } } else { for (u32 i = count - 1; i != NumericLimits::max(); --i) { auto value = source_instance->elements()[source_offset + i]; destination_instance->elements()[destination_offset + i] = value; } } TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(table_fill) { auto table_index = instruction->arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); // bounds checked by verifier. auto count = configuration.take_source(0, addresses.sources).to(); auto value = configuration.take_source(1, addresses.sources); auto start = configuration.take_source(2, addresses.sources).to(); Checked checked_offset = start; checked_offset += count; TRAP_IN_LOOP_IF_NOT(!checked_offset.has_overflow() && checked_offset <= (u32)table->elements().size()); for (u32 i = 0; i < count; ++i) table->elements()[start + i] = value.to(); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(table_set) { // bounds checked by verifier. auto ref = configuration.take_source(0, addresses.sources); auto index = (size_t)(configuration.take_source(1, addresses.sources).to()); auto table_index = instruction->arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); TRAP_IN_LOOP_IF_NOT(index < table->elements().size()); table->elements()[index] = ref.to(); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(table_get) { // bounds checked by verifier. auto& index_value = configuration.source_value(0, addresses.sources); auto index = static_cast(index_value.to()); auto table_index = instruction->arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); TRAP_IN_LOOP_IF_NOT(index < table->elements().size()); index_value = Value(table->elements()[index]); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(table_grow) { // bounds checked by verifier. auto size = configuration.take_source(0, addresses.sources).to(); auto fill_value = configuration.take_source(1, addresses.sources); auto table_index = instruction->arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); auto previous_size = table->elements().size(); auto did_grow = table->grow(size, fill_value.to()); if (!did_grow) { configuration.push_to_destination(Value(-1), addresses.destination); } else { configuration.push_to_destination(Value(static_cast(previous_size)), addresses.destination); } TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(table_size) { auto table_index = instruction->arguments().get(); auto address = configuration.frame().module().tables()[table_index.value()]; auto table = configuration.store().get(address); configuration.push_to_destination(Value(static_cast(table->elements().size())), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(ref_null) { auto type = instruction->arguments().get(); configuration.push_to_destination(Value(Reference(Reference::Null { type })), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(ref_func) { auto index = instruction->arguments().get().value(); auto& functions = configuration.frame().module().functions(); auto address = functions[index]; configuration.push_to_destination(Value(Reference { Reference::Func { address, configuration.store().get_module_for(address) } }), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(ref_is_null) { // bounds checked by verifier. auto ref = configuration.take_source(0, addresses.sources); configuration.push_to_destination(Value(static_cast(ref.to().ref().has() ? 1 : 0)), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(drop) { // bounds checked by verifier. configuration.take_source(0, addresses.sources); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(select) { // Note: The type seems to only be used for validation. auto value = configuration.take_source(0, addresses.sources).to(); // bounds checked by verifier. dbgln_if(WASM_TRACE_DEBUG, "select({})", value); auto rhs = configuration.take_source(1, addresses.sources); auto& lhs = configuration.source_value(2, addresses.sources); // bounds checked by verifier. lhs = value != 0 ? lhs : rhs; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(select_typed) { // Note: The type seems to only be used for validation. auto value = configuration.take_source(0, addresses.sources).to(); // bounds checked by verifier. dbgln_if(WASM_TRACE_DEBUG, "select({})", value); auto rhs = configuration.take_source(1, addresses.sources); auto& lhs = configuration.source_value(2, addresses.sources); // bounds checked by verifier. lhs = value != 0 ? lhs : rhs; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_eqz) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_eq) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_ne) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_lts) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_ltu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_gts) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_gtu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_les) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_leu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_ges) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32_geu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_eqz) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_eq) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_ne) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_lts) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_ltu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_gts) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_gtu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_les) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_leu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_ges) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64_geu) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_eq) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_ne) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_lt) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_gt) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_le) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32_ge) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_eq) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_ne) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_lt) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_gt) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64_le) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extmul_high_i16x8_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extmul_low_i16x8_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_eq) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_ne) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_lt_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_gt_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_le_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_ge_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_abs) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_neg) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_all_true) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_add) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_sub) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_mul) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extend_low_i32x4_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extend_high_i32x4_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extend_low_i32x4_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extend_high_i32x4_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extmul_low_i32x4_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extmul_high_i32x4_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extmul_low_i32x4_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extmul_high_i32x4_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_eq) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_ne) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_lt) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_gt) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_le) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_ge) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_min) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_max) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_eq) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_ne) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_lt) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_gt) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_le) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_ge) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_min) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_max) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_div) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_mul) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_sub) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_add) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_pmin) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_pmax) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_div) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_mul) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_sub) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_add) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_pmin) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_pmax) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_ceil) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_floor) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_trunc) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_nearest) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_sqrt) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_neg) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_abs) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_ceil) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_floor) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_trunc) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_nearest) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_sqrt) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_neg) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_abs) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_and) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_or) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_xor) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_not) { if (interpreter.unary_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_andnot) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_bitselect) { // bounds checked by verifier. auto mask = configuration.take_source(0, addresses.sources).to(); auto false_vector = configuration.take_source(1, addresses.sources).to(); auto true_vector = configuration.take_source(2, addresses.sources).to(); u128 result = (true_vector & mask) | (false_vector & ~mask); configuration.push_to_destination(Value(result), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_any_true) { auto vector = configuration.take_source(0, addresses.sources).to(); // bounds checked by verifier. configuration.push_to_destination(Value(static_cast(vector != 0)), addresses.destination); TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load8_lane) { if (interpreter.load_and_push_lane_n<8>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load16_lane) { if (interpreter.load_and_push_lane_n<16>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load32_lane) { if (interpreter.load_and_push_lane_n<32>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load64_lane) { if (interpreter.load_and_push_lane_n<64>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load32_zero) { if (interpreter.load_and_push_zero_n<32>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_load64_zero) { if (interpreter.load_and_push_zero_n<64>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_store8_lane) { if (interpreter.pop_and_store_lane_n<8>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_store16_lane) { if (interpreter.pop_and_store_lane_n<16>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_store32_lane) { if (interpreter.pop_and_store_lane_n<32>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(v128_store64_lane) { if (interpreter.pop_and_store_lane_n<64>(configuration, *instruction, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_trunc_sat_f32x4_s) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_trunc_sat_f32x4_u) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_bitmask) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_bitmask) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_bitmask) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_bitmask) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_dot_i16x8_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_narrow_i16x8_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_narrow_i16x8_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_narrow_i32x4_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_narrow_i32x4_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_q15mulr_sat_s) { if (interpreter.binary_numeric_operation, MakeSigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_convert_i32x4_s) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_convert_i32x4_u) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_convert_low_i32x4_s) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_convert_low_i32x4_u) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_demote_f64x2_zero) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_promote_low_f32x4) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_trunc_sat_f64x2_s_zero) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_trunc_sat_f64x2_u_zero) { if (interpreter.unary_operation>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_shl) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_shr_u) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_shr_s) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_shl) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_shr_u) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_shr_s) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_shl) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_shr_u) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_shr_s) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_shl) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_shr_u) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_shr_s) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_swizzle) { if (interpreter.binary_numeric_operation(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_extract_lane_s) { if (interpreter.unary_operation>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_extract_lane_u) { if (interpreter.unary_operation>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extract_lane_s) { if (interpreter.unary_operation>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extract_lane_u) { if (interpreter.unary_operation>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extract_lane) { if (interpreter.unary_operation>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_extract_lane) { if (interpreter.unary_operation>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_extract_lane) { if (interpreter.unary_operation>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_extract_lane) { if (interpreter.unary_operation>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_replace_lane) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_replace_lane) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_replace_lane) { if (interpreter.binary_numeric_operation, i32>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i64x2_replace_lane) { if (interpreter.binary_numeric_operation, i64>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_replace_lane) { if (interpreter.binary_numeric_operation, float>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_replace_lane) { if (interpreter.binary_numeric_operation, double>(configuration, addresses, instruction->arguments().get().lane)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_eq) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_ne) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_lt_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_lt_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_gt_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_gt_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_le_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_le_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_ge_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_ge_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_abs) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_neg) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_all_true) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_popcnt) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_add) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_sub) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_avgr_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_add_sat_s) { if (interpreter.binary_numeric_operation, MakeSigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_add_sat_u) { if (interpreter.binary_numeric_operation, MakeUnsigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_sub_sat_s) { if (interpreter.binary_numeric_operation, MakeSigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_sub_sat_u) { if (interpreter.binary_numeric_operation, MakeUnsigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_min_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_min_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_max_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i8x16_max_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_eq) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_ne) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_lt_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_lt_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_gt_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_gt_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_le_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_le_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_ge_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_ge_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_abs) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_neg) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_all_true) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_add) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_sub) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_mul) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_avgr_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_add_sat_s) { if (interpreter.binary_numeric_operation, MakeSigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_add_sat_u) { if (interpreter.binary_numeric_operation, MakeUnsigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_sub_sat_s) { if (interpreter.binary_numeric_operation, MakeSigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_sub_sat_u) { if (interpreter.binary_numeric_operation, MakeUnsigned>>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_min_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_min_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_max_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_max_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extend_low_i8x16_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extend_high_i8x16_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extend_low_i8x16_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extend_high_i8x16_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extadd_pairwise_i8x16_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extadd_pairwise_i8x16_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extmul_low_i8x16_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extmul_high_i8x16_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extmul_low_i8x16_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i16x8_extmul_high_i8x16_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_eq) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_ne) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_lt_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_lt_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_gt_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_gt_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_le_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_le_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_ge_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_ge_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_abs) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_neg) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_all_true) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_add) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_sub) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_mul) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_min_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_min_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_max_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_max_u) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extend_low_i16x8_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extend_high_i16x8_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extend_low_i16x8_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extend_high_i16x8_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extadd_pairwise_i16x8_s) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extadd_pairwise_i16x8_u) { if (interpreter.unary_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extmul_low_i16x8_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(i32x4_extmul_high_i16x8_s) { if (interpreter.binary_numeric_operation>(configuration, addresses)) return Outcome::Return; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } template constexpr static auto handle_instruction(Args&&... a) { return InstructionHandler::template operator()(forward(a)...); } template FLATTEN void BytecodeInterpreter::interpret_impl(Configuration& configuration, Expression const& expression) { auto& instructions = expression.instructions(); auto current_ip_value = configuration.ip(); u64 executed_instructions = 0; constexpr static u32 default_sources_and_destination = (to_underlying(Dispatch::RegisterOrStack::Stack) | (to_underlying(Dispatch::RegisterOrStack::Stack) << 2) | (to_underlying(Dispatch::RegisterOrStack::Stack) << 4)); SourcesAndDestination addresses { .sources_and_destination = default_sources_and_destination }; auto const cc = expression.compiled_instructions.dispatches.data(); if constexpr (HaveDirectThreadingInfo) { static_assert(HasCompiledList, "Direct threading requires a compiled instruction list"); addresses.sources_and_destination = cc[current_ip_value].sources_and_destination; auto const instruction = cc[current_ip_value].instruction; auto const handler = bit_cast(cc[current_ip_value].handler_ptr); handler(*this, configuration, instruction, addresses, current_ip_value, cc); return; } while (true) { if constexpr (HasDynamicInsnLimit) { if (executed_instructions++ >= Constants::max_allowed_executed_instructions_per_call) [[unlikely]] { m_trap = Trap::from_string("Exceeded maximum allowed number of instructions"); return; } } // bounds checked by loop condition. addresses.sources_and_destination = HasCompiledList ? cc[current_ip_value].sources_and_destination : default_sources_and_destination; auto const instruction = HasCompiledList ? cc[current_ip_value].instruction : &instructions.data()[current_ip_value]; auto const opcode = (HasCompiledList && !HaveDirectThreadingInfo ? cc[current_ip_value].instruction_opcode : instruction->opcode()) .value(); #define RUN_NEXT_INSTRUCTION() \ { \ ++current_ip_value; \ break; \ } #define HANDLE_INSTRUCTION_NEW(name, ...) \ case Instructions::name.value(): { \ auto outcome = handle_instruction(*this, configuration, instruction, addresses, current_ip_value, cc); \ if (outcome == Outcome::Return) \ return; \ current_ip_value = to_underlying(outcome); \ RUN_NEXT_INSTRUCTION(); \ } dbgln_if(WASM_TRACE_DEBUG, "Executing instruction {} at current_ip_value {}", instruction_name(instruction->opcode()), current_ip_value); if ((opcode & Instructions::SyntheticInstructionBase.value()) != Instructions::SyntheticInstructionBase.value()) __builtin_prefetch(&instruction->arguments(), /* read */ 0, /* low temporal locality */ 1); switch (opcode) { ENUMERATE_WASM_OPCODES(HANDLE_INSTRUCTION_NEW) default: dbgln("Bad opcode {} in insn {} (ip {})", opcode, instruction_name(instruction->opcode()), current_ip_value); VERIFY_NOT_REACHED(); } } } InstructionPointer BytecodeInterpreter::branch_to_label(Configuration& configuration, LabelIndex index) { dbgln_if(WASM_TRACE_DEBUG, "Branch to label with index {}...", index.value()); auto& label_stack = configuration.label_stack(); label_stack.shrink(label_stack.size() - index.value(), true); auto label = configuration.label_stack().last(); dbgln_if(WASM_TRACE_DEBUG, "...which is actually IP {}, and has {} result(s)", label.continuation().value(), label.arity()); configuration.value_stack().remove(label.stack_height(), configuration.value_stack().size() - label.stack_height() - label.arity()); return label.continuation().value() - 1; } template bool BytecodeInterpreter::load_and_push(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { auto& arg = instruction.arguments().get(); auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; auto memory = configuration.store().get(address); auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto base = entry.to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; if (instance_address + sizeof(ReadType) > memory->size()) { m_trap = Trap::from_string("Memory access out of bounds"); dbgln_if(WASM_TRACE_DEBUG, "LibWasm: Memory access out of bounds (expected {} to be less than or equal to {})", instance_address + sizeof(ReadType), memory->size()); return true; } dbgln_if(WASM_TRACE_DEBUG, "load({} : {}) -> stack", instance_address, sizeof(ReadType)); auto slice = memory->data().bytes().slice(instance_address, sizeof(ReadType)); entry = Value(static_cast(read_value(slice))); return false; } template ALWAYS_INLINE static TDst convert_vector(TSrc v) { return __builtin_convertvector(v, TDst); } template typename SetSign> bool BytecodeInterpreter::load_and_push_mxn(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { auto& arg = instruction.arguments().get(); auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; auto memory = configuration.store().get(address); auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto base = entry.to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; if (instance_address + M * N / 8 > memory->size()) { m_trap = Trap::from_string("Memory access out of bounds"); dbgln_if(WASM_TRACE_DEBUG, "LibWasm: Memory access out of bounds (expected {} to be less than or equal to {})", instance_address + M * N / 8, memory->size()); return true; } dbgln_if(WASM_TRACE_DEBUG, "vec-load({} : {}) -> stack", instance_address, M * N / 8); auto slice = memory->data().bytes().slice(instance_address, M * N / 8); using V64 = NativeVectorType; using V128 = NativeVectorType; V64 bytes { 0 }; if (bit_cast(slice.data()) % sizeof(V64) == 0) bytes = *bit_cast(slice.data()); else ByteReader::load(slice.data(), bytes); entry = Value(bit_cast(convert_vector(bytes))); return false; } template bool BytecodeInterpreter::load_and_push_lane_n(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { auto memarg_and_lane = instruction.arguments().get(); auto& address = configuration.frame().module().memories()[memarg_and_lane.memory.memory_index.value()]; auto memory = configuration.store().get(address); // bounds checked by verifier. auto vector = configuration.take_source(0, addresses.sources).to(); auto base = configuration.take_source(1, addresses.sources).to(); u64 instance_address = static_cast(bit_cast(base)) + memarg_and_lane.memory.offset; if (instance_address + N / 8 > memory->size()) { m_trap = Trap::from_string("Memory access out of bounds"); return true; } auto slice = memory->data().bytes().slice(instance_address, N / 8); auto dst = bit_cast(&vector) + memarg_and_lane.lane * N / 8; memcpy(dst, slice.data(), N / 8); configuration.push_to_destination(Value(vector), addresses.destination); return false; } template bool BytecodeInterpreter::load_and_push_zero_n(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { auto memarg_and_lane = instruction.arguments().get(); auto& address = configuration.frame().module().memories()[memarg_and_lane.memory_index.value()]; auto memory = configuration.store().get(address); // bounds checked by verifier. auto base = configuration.take_source(0, addresses.sources).to(); u64 instance_address = static_cast(bit_cast(base)) + memarg_and_lane.offset; if (instance_address + N / 8 > memory->size()) { m_trap = Trap::from_string("Memory access out of bounds"); return true; } auto slice = memory->data().bytes().slice(instance_address, N / 8); u128 vector = 0; memcpy(&vector, slice.data(), N / 8); configuration.push_to_destination(Value(vector), addresses.destination); return false; } template bool BytecodeInterpreter::load_and_push_m_splat(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { auto& arg = instruction.arguments().get(); auto& address = configuration.frame().module().memories()[arg.memory_index.value()]; auto memory = configuration.store().get(address); auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto base = entry.to(); u64 instance_address = static_cast(bit_cast(base)) + arg.offset; if (instance_address + M / 8 > memory->size()) { m_trap = Trap::from_string("Memory access out of bounds"); dbgln_if(WASM_TRACE_DEBUG, "LibWasm: Memory access out of bounds (expected {} to be less than or equal to {})", instance_address + M / 8, memory->size()); return true; } dbgln_if(WASM_TRACE_DEBUG, "vec-splat({} : {}) -> stack", instance_address, M / 8); auto slice = memory->data().bytes().slice(instance_address, M / 8); auto value = read_value>(slice); set_top_m_splat(configuration, value, addresses); return false; } template typename NativeType> void BytecodeInterpreter::set_top_m_splat(Wasm::Configuration& configuration, NativeType value, SourcesAndDestination const& addresses) { auto push = [&](auto result) { configuration.source_value(0, addresses.sources) = Value(bit_cast(result)); }; if constexpr (IsFloatingPoint>) { if constexpr (M == 32) // 32 -> 32x4 push(expand4(value)); else if constexpr (M == 64) // 64 -> 64x2 push(f64x2 { value, value }); else static_assert(DependentFalse>, "Invalid vector size"); } else { if constexpr (M == 8) // 8 -> 8x4 -> 32x4 push(expand4(bit_cast(u8x4 { value, value, value, value }))); else if constexpr (M == 16) // 16 -> 16x2 -> 32x4 push(expand4(bit_cast(u16x2 { value, value }))); else if constexpr (M == 32) // 32 -> 32x4 push(expand4(value)); else if constexpr (M == 64) // 64 -> 64x2 push(u64x2 { value, value }); else static_assert(DependentFalse>, "Invalid vector size"); } } template typename NativeType> void BytecodeInterpreter::pop_and_push_m_splat(Wasm::Configuration& configuration, Instruction const&, SourcesAndDestination const& addresses) { using PopT = Conditional, NativeType<64>>; using ReadT = NativeType; auto entry = configuration.source_value(0, addresses.sources); auto value = static_cast(entry.to()); dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> splat({})", value, M); set_top_m_splat(configuration, value, addresses); } template typename SetSign, typename VectorType> VectorType BytecodeInterpreter::pop_vector(Configuration& configuration, size_t source, SourcesAndDestination const& addresses) { // bounds checked by verifier. return bit_cast(configuration.take_source(source, addresses.sources).to()); } bool BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address, CallAddressSource source) { TRAP_IF_NOT(m_stack_info.size_free() >= Constants::minimum_stack_space_to_keep_free, "{}: {}", Constants::stack_exhaustion_message); auto instance = configuration.store().get(address); FunctionType const* type { nullptr }; instance->visit([&](auto const& function) { type = &function.type(); }); if (source == CallAddressSource::IndirectCall) { TRAP_IF_NOT(type->parameters().size() <= configuration.value_stack().size()); } Vector args; if (!type->parameters().is_empty()) { args.ensure_capacity(type->parameters().size()); auto span = configuration.value_stack().span().slice_from_end(type->parameters().size()); for (auto& value : span) args.unchecked_append(value); configuration.value_stack().remove(configuration.value_stack().size() - span.size(), span.size()); } Result result { Trap::from_string("") }; if (instance->has()) { CallFrameHandle handle { *this, configuration }; result = configuration.call(*this, address, move(args)); } else { result = configuration.call(*this, address, move(args)); } if (result.is_trap()) { m_trap = move(result.trap()); return true; } if (!result.values().is_empty()) { configuration.value_stack().ensure_capacity(configuration.value_stack().size() + result.values().size()); for (auto& entry : result.values().in_reverse()) configuration.value_stack().unchecked_append(entry); } return false; } template bool BytecodeInterpreter::binary_numeric_operation(Configuration& configuration, SourcesAndDestination const& addresses, Args&&... args) { // bounds checked by Nor. auto rhs = configuration.take_source(0, addresses.sources).to(); auto lhs = configuration.take_source(1, addresses.sources).to(); // bounds checked by verifier. PushType result; auto call_result = Operator { forward(args)... }(lhs, rhs); if constexpr (IsSpecializationOf) { if (call_result.is_error()) return trap_if_not(false, call_result.error()); result = call_result.release_value(); } else { result = call_result; } dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = {}", lhs, Operator::name(), rhs, result); configuration.push_to_destination(Value(result), addresses.destination); return false; } template bool BytecodeInterpreter::unary_operation(Configuration& configuration, SourcesAndDestination const& addresses, Args&&... args) { auto& entry = configuration.source_value(0, addresses.sources); // bounds checked by verifier. auto value = entry.to(); auto call_result = Operator { forward(args)... }(value); PushType result; if constexpr (IsSpecializationOf) { if (call_result.is_error()) return trap_if_not(false, call_result.error()); result = call_result.release_value(); } else { result = call_result; } dbgln_if(WASM_TRACE_DEBUG, "map({}) {} = {}", Operator::name(), value, result); entry = Value(result); return false; } template bool BytecodeInterpreter::pop_and_store(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { // bounds checked by verifier. auto entry = configuration.take_source(0, addresses.sources); auto value = ConvertToRaw {}(entry.to()); return store_value(configuration, instruction, value, 1, addresses); } template bool BytecodeInterpreter::store_value(Configuration& configuration, Instruction const& instruction, StoreT value, size_t address_source, SourcesAndDestination const& addresses) { auto& memarg = instruction.arguments().unsafe_get(); dbgln_if(WASM_TRACE_DEBUG, "stack({}) -> temporary({}b)", value, sizeof(StoreT)); auto base = configuration.take_source(address_source, addresses.sources).to(); return store_to_memory(configuration, memarg, { &value, sizeof(StoreT) }, base); } template bool BytecodeInterpreter::pop_and_store_lane_n(Configuration& configuration, Instruction const& instruction, SourcesAndDestination const& addresses) { auto& memarg_and_lane = instruction.arguments().get(); // bounds checked by verifier. auto vector = configuration.take_source(0, addresses.sources).to(); auto src = bit_cast(&vector) + memarg_and_lane.lane * N / 8; auto base = configuration.take_source(1, addresses.sources).to(); return store_to_memory(configuration, memarg_and_lane.memory, { src, N / 8 }, base); } bool BytecodeInterpreter::store_to_memory(Configuration& configuration, Instruction::MemoryArgument const& arg, ReadonlyBytes data, u32 base) { auto& address = configuration.frame().module().memories().data()[arg.memory_index.value()]; auto memory = configuration.store().get(address); u64 instance_address = static_cast(base) + arg.offset; Checked addition { instance_address }; addition += data.size(); if (addition.has_overflow() || addition.value() > memory->size()) [[unlikely]] { m_trap = Trap::from_string("Memory access out of bounds"); dbgln_if(WASM_TRACE_DEBUG, "LibWasm: Memory access out of bounds (expected 0 <= {} and {} <= {})", instance_address, instance_address + data.size(), memory->size()); return true; } dbgln_if(WASM_TRACE_DEBUG, "temporary({}b) -> store({})", data.size(), instance_address); (void)data.copy_to(memory->data().bytes().slice(instance_address, data.size())); return false; } template T BytecodeInterpreter::read_value(ReadonlyBytes data) { VERIFY(sizeof(T) <= data.size()); if (bit_cast(data.data()) % alignof(T)) { alignas(T) u8 buf[sizeof(T)]; memcpy(buf, data.data(), sizeof(T)); return bit_cast>(buf); } return *bit_cast const*>(data.data()); } template<> float BytecodeInterpreter::read_value(ReadonlyBytes data) { return bit_cast(read_value(data)); } template<> double BytecodeInterpreter::read_value(ReadonlyBytes data) { return bit_cast(read_value(data)); } CompiledInstructions try_compile_instructions(Expression const& expression, Span functions) { CompiledInstructions result; result.dispatches.ensure_capacity(expression.instructions().size()); result.extra_instruction_storage.ensure_capacity(expression.instructions().size()); i32 i32_const_value { 0 }; LocalIndex local_index_0 { 0 }; LocalIndex local_index_1 { 0 }; enum class InsnPatternState { Nothing, GetLocal, GetLocalI32Const, GetLocalx2, I32Const, I32ConstGetLocal, } pattern_state { InsnPatternState::Nothing }; static Instruction nop { Instructions::nop }; constexpr auto default_dispatch = [](Instruction const& instruction) { return Dispatch { { .instruction_opcode = instruction.opcode() }, &instruction, { .sources = { Dispatch::Stack, Dispatch::Stack, Dispatch::Stack }, .destination = Dispatch::Stack } }; }; for (auto& instruction : expression.instructions()) { if (instruction.opcode() == Instructions::call) { auto& function = functions[instruction.arguments().get().value()]; if (function.results().size() <= 1 && function.parameters().size() < 4) { pattern_state = InsnPatternState::Nothing; OpCode op { Instructions::synthetic_call_00.value() + function.parameters().size() * 2 + function.results().size() }; result.extra_instruction_storage.unchecked_append(Instruction( op, instruction.arguments())); result.dispatches.unchecked_append(default_dispatch(result.extra_instruction_storage.unsafe_last())); continue; } } switch (pattern_state) { case InsnPatternState::Nothing: if (instruction.opcode() == Instructions::local_get) { local_index_0 = instruction.local_index(); pattern_state = InsnPatternState::GetLocal; } else if (instruction.opcode() == Instructions::i32_const) { i32_const_value = instruction.arguments().get(); pattern_state = InsnPatternState::I32Const; } break; case InsnPatternState::GetLocal: if (instruction.opcode() == Instructions::local_get) { local_index_1 = instruction.local_index(); pattern_state = InsnPatternState::GetLocalx2; } else if (instruction.opcode() == Instructions::i32_const) { i32_const_value = instruction.arguments().get(); pattern_state = InsnPatternState::GetLocalI32Const; } else if (instruction.opcode() == Instructions::i32_store) { // `local.get a; i32.store m` -> `i32.storelocal a m`. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction( Instructions::synthetic_i32_storelocal, local_index_0, instruction.arguments())); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } else if (instruction.opcode() == Instructions::i64_store) { // `local.get a; i64.store m` -> `i64.storelocal a m`. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction( Instructions::synthetic_i64_storelocal, local_index_0, instruction.arguments())); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } else { pattern_state = InsnPatternState::Nothing; } break; case InsnPatternState::GetLocalx2: if (instruction.opcode() == Instructions::i32_add) { // `local.get a; local.get b; i32.add` -> `i32.add_2local a b`. // Replace the previous two ops with noops, and add i32.add_2local. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.dispatches[result.dispatches.size() - 2] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction { Instructions::synthetic_i32_add2local, local_index_0, local_index_1, }); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } if (instruction.opcode() == Instructions::i32_store) { // `local.get a; i32.store m` -> `i32.storelocal a m`. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction( Instructions::synthetic_i32_storelocal, local_index_1, instruction.arguments())); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } if (instruction.opcode() == Instructions::i64_store) { // `local.get a; i64.store m` -> `i64.storelocal a m`. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction( Instructions::synthetic_i64_storelocal, local_index_1, instruction.arguments())); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } if (instruction.opcode() == Instructions::i32_const) { swap(local_index_0, local_index_1); i32_const_value = instruction.arguments().get(); pattern_state = InsnPatternState::GetLocalI32Const; } else { pattern_state = InsnPatternState::Nothing; } break; case InsnPatternState::I32Const: if (instruction.opcode() == Instructions::local_get) { local_index_0 = instruction.local_index(); pattern_state = InsnPatternState::I32ConstGetLocal; } else if (instruction.opcode() == Instructions::i32_const) { i32_const_value = instruction.arguments().get(); } else if (instruction.opcode() == Instructions::local_set) { // `i32.const a; local.set b` -> `local.seti32_const b a`. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction( Instructions::synthetic_local_seti32_const, instruction.local_index(), i32_const_value)); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } else { pattern_state = InsnPatternState::Nothing; } break; case InsnPatternState::GetLocalI32Const: if (instruction.opcode() == Instructions::local_set) { // `i32.const a; local.set b` -> `local.seti32_const b a`. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction( Instructions::synthetic_local_seti32_const, instruction.local_index(), i32_const_value)); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } if (instruction.opcode() == Instructions::i32_const) { i32_const_value = instruction.arguments().get(); pattern_state = InsnPatternState::I32Const; break; } if (instruction.opcode() == Instructions::local_get) { local_index_0 = instruction.local_index(); pattern_state = InsnPatternState::I32ConstGetLocal; break; } [[fallthrough]]; case InsnPatternState::I32ConstGetLocal: if (instruction.opcode() == Instructions::i32_const) { i32_const_value = instruction.arguments().get(); pattern_state = InsnPatternState::GetLocalI32Const; } else if (instruction.opcode() == Instructions::local_get) { swap(local_index_0, local_index_1); local_index_1 = instruction.local_index(); pattern_state = InsnPatternState::GetLocalx2; } else if (instruction.opcode() == Instructions::i32_add) { // `i32.const a; local.get b; i32.add` -> `i32.add_constlocal b a`. // Replace the previous two ops with noops, and add i32.add_constlocal. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.dispatches[result.dispatches.size() - 2] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction( Instructions::synthetic_i32_addconstlocal, local_index_0, i32_const_value)); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } if (instruction.opcode() == Instructions::i32_and) { // `i32.const a; local.get b; i32.add` -> `i32.and_constlocal b a`. // Replace the previous two ops with noops, and add i32.and_constlocal. result.dispatches[result.dispatches.size() - 1] = default_dispatch(nop); result.dispatches[result.dispatches.size() - 2] = default_dispatch(nop); result.extra_instruction_storage.append(Instruction( Instructions::synthetic_i32_andconstlocal, local_index_0, i32_const_value)); result.dispatches.append(default_dispatch(result.extra_instruction_storage.unsafe_last())); pattern_state = InsnPatternState::Nothing; continue; } pattern_state = InsnPatternState::Nothing; break; } result.dispatches.unchecked_append(default_dispatch(instruction)); } // Remove all nops (that were either added by the above patterns or were already present in the original instructions), // and adjust jumps accordingly. RedBlackTree nops_to_remove; for (size_t i = 0; i < result.dispatches.size(); ++i) { if (result.dispatches[i].instruction->opcode() == Instructions::nop) nops_to_remove.insert(i, {}); } auto nops_to_remove_it = nops_to_remove.begin(); size_t offset_accumulated = 0; for (size_t i = 0; i < result.dispatches.size(); ++i) { if (result.dispatches[i].instruction->opcode() == Instructions::nop) { offset_accumulated++; ++nops_to_remove_it; continue; } auto& args = result.dispatches[i].instruction->arguments(); if (auto ptr = args.get_pointer()) { auto offset_to = [&](InstructionPointer ip) { size_t offset = 0; auto it = nops_to_remove_it; while (it != nops_to_remove.end() && it.key() < ip.value()) { ++offset; ++it; } return offset; }; InstructionPointer end_ip = ptr->end_ip.value() - offset_accumulated - offset_to(ptr->end_ip - ptr->else_ip.has_value()); auto else_ip = ptr->else_ip.map([&](InstructionPointer const& ip) -> InstructionPointer { return ip.value() - offset_accumulated - offset_to(ip - 1); }); auto instruction = *result.dispatches[i].instruction; instruction.arguments() = Instruction::StructuredInstructionArgs { .block_type = ptr->block_type, .end_ip = end_ip, .else_ip = else_ip, }; result.extra_instruction_storage.append(move(instruction)); result.dispatches[i].instruction = &result.extra_instruction_storage.unsafe_last(); result.dispatches[i].instruction_opcode = result.dispatches[i].instruction->opcode(); } } result.dispatches.remove_all(nops_to_remove, [](auto const& it) { return it.key(); }); // Allocate registers for instructions, meeting the following constraints: // - Any instruction that produces polymorphic stack, or requires its inputs on the stack must sink all active values to the stack. // - All instructions must have the same location for their last input and their destination value (if any). // - Any value left at the end of the expression must be on the stack. // - All inputs and outputs of call instructions with <4 inputs and <=1 output must be on the stack. using ValueID = DistinctNumeric; using IP = DistinctNumeric; struct Value { ValueID id; IP definition_index; Vector uses; IP last_use = 0; }; struct ActiveReg { ValueID value_id; IP end; Dispatch::RegisterOrStack reg; }; HashMap values; Vector value_stack; ValueID next_value_id = 0; HashMap instr_to_output_value; HashMap> instr_to_input_values; HashMap> instr_to_dependent_values; instr_to_output_value.ensure_capacity(result.dispatches.size()); instr_to_input_values.ensure_capacity(result.dispatches.size()); instr_to_dependent_values.ensure_capacity(result.dispatches.size()); Vector forced_stack_values; Vector parent; // parent[id] -> parent ValueID of id in the alias tree Vector rank; // rank[id] -> rank of the tree rooted at id Vector final_roots; // final_roots[id] -> the final root parent of id auto ensure_id_space = [&](ValueID id) { if (id >= parent.size()) { size_t old_size = parent.size(); parent.resize(id.value() + 1); rank.resize(id.value() + 1); final_roots.resize(id.value() + 1); for (size_t i = old_size; i <= id; ++i) { parent[i] = i; rank[i] = 0; final_roots[i] = i; } } }; auto find_root = [&parent](this auto& self, ValueID x) -> ValueID { if (parent[x.value()] != x) parent[x.value()] = self(parent[x.value()]); return parent[x.value()]; }; auto union_alias = [&](ValueID a, ValueID b) { ensure_id_space(max(a, b)); auto const root_a = find_root(a); auto const root_b = find_root(b); if (root_a == root_b) return; if (rank[root_a.value()] < rank[root_b.value()]) { parent[root_a.value()] = root_b; } else if (rank[root_a.value()] > rank[root_b.value()]) { parent[root_b.value()] = root_a; } else { parent[root_b.value()] = root_a; ++rank[root_a.value()]; } }; HashTable stack_forced_roots; Vector> live_at_instr; live_at_instr.resize(result.dispatches.size()); for (size_t i = 0; i < result.dispatches.size(); ++i) { auto& dispatch = result.dispatches[i]; auto opcode = dispatch.instruction->opcode(); size_t inputs = 0; size_t outputs = 0; Vector dependent_ids; bool variadic_or_unknown = false; auto const is_known_call = opcode == Instructions::synthetic_call_00 || opcode == Instructions::synthetic_call_01 || opcode == Instructions::synthetic_call_10 || opcode == Instructions::synthetic_call_11 || opcode == Instructions::synthetic_call_20 || opcode == Instructions::synthetic_call_21 || opcode == Instructions::synthetic_call_30 || opcode == Instructions::synthetic_call_31; switch (opcode.value()) { #define M(name, _, ins, outs) \ case Instructions::name.value(): \ if constexpr (ins == -1 || outs == -1) { \ variadic_or_unknown = true; \ } else { \ inputs = ins; \ outputs = outs; \ } \ break; ENUMERATE_WASM_OPCODES(M) #undef M } if (variadic_or_unknown) { for (auto val : value_stack) { auto& value = values.get(val).value(); value.uses.append(i); value.last_use = max(value.last_use, i); dependent_ids.append(val); forced_stack_values.append(val); live_at_instr[i].append(val); } value_stack.clear_with_capacity(); } Vector input_ids; if (!variadic_or_unknown && value_stack.size() < inputs) { size_t j = 0; for (; j < inputs && !value_stack.is_empty(); ++j) { auto input_value = value_stack.take_last(); input_ids.append(input_value); dependent_ids.append(input_value); auto& value = values.get(input_value).value(); value.uses.append(i); value.last_use = max(value.last_use, i); } for (; j < inputs; ++j) { auto val_id = next_value_id++; values.set(val_id, Value { val_id, i, {}, i }); input_ids.append(val_id); forced_stack_values.append(val_id); ensure_id_space(val_id); } inputs = 0; } for (size_t j = 0; j < inputs; ++j) { auto input_value = value_stack.take_last(); input_ids.append(input_value); dependent_ids.append(input_value); auto& value = values.get(input_value).value(); value.uses.append(i); value.last_use = max(value.last_use, i); if (is_known_call) forced_stack_values.append(input_value); } instr_to_input_values.set(i, input_ids); instr_to_dependent_values.set(i, dependent_ids); ValueID output_id = NumericLimits::max(); for (size_t j = 0; j < outputs; ++j) { auto id = next_value_id++; values.set(id, Value { id, i, {}, i }); value_stack.append(id); instr_to_output_value.set(i, id); output_id = id; ensure_id_space(id); if (is_known_call) forced_stack_values.append(id); } // Alias the output with the last input, if one exists. if (outputs > 0) { auto maybe_input_ids = instr_to_input_values.get(i); if (maybe_input_ids.has_value() && !maybe_input_ids->is_empty()) { auto last_input_id = maybe_input_ids->last(); union_alias(output_id, last_input_id); auto alias_root = find_root(last_input_id); // If any *other* input is forced to alias the output, we have no choice but to place all three on the stack. for (size_t j = 0; j < maybe_input_ids->size() - 1; ++j) { auto input_root = find_root((*maybe_input_ids)[j]); if (input_root == alias_root) { stack_forced_roots.set(alias_root); break; } } } } } forced_stack_values.extend(value_stack); for (size_t i = 0; i < final_roots.size(); ++i) final_roots[i] = find_root(i); // One more pass to ensure that all inputs and outputs of known calls are forced to the stack after aliases are resolved. for (size_t i = 0; i < result.dispatches.size(); ++i) { auto const opcode = result.dispatches[i].instruction->opcode(); auto const is_known_call = opcode == Instructions::synthetic_call_00 || opcode == Instructions::synthetic_call_01 || opcode == Instructions::synthetic_call_10 || opcode == Instructions::synthetic_call_11 || opcode == Instructions::synthetic_call_20 || opcode == Instructions::synthetic_call_21 || opcode == Instructions::synthetic_call_30 || opcode == Instructions::synthetic_call_31; if (is_known_call) { if (auto input_ids = instr_to_input_values.get(i); input_ids.has_value()) { for (auto input_id : *input_ids) { if (input_id.value() < final_roots.size()) { stack_forced_roots.set(final_roots[input_id.value()]); } } } if (auto output_id = instr_to_output_value.get(i); output_id.has_value()) { if (output_id->value() < final_roots.size()) { stack_forced_roots.set(final_roots[output_id->value()]); } } } } struct LiveInterval { ValueID value_id; IP start; IP end; bool forced_to_stack { false }; }; Vector intervals; intervals.ensure_capacity(values.size()); for (auto const& [_, value] : values) { auto start = value.definition_index; auto end = max(start, value.last_use); intervals.append({ value.id, start, end }); } for (auto id : forced_stack_values) stack_forced_roots.set(final_roots[id.value()]); for (auto& interval : intervals) interval.forced_to_stack = stack_forced_roots.contains(final_roots[interval.value_id.value()]); quick_sort(intervals, [](auto const& a, auto const& b) { return a.start < b.start; }); HashMap value_alloc; RedBlackTree active_by_end; auto expire_old_intervals = [&](IP current_start) { while (true) { auto it = active_by_end.find_smallest_not_below_iterator(current_start.value()); if (it.is_end()) break; active_by_end.remove(it.key()); } }; HashMap> alias_groups; for (auto& interval : intervals) { auto root = final_roots[interval.value_id.value()]; alias_groups.ensure(root).append(&interval); } struct RegisterOccupancy { Bitmap occupied; Vector roots_at_position; bool can_place(IP start, IP end, ValueID root) const { for (size_t i = start.value(); i <= end.value(); ++i) { if (occupied.get(i)) { if (roots_at_position.size() > i && roots_at_position[i].value() != root.value()) return false; } } return true; } void place(IP start, IP end, ValueID root) { if (roots_at_position.size() <= end.value()) roots_at_position.resize(end.value() + 1); occupied.set_range(start.value(), end.value() - start.value() + 1); for (size_t i = start.value(); i <= end.value(); ++i) roots_at_position[i] = root; } }; Array reg_occupancy; for (u8 r = 0; r < Dispatch::CountRegisters; ++r) { auto bitmap_result = Bitmap::create(result.dispatches.size(), false); if (bitmap_result.is_error()) { dbgln("Failed to allocate register bitmap of size {} ({}), bailing on register allocation", result.dispatches.size(), bitmap_result.error()); return {}; } reg_occupancy[r].occupied = bitmap_result.release_value(); } for (auto& [key, group] : alias_groups) { IP group_start = NumericLimits::max(); IP group_end = 0; auto group_forced_to_stack = false; for (auto* interval : group) { group_start = min(group_start, interval->start); group_end = max(group_end, interval->end); if (interval->forced_to_stack) group_forced_to_stack = true; } expire_old_intervals(group_start); Dispatch::RegisterOrStack reg = Dispatch::RegisterOrStack::Stack; if (!group_forced_to_stack) { Array used_regs; used_regs.fill(false); for (auto const& active_entry : active_by_end) { if (active_entry.reg != Dispatch::RegisterOrStack::Stack) used_regs[to_underlying(active_entry.reg)] = true; } auto group_root = final_roots[key.value()]; for (u8 r = 0; r < Dispatch::CountRegisters; ++r) { if (used_regs[r]) continue; if (reg_occupancy[r].can_place(group_start, group_end, group_root)) { reg = static_cast(r); active_by_end.insert(group_end.value(), { key, group_end, reg }); reg_occupancy[r].place(group_start, group_end, group_root); break; } } } for (auto* interval : group) value_alloc.set(interval->value_id, reg); } for (size_t i = 0; i < result.dispatches.size(); ++i) { auto& dispatch = result.dispatches[i]; auto input_ids = instr_to_input_values.get(i).value_or({}); for (size_t j = 0; j < input_ids.size(); ++j) { auto reg = value_alloc.get(input_ids[j]).value_or(Dispatch::RegisterOrStack::Stack); dispatch.sources[j] = reg; } if (auto output_id = instr_to_output_value.get(i); output_id.has_value()) dispatch.destination = value_alloc.get(*output_id).value_or(Dispatch::RegisterOrStack::Stack); } if constexpr (should_try_to_use_direct_threading) { for (auto& dispatch : result.dispatches) { #define CASE(name, ...) \ case Instructions::name.value(): \ dispatch.handler_ptr = bit_cast(&InstructionHandler::template operator()); \ break; switch (dispatch.instruction->opcode().value()) { ENUMERATE_WASM_OPCODES(CASE) default: VERIFY_NOT_REACHED(); } } result.direct = true; } return result; } }