From a21ebae6521b2310b18a3a777575d9f479133f02 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Tue, 18 May 2021 01:19:40 +0430 Subject: [PATCH] LibWasm: Implement checked truncation instructions This implements the 8 i.truncate.f_ instructions. --- .../LibWasm/AbstractMachine/AbstractMachine.h | 2 + .../LibWasm/AbstractMachine/Interpreter.cpp | 87 ++++++++++++++++--- .../LibWasm/AbstractMachine/Interpreter.h | 7 ++ 3 files changed, 86 insertions(+), 10 deletions(-) diff --git a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h index 42646df276..315d6cee8d 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.h @@ -115,6 +115,8 @@ public: [&](auto value) { if constexpr (IsSame) result = value; + else if constexpr (!IsFloatingPoint && IsSame>) + result = value; }, [&](const FunctionAddress& address) { if constexpr (IsSame) diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp index 6e789c8671..68312a5f3c 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp @@ -274,6 +274,51 @@ struct ConvertToRaw { } }; +template +MakeSigned Interpreter::checked_signed_truncate(V value) +{ + if (isnan(value) || isinf(value)) { // "undefined", let's just trap. + m_do_trap = true; + return 0; + } + + double truncated; + if constexpr (IsSame) + truncated = truncf(value); + else + truncated = trunc(value); + + using SignedT = MakeSigned; + if (NumericLimits::min() <= truncated && static_cast(NumericLimits::max()) >= truncated) + return static_cast(truncated); + + dbgln_if(WASM_TRACE_DEBUG, "Truncate out of range error"); + m_do_trap = true; + return true; +} + +template +MakeUnsigned Interpreter::checked_unsigned_truncate(V value) +{ + if (isnan(value) || isinf(value)) { // "undefined", let's just trap. + m_do_trap = true; + return 0; + } + double truncated; + if constexpr (IsSame) + truncated = truncf(value); + else + truncated = trunc(value); + + using UnsignedT = MakeUnsigned; + if (NumericLimits::min() <= truncated && static_cast(NumericLimits::max()) >= truncated) + return static_cast(truncated); + + dbgln_if(WASM_TRACE_DEBUG, "Truncate out of range error"); + m_do_trap = true; + return true; +} + Vector> Interpreter::pop_values(Configuration& configuration, size_t count) { Vector> results; @@ -753,20 +798,42 @@ void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip BINARY_PREFIX_NUMERIC_OPERATION(double, copysign, double); case Instructions::i32_wrap_i64.value(): UNARY_MAP(i64, i32, i32); - case Instructions::i32_trunc_sf32.value(): - case Instructions::i32_trunc_uf32.value(): - case Instructions::i32_trunc_sf64.value(): - case Instructions::i32_trunc_uf64.value(): - goto unimplemented; + case Instructions::i32_trunc_sf32.value(): { + auto fn = [this](auto& v) { return checked_signed_truncate(v); }; + UNARY_MAP(float, fn, i32); + } + case Instructions::i32_trunc_uf32.value(): { + auto fn = [this](auto& value) { return checked_unsigned_truncate(value); }; + UNARY_MAP(float, fn, i32); + } + case Instructions::i32_trunc_sf64.value(): { + auto fn = [this](auto& value) { return checked_signed_truncate(value); }; + UNARY_MAP(double, fn, i32); + } + case Instructions::i32_trunc_uf64.value(): { + auto fn = [this](auto& value) { return checked_unsigned_truncate(value); }; + UNARY_MAP(double, fn, i32); + } + case Instructions::i64_trunc_sf32.value(): { + auto fn = [this](auto& value) { return checked_signed_truncate(value); }; + UNARY_MAP(float, fn, i64); + } + case Instructions::i64_trunc_uf32.value(): { + auto fn = [this](auto& value) { return checked_unsigned_truncate(value); }; + UNARY_MAP(float, fn, i64); + } + case Instructions::i64_trunc_sf64.value(): { + auto fn = [this](auto& value) { return checked_signed_truncate(value); }; + UNARY_MAP(double, fn, i64); + } + case Instructions::i64_trunc_uf64.value(): { + auto fn = [this](auto& value) { return checked_unsigned_truncate(value); }; + UNARY_MAP(double, fn, i64); + } case Instructions::i64_extend_si32.value(): UNARY_MAP(i32, i64, i64); case Instructions::i64_extend_ui32.value(): UNARY_MAP(u32, i64, i64); - case Instructions::i64_trunc_sf32.value(): - case Instructions::i64_trunc_uf32.value(): - case Instructions::i64_trunc_sf64.value(): - case Instructions::i64_trunc_uf64.value(): - goto unimplemented; case Instructions::f32_convert_si32.value(): UNARY_MAP(i32, float, float); case Instructions::f32_convert_ui32.value(): diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h index e78f8f462d..0e06650bf5 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.h @@ -20,6 +20,13 @@ private: ReadonlyBytes load_from_memory(Configuration&, const Instruction&, size_t); void store_to_memory(Configuration&, const Instruction&, ReadonlyBytes data); void call_address(Configuration&, FunctionAddress); + + template + MakeUnsigned checked_unsigned_truncate(V); + + template + MakeSigned checked_signed_truncate(V); + Vector> pop_values(Configuration& configuration, size_t count); bool trap_if_not(bool value) {