summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorAli Mohammad Pur <ali.mpfard@gmail.com>2021-08-09 02:55:01 +0430
committerAndreas Kling <kling@serenityos.org>2021-08-12 21:03:53 +0200
commit563b402f04fadb3535d76992f388955544402a98 (patch)
treedee3b8a3c341e93d5d5d83d196a5c32ab93cd717 /Userland
parent799471d16f8255285a19c9ce8d7ca5675c6ce390 (diff)
downloadserenity-563b402f04fadb3535d76992f388955544402a98.zip
LibWasm: Replace the numeric operation macros with templated functions
This should make debugging and profiling much better, at little to no runtime cost. Also moves off the operator definitions to a separate header, so it should also improve the editing experience quite a bit.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp554
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h6
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp6
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/Configuration.h9
-rw-r--r--Userland/Libraries/LibWasm/AbstractMachine/Operators.h444
5 files changed, 662 insertions, 357 deletions
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp b/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp
index dedde43160..4a08bf9866 100644
--- a/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp
+++ b/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp
@@ -8,10 +8,9 @@
#include <LibWasm/AbstractMachine/AbstractMachine.h>
#include <LibWasm/AbstractMachine/BytecodeInterpreter.h>
#include <LibWasm/AbstractMachine/Configuration.h>
+#include <LibWasm/AbstractMachine/Operators.h>
#include <LibWasm/Opcode.h>
#include <LibWasm/Printer/Printer.h>
-#include <limits.h>
-#include <math.h>
namespace Wasm {
@@ -168,79 +167,58 @@ void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAdd
configuration.stack().entries().unchecked_append(move(entry));
}
-#define BINARY_NUMERIC_OPERATION(type, operator, cast, ...) \
- do { \
- TRAP_IF_NOT(!configuration.stack().is_empty()); \
- auto rhs_entry = configuration.stack().pop(); \
- auto& lhs_entry = configuration.stack().peek(); \
- TRAP_IF_NOT(rhs_entry.has<Value>()); \
- TRAP_IF_NOT(lhs_entry.has<Value>()); \
- auto rhs = rhs_entry.get<Value>().to<type>(); \
- auto lhs = lhs_entry.get<Value>().to<type>(); \
- TRAP_IF_NOT(lhs.has_value()); \
- TRAP_IF_NOT(rhs.has_value()); \
- __VA_ARGS__; \
- auto result = lhs.value() operator rhs.value(); \
- dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = {}", lhs.value(), #operator, rhs.value(), result); \
- configuration.stack().peek() = Value(cast(result)); \
- return; \
- } while (false)
-
-#define OVF_CHECKED_BINARY_NUMERIC_OPERATION(type, operator, cast, ...) \
- do { \
- TRAP_IF_NOT(!configuration.stack().is_empty()); \
- auto rhs_entry = configuration.stack().pop(); \
- auto& lhs_entry = configuration.stack().peek(); \
- TRAP_IF_NOT(rhs_entry.has<Value>()); \
- TRAP_IF_NOT(lhs_entry.has<Value>()); \
- auto rhs = rhs_entry.get<Value>().to<type>(); \
- auto ulhs = lhs_entry.get<Value>().to<type>(); \
- TRAP_IF_NOT(ulhs.has_value()); \
- TRAP_IF_NOT(rhs.has_value()); \
- dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = ??", ulhs.value(), #operator, rhs.value()); \
- __VA_ARGS__; \
- Checked<type> lhs = ulhs.value(); \
- lhs operator##= rhs.value(); \
- TRAP_IF_NOT(!lhs.has_overflow()); \
- auto result = lhs.value(); \
- dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = {}", ulhs.value(), #operator, rhs.value(), result); \
- configuration.stack().peek() = Value(cast(result)); \
- return; \
- } while (false)
-
-#define BINARY_PREFIX_NUMERIC_OPERATION(type, operation, cast, ...) \
- do { \
- TRAP_IF_NOT(!configuration.stack().is_empty()); \
- auto rhs_entry = configuration.stack().pop(); \
- auto& lhs_entry = configuration.stack().peek(); \
- TRAP_IF_NOT(rhs_entry.has<Value>()); \
- TRAP_IF_NOT(lhs_entry.has<Value>()); \
- auto rhs = rhs_entry.get<Value>().to<type>(); \
- auto lhs = lhs_entry.get<Value>().to<type>(); \
- TRAP_IF_NOT(lhs.has_value()); \
- TRAP_IF_NOT(rhs.has_value()); \
- __VA_ARGS__; \
- auto result = operation(lhs.value(), rhs.value()); \
- dbgln_if(WASM_TRACE_DEBUG, "{}({} {}) = {}", #operation, lhs.value(), rhs.value(), result); \
- configuration.stack().peek() = Value(cast(result)); \
- return; \
- } while (false)
-
-#define UNARY_MAP(pop_type, operation, ...) \
- do { \
- TRAP_IF_NOT(!configuration.stack().is_empty()); \
- auto& entry = configuration.stack().peek(); \
- TRAP_IF_NOT(entry.has<Value>()); \
- auto value = entry.get<Value>().to<pop_type>(); \
- TRAP_IF_NOT(value.has_value()); \
- auto result = operation(value.value()); \
- dbgln_if(WASM_TRACE_DEBUG, "map({}) {} = {}", #operation, value.value(), result); \
- configuration.stack().peek() = Value(__VA_ARGS__(result)); \
- return; \
- } while (false)
+template<typename PopType, typename PushType, typename Operator>
+void BytecodeInterpreter::binary_numeric_operation(Configuration& configuration)
+{
+ TRAP_IF_NOT(!configuration.stack().is_empty());
+ auto rhs_entry = configuration.stack().pop();
+ auto& lhs_entry = configuration.stack().peek();
+ auto rhs_ptr = rhs_entry.get_pointer<Value>();
+ auto lhs_ptr = lhs_entry.get_pointer<Value>();
+ TRAP_IF_NOT(rhs_ptr);
+ TRAP_IF_NOT(lhs_ptr);
+ auto rhs = rhs_ptr->to<PopType>();
+ auto lhs = lhs_ptr->to<PopType>();
+ TRAP_IF_NOT(lhs.has_value());
+ TRAP_IF_NOT(rhs.has_value());
+ PushType result;
+ auto call_result = Operator {}(lhs.value(), rhs.value());
+ if constexpr (IsSpecializationOf<decltype(call_result), AK::Result>) {
+ if (call_result.is_error()) {
+ trap_if_not(false, call_result.error());
+ return;
+ }
+ result = call_result.release_value();
+ } else {
+ result = call_result;
+ }
+ dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = {}", lhs.value(), Operator::name(), rhs.value(), result);
+ configuration.stack().peek() = Value(result);
+}
-#define UNARY_NUMERIC_OPERATION(type, operation) \
- UNARY_MAP(type, operation, type)
+template<typename PopType, typename PushType, typename Operator>
+void BytecodeInterpreter::unary_operation(Configuration& configuration)
+{
+ TRAP_IF_NOT(!configuration.stack().is_empty());
+ auto& entry = configuration.stack().peek();
+ auto entry_ptr = entry.get_pointer<Value>();
+ TRAP_IF_NOT(entry_ptr);
+ auto value = entry_ptr->to<PopType>();
+ TRAP_IF_NOT(value.has_value());
+ auto call_result = Operator {}(*value);
+ PushType result;
+ if constexpr (IsSpecializationOf<decltype(call_result), AK::Result>) {
+ if (call_result.is_error()) {
+ trap_if_not(false, call_result.error());
+ return;
+ }
+ result = call_result.release_value();
+ } else {
+ result = call_result;
+ }
+ dbgln_if(WASM_TRACE_DEBUG, "map({}) {} = {}", Operator::name(), *value, result);
+ configuration.stack().peek() = Value(result);
+}
#define LOAD_AND_PUSH(read_type, push_type) \
do { \
@@ -387,115 +365,6 @@ Vector<Value> BytecodeInterpreter::pop_values(Configuration& configuration, size
return results;
}
-template<typename T, typename R>
-ALWAYS_INLINE static T rotl(T value, R shift)
-{
- // generates a single 'rol' instruction if shift is positive
- // otherwise generate a `ror`
- auto const mask = CHAR_BIT * sizeof(T) - 1;
- shift &= mask;
- return (value << shift) | (value >> ((-shift) & mask));
-}
-
-template<typename T, typename R>
-ALWAYS_INLINE static T rotr(T value, R shift)
-{
- // generates a single 'ror' instruction if shift is positive
- // otherwise generate a `rol`
- auto const mask = CHAR_BIT * sizeof(T) - 1;
- shift &= mask;
- return (value >> shift) | (value << ((-shift) & mask));
-}
-
-template<typename T>
-ALWAYS_INLINE static i32 clz(T value)
-{
- if (value == 0)
- return sizeof(T) * CHAR_BIT;
-
- if constexpr (sizeof(T) == 4)
- return __builtin_clz(value);
- else if constexpr (sizeof(T) == 8)
- return __builtin_clzll(value);
- else
- VERIFY_NOT_REACHED();
-}
-
-template<typename T>
-ALWAYS_INLINE static i32 ctz(T value)
-{
- if (value == 0)
- return sizeof(T) * CHAR_BIT;
-
- if constexpr (sizeof(T) == 4)
- return __builtin_ctz(value);
- else if constexpr (sizeof(T) == 8)
- return __builtin_ctzll(value);
- else
- VERIFY_NOT_REACHED();
-}
-
-template<typename InputT, typename OutputT>
-ALWAYS_INLINE static OutputT extend_signed(InputT value)
-{
- // Note: C++ will take care of sign extension.
- return value;
-}
-
-template<typename TruncT, typename T>
-ALWAYS_INLINE static TruncT saturating_truncate(T value)
-{
- if (isnan(value))
- return 0;
-
- if (isinf(value)) {
- if (value < 0)
- return NumericLimits<TruncT>::min();
- return NumericLimits<TruncT>::max();
- }
-
- constexpr auto convert = [](auto truncated_value) {
- if (truncated_value < NumericLimits<TruncT>::min())
- return NumericLimits<TruncT>::min();
- if (static_cast<double>(truncated_value) > static_cast<double>(NumericLimits<TruncT>::max()))
- return NumericLimits<TruncT>::max();
- return static_cast<TruncT>(truncated_value);
- };
-
- if constexpr (IsSame<T, float>)
- return convert(truncf(value));
- else
- return convert(trunc(value));
-}
-
-template<typename T>
-ALWAYS_INLINE static T float_max(T lhs, T rhs)
-{
- if (isnan(lhs))
- return lhs;
- if (isnan(rhs))
- return rhs;
- if (isinf(lhs))
- return lhs > 0 ? lhs : rhs;
- if (isinf(rhs))
- return rhs > 0 ? rhs : lhs;
- return max(lhs, rhs);
-}
-
-template<typename T>
-ALWAYS_INLINE static T float_min(T lhs, T rhs)
-{
- if (isnan(lhs))
- return lhs;
- if (isnan(rhs))
- return rhs;
- if (isinf(lhs))
- return lhs > 0 ? rhs : lhs;
- if (isinf(rhs))
- return rhs > 0 ? lhs : rhs;
- return min(lhs, rhs);
-}
-
void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPointer& ip, Instruction const& instruction)
{
dbgln_if(WASM_TRACE_DEBUG, "Executing instruction {} at ip {}", instruction_name(instruction.opcode()), ip.value());
@@ -570,20 +439,16 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
}
case Instructions::structured_end.value():
case Instructions::structured_else.value(): {
- auto label = configuration.nth_label(0);
- TRAP_IF_NOT(label.has_value());
- size_t end = configuration.stack().size() - label->arity() - 1;
- size_t start = end;
- while (start > 0 && start < configuration.stack().size() && !configuration.stack().entries()[start].has<Label>())
- --start;
-
- configuration.stack().entries().remove(start, end - start + 1);
+ auto index = configuration.nth_label_index(0);
+ TRAP_IF_NOT(index.has_value());
+ auto label = configuration.stack().entries()[*index].get<Label>();
+ configuration.stack().entries().remove(*index, 1);
if (instruction.opcode() == Instructions::structured_end)
return;
// Jump to the end label
- configuration.ip() = label->continuation();
+ configuration.ip() = label.continuation();
return;
}
case Instructions::return_.value(): {
@@ -811,293 +676,277 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
return;
}
case Instructions::i32_eqz.value():
- UNARY_NUMERIC_OPERATION(i32, 0 ==);
+ return unary_operation<i32, i32, Operators::EqualsZero>(configuration);
case Instructions::i32_eq.value():
- BINARY_NUMERIC_OPERATION(i32, ==, i32);
+ return binary_numeric_operation<i32, i32, Operators::Equals>(configuration);
case Instructions::i32_ne.value():
- BINARY_NUMERIC_OPERATION(i32, !=, i32);
+ return binary_numeric_operation<i32, i32, Operators::NotEquals>(configuration);
case Instructions::i32_lts.value():
- BINARY_NUMERIC_OPERATION(i32, <, i32);
+ return binary_numeric_operation<i32, i32, Operators::LessThan>(configuration);
case Instructions::i32_ltu.value():
- BINARY_NUMERIC_OPERATION(u32, <, i32);
+ return binary_numeric_operation<u32, i32, Operators::LessThan>(configuration);
case Instructions::i32_gts.value():
- BINARY_NUMERIC_OPERATION(i32, >, i32);
+ return binary_numeric_operation<i32, i32, Operators::GreaterThan>(configuration);
case Instructions::i32_gtu.value():
- BINARY_NUMERIC_OPERATION(u32, >, i32);
+ return binary_numeric_operation<u32, i32, Operators::GreaterThan>(configuration);
case Instructions::i32_les.value():
- BINARY_NUMERIC_OPERATION(i32, <=, i32);
+ return binary_numeric_operation<i32, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::i32_leu.value():
- BINARY_NUMERIC_OPERATION(u32, <=, i32);
+ return binary_numeric_operation<u32, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::i32_ges.value():
- BINARY_NUMERIC_OPERATION(i32, >=, i32);
+ return binary_numeric_operation<i32, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::i32_geu.value():
- BINARY_NUMERIC_OPERATION(u32, >=, i32);
+ return binary_numeric_operation<u32, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::i64_eqz.value():
- UNARY_NUMERIC_OPERATION(i64, 0ull ==);
+ return unary_operation<i64, i32, Operators::EqualsZero>(configuration);
case Instructions::i64_eq.value():
- BINARY_NUMERIC_OPERATION(i64, ==, i32);
+ return binary_numeric_operation<i64, i32, Operators::Equals>(configuration);
case Instructions::i64_ne.value():
- BINARY_NUMERIC_OPERATION(i64, !=, i32);
+ return binary_numeric_operation<i64, i32, Operators::NotEquals>(configuration);
case Instructions::i64_lts.value():
- BINARY_NUMERIC_OPERATION(i64, <, i32);
+ return binary_numeric_operation<i64, i32, Operators::LessThan>(configuration);
case Instructions::i64_ltu.value():
- BINARY_NUMERIC_OPERATION(u64, <, i32);
+ return binary_numeric_operation<u64, i32, Operators::LessThan>(configuration);
case Instructions::i64_gts.value():
- BINARY_NUMERIC_OPERATION(i64, >, i32);
+ return binary_numeric_operation<i64, i32, Operators::GreaterThan>(configuration);
case Instructions::i64_gtu.value():
- BINARY_NUMERIC_OPERATION(u64, >, i32);
+ return binary_numeric_operation<u64, i32, Operators::GreaterThan>(configuration);
case Instructions::i64_les.value():
- BINARY_NUMERIC_OPERATION(i64, <=, i32);
+ return binary_numeric_operation<i64, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::i64_leu.value():
- BINARY_NUMERIC_OPERATION(u64, <=, i32);
+ return binary_numeric_operation<u64, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::i64_ges.value():
- BINARY_NUMERIC_OPERATION(i64, >=, i32);
+ return binary_numeric_operation<i64, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::i64_geu.value():
- BINARY_NUMERIC_OPERATION(u64, >=, i32);
+ return binary_numeric_operation<u64, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::f32_eq.value():
- BINARY_NUMERIC_OPERATION(float, ==, i32);
+ return binary_numeric_operation<float, i32, Operators::Equals>(configuration);
case Instructions::f32_ne.value():
- BINARY_NUMERIC_OPERATION(float, !=, i32);
+ return binary_numeric_operation<float, i32, Operators::NotEquals>(configuration);
case Instructions::f32_lt.value():
- BINARY_NUMERIC_OPERATION(float, <, i32);
+ return binary_numeric_operation<float, i32, Operators::LessThan>(configuration);
case Instructions::f32_gt.value():
- BINARY_NUMERIC_OPERATION(float, >, i32);
+ return binary_numeric_operation<float, i32, Operators::GreaterThan>(configuration);
case Instructions::f32_le.value():
- BINARY_NUMERIC_OPERATION(float, <=, i32);
+ return binary_numeric_operation<float, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::f32_ge.value():
- BINARY_NUMERIC_OPERATION(float, >=, i32);
+ return binary_numeric_operation<float, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::f64_eq.value():
- BINARY_NUMERIC_OPERATION(double, ==, i32);
+ return binary_numeric_operation<double, i32, Operators::Equals>(configuration);
case Instructions::f64_ne.value():
- BINARY_NUMERIC_OPERATION(double, !=, i32);
+ return binary_numeric_operation<double, i32, Operators::NotEquals>(configuration);
case Instructions::f64_lt.value():
- BINARY_NUMERIC_OPERATION(double, <, i32);
+ return binary_numeric_operation<double, i32, Operators::LessThan>(configuration);
case Instructions::f64_gt.value():
- BINARY_NUMERIC_OPERATION(double, >, i32);
+ return binary_numeric_operation<double, i32, Operators::GreaterThan>(configuration);
case Instructions::f64_le.value():
- BINARY_NUMERIC_OPERATION(double, <=, i32);
+ return binary_numeric_operation<double, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::f64_ge.value():
- BINARY_NUMERIC_OPERATION(double, >, i32);
+ return binary_numeric_operation<double, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::i32_clz.value():
- UNARY_NUMERIC_OPERATION(i32, clz);
+ return unary_operation<i32, i32, Operators::CountLeadingZeros>(configuration);
case Instructions::i32_ctz.value():
- UNARY_NUMERIC_OPERATION(i32, ctz);
+ return unary_operation<i32, i32, Operators::CountTrailingZeros>(configuration);
case Instructions::i32_popcnt.value():
- UNARY_NUMERIC_OPERATION(i32, __builtin_popcount);
+ return unary_operation<i32, i32, Operators::PopCount>(configuration);
case Instructions::i32_add.value():
- BINARY_NUMERIC_OPERATION(u32, +, i32);
+ return binary_numeric_operation<u32, i32, Operators::Add>(configuration);
case Instructions::i32_sub.value():
- BINARY_NUMERIC_OPERATION(u32, -, i32);
+ return binary_numeric_operation<u32, i32, Operators::Subtract>(configuration);
case Instructions::i32_mul.value():
- BINARY_NUMERIC_OPERATION(u32, *, i32);
+ return binary_numeric_operation<u32, i32, Operators::Multiply>(configuration);
case Instructions::i32_divs.value():
- BINARY_NUMERIC_OPERATION(i32, /, i32, TRAP_IF_NOT(!(Checked<i32>(lhs.value()) /= rhs.value()).has_overflow()));
+ return binary_numeric_operation<i32, i32, Operators::Divide>(configuration);
case Instructions::i32_divu.value():
- BINARY_NUMERIC_OPERATION(u32, /, i32, TRAP_IF_NOT(rhs.value() != 0));
+ return binary_numeric_operation<u32, i32, Operators::Divide>(configuration);
case Instructions::i32_rems.value():
- BINARY_NUMERIC_OPERATION(i32, %, i32, TRAP_IF_NOT(!(Checked<i32>(lhs.value()) /= rhs.value()).has_overflow()));
+ return binary_numeric_operation<i32, i32, Operators::Modulo>(configuration);
case Instructions::i32_remu.value():
- BINARY_NUMERIC_OPERATION(u32, %, i32, TRAP_IF_NOT(rhs.value() != 0));
+ return binary_numeric_operation<u32, i32, Operators::Modulo>(configuration);
case Instructions::i32_and.value():
- BINARY_NUMERIC_OPERATION(i32, &, i32);
+ return binary_numeric_operation<i32, i32, Operators::BitAnd>(configuration);
case Instructions::i32_or.value():
- BINARY_NUMERIC_OPERATION(i32, |, i32);
+ return binary_numeric_operation<i32, i32, Operators::BitOr>(configuration);
case Instructions::i32_xor.value():
- BINARY_NUMERIC_OPERATION(i32, ^, i32);
+ return binary_numeric_operation<i32, i32, Operators::BitXor>(configuration);
case Instructions::i32_shl.value():
- BINARY_NUMERIC_OPERATION(u32, <<, i32, (rhs = rhs.value() % 32));
+ return binary_numeric_operation<u32, i32, Operators::BitShiftLeft>(configuration);
case Instructions::i32_shrs.value():
- BINARY_NUMERIC_OPERATION(u32, >>, i32, (rhs = rhs.value() % 32)); // FIXME: eh, shouldn't we keep lhs as signed?
+ return binary_numeric_operation<i32, i32, Operators::BitShiftRight>(configuration);
case Instructions::i32_shru.value():
- BINARY_NUMERIC_OPERATION(u32, >>, i32, (rhs = rhs.value() % 32));
+ return binary_numeric_operation<u32, i32, Operators::BitShiftRight>(configuration);
case Instructions::i32_rotl.value():
- BINARY_PREFIX_NUMERIC_OPERATION(u32, rotl, i32, (rhs = rhs.value() % 32));
+ return binary_numeric_operation<u32, i32, Operators::BitRotateLeft>(configuration);
case Instructions::i32_rotr.value():
- BINARY_PREFIX_NUMERIC_OPERATION(u32, rotr, i32, (rhs = rhs.value() % 32));
+ return binary_numeric_operation<u32, i32, Operators::BitRotateRight>(configuration);
case Instructions::i64_clz.value():
- UNARY_NUMERIC_OPERATION(i64, clz);
+ return unary_operation<i64, i64, Operators::CountLeadingZeros>(configuration);
case Instructions::i64_ctz.value():
- UNARY_NUMERIC_OPERATION(i64, ctz);
+ return unary_operation<i64, i64, Operators::CountTrailingZeros>(configuration);
case Instructions::i64_popcnt.value():
- UNARY_NUMERIC_OPERATION(i64, __builtin_popcountll);
+ return unary_operation<i64, i64, Operators::PopCount>(configuration);
case Instructions::i64_add.value():
- BINARY_NUMERIC_OPERATION(u64, +, i64);
+ return binary_numeric_operation<u64, i64, Operators::Add>(configuration);
case Instructions::i64_sub.value():
- BINARY_NUMERIC_OPERATION(u64, -, i64);
+ return binary_numeric_operation<u64, i64, Operators::Subtract>(configuration);
case Instructions::i64_mul.value():
- BINARY_NUMERIC_OPERATION(u64, *, i64);
+ return binary_numeric_operation<u64, i64, Operators::Multiply>(configuration);
case Instructions::i64_divs.value():
- OVF_CHECKED_BINARY_NUMERIC_OPERATION(i64, /, i64, TRAP_IF_NOT(rhs.value() != 0));
+ return binary_numeric_operation<i64, i64, Operators::Divide>(configuration);
case Instructions::i64_divu.value():
- OVF_CHECKED_BINARY_NUMERIC_OPERATION(u64, /, i64, TRAP_IF_NOT(rhs.value() != 0));
+ return binary_numeric_operation<u64, i64, Operators::Divide>(configuration);
case Instructions::i64_rems.value():
- BINARY_NUMERIC_OPERATION(i64, %, i64, TRAP_IF_NOT(!(Checked<i32>(lhs.value()) /= rhs.value()).has_overflow()));
+ return binary_numeric_operation<i64, i64, Operators::Modulo>(configuration);
case Instructions::i64_remu.value():
- BINARY_NUMERIC_OPERATION(u64, %, i64, TRAP_IF_NOT(rhs.value() != 0));
+ return binary_numeric_operation<u64, i64, Operators::Modulo>(configuration);
case Instructions::i64_and.value():
- BINARY_NUMERIC_OPERATION(i64, &, i64);
+ return binary_numeric_operation<i64, i64, Operators::BitAnd>(configuration);
case Instructions::i64_or.value():
- BINARY_NUMERIC_OPERATION(i64, |, i64);
+ return binary_numeric_operation<i64, i64, Operators::BitOr>(configuration);
case Instructions::i64_xor.value():
- BINARY_NUMERIC_OPERATION(i64, ^, i64);
+ return binary_numeric_operation<i64, i64, Operators::BitXor>(configuration);
case Instructions::i64_shl.value():
- BINARY_NUMERIC_OPERATION(u64, <<, i64, (rhs = rhs.value() % 64));
+ return binary_numeric_operation<u64, i64, Operators::BitShiftLeft>(configuration);
case Instructions::i64_shrs.value():
- BINARY_NUMERIC_OPERATION(u64, >>, i64, (rhs = rhs.value() % 64)); // FIXME: eh, shouldn't we keep lhs as signed?
+ return binary_numeric_operation<i64, i64, Operators::BitShiftRight>(configuration);
case Instructions::i64_shru.value():
- BINARY_NUMERIC_OPERATION(u64, >>, i64, (rhs = rhs.value() % 64));
+ return binary_numeric_operation<u64, i64, Operators::BitShiftLeft>(configuration);
case Instructions::i64_rotl.value():
- BINARY_PREFIX_NUMERIC_OPERATION(u64, rotl, i64, (rhs = rhs.value() % 64));
+ return binary_numeric_operation<u64, i64, Operators::BitRotateLeft>(configuration);
case Instructions::i64_rotr.value():
- BINARY_PREFIX_NUMERIC_OPERATION(u64, rotr, i64, (rhs = rhs.value() % 64));
+ return binary_numeric_operation<u64, i64, Operators::BitRotateRight>(configuration);
case Instructions::f32_abs.value():
- UNARY_NUMERIC_OPERATION(float, fabsf);
+ return unary_operation<float, float, Operators::Absolute>(configuration);
case Instructions::f32_neg.value():
- UNARY_NUMERIC_OPERATION(float, -);
+ return unary_operation<float, float, Operators::Negate>(configuration);
case Instructions::f32_ceil.value():
- UNARY_NUMERIC_OPERATION(float, ceilf);
+ return unary_operation<float, float, Operators::Ceil>(configuration);
case Instructions::f32_floor.value():
- UNARY_NUMERIC_OPERATION(float, floorf);
+ return unary_operation<float, float, Operators::Floor>(configuration);
case Instructions::f32_trunc.value():
- UNARY_NUMERIC_OPERATION(float, truncf);
+ return unary_operation<float, float, Operators::Truncate>(configuration);
case Instructions::f32_nearest.value():
- UNARY_NUMERIC_OPERATION(float, roundf);
+ return unary_operation<float, float, Operators::Round>(configuration);
case Instructions::f32_sqrt.value():
- UNARY_NUMERIC_OPERATION(float, sqrtf);
+ return unary_operation<float, float, Operators::SquareRoot>(configuration);
case Instructions::f32_add.value():
- BINARY_NUMERIC_OPERATION(float, +, float);
+ return binary_numeric_operation<float, float, Operators::Add>(configuration);
case Instructions::f32_sub.value():
- BINARY_NUMERIC_OPERATION(float, -, float);
+ return binary_numeric_operation<float, float, Operators::Subtract>(configuration);
case Instructions::f32_mul.value():
- BINARY_NUMERIC_OPERATION(float, *, float);
+ return binary_numeric_operation<float, float, Operators::Multiply>(configuration);
case Instructions::f32_div.value():
- BINARY_NUMERIC_OPERATION(float, /, float);
+ return binary_numeric_operation<float, float, Operators::Divide>(configuration);
case Instructions::f32_min.value():
- BINARY_PREFIX_NUMERIC_OPERATION(float, float_min, float);
+ return binary_numeric_operation<float, float, Operators::Minimum>(configuration);
case Instructions::f32_max.value():
- BINARY_PREFIX_NUMERIC_OPERATION(float, float_max, float);
+ return binary_numeric_operation<float, float, Operators::Maximum>(configuration);
case Instructions::f32_copysign.value():
- BINARY_PREFIX_NUMERIC_OPERATION(float, copysignf, float);
+ return binary_numeric_operation<float, float, Operators::CopySign>(configuration);
case Instructions::f64_abs.value():
- UNARY_NUMERIC_OPERATION(double, fabs);
+ return unary_operation<double, double, Operators::Absolute>(configuration);
case Instructions::f64_neg.value():
- UNARY_NUMERIC_OPERATION(double, -);
+ return unary_operation<double, double, Operators::Negate>(configuration);
case Instructions::f64_ceil.value():
- UNARY_NUMERIC_OPERATION(double, ceil);
+ return unary_operation<double, double, Operators::Ceil>(configuration);
case Instructions::f64_floor.value():
- UNARY_NUMERIC_OPERATION(double, floor);
+ return unary_operation<double, double, Operators::Floor>(configuration);
case Instructions::f64_trunc.value():
- UNARY_NUMERIC_OPERATION(double, trunc);
+ return unary_operation<double, double, Operators::Truncate>(configuration);
case Instructions::f64_nearest.value():
- UNARY_NUMERIC_OPERATION(double, round);
+ return unary_operation<double, double, Operators::Round>(configuration);
case Instructions::f64_sqrt.value():
- UNARY_NUMERIC_OPERATION(double, sqrt);
+ return unary_operation<double, double, Operators::SquareRoot>(configuration);
case Instructions::f64_add.value():
- BINARY_NUMERIC_OPERATION(double, +, double);
+ return binary_numeric_operation<double, double, Operators::Add>(configuration);
case Instructions::f64_sub.value():
- BINARY_NUMERIC_OPERATION(double, -, double);
+ return binary_numeric_operation<double, double, Operators::Subtract>(configuration);
case Instructions::f64_mul.value():
- BINARY_NUMERIC_OPERATION(double, *, double);
+ return binary_numeric_operation<double, double, Operators::Multiply>(configuration);
case Instructions::f64_div.value():
- BINARY_NUMERIC_OPERATION(double, /, double);
+ return binary_numeric_operation<double, double, Operators::Divide>(configuration);
case Instructions::f64_min.value():
- BINARY_PREFIX_NUMERIC_OPERATION(double, float_min, double);
+ return binary_numeric_operation<double, double, Operators::Minimum>(configuration);
case Instructions::f64_max.value():
- BINARY_PREFIX_NUMERIC_OPERATION(double, float_max, double);
+ return binary_numeric_operation<double, double, Operators::Maximum>(configuration);
case Instructions::f64_copysign.value():
- BINARY_PREFIX_NUMERIC_OPERATION(double, copysign, double);
+ return binary_numeric_operation<double, double, Operators::CopySign>(configuration);
case Instructions::i32_wrap_i64.value():
- UNARY_MAP(i64, i32, i32);
- case Instructions::i32_trunc_sf32.value(): {
- auto fn = [this](auto& v) { return checked_signed_truncate<float, i32>(v); };
- UNARY_MAP(float, fn, i32);
- }
- case Instructions::i32_trunc_uf32.value(): {
- auto fn = [this](auto& value) { return checked_unsigned_truncate<float, i32>(value); };
- UNARY_MAP(float, fn, i32);
- }
- case Instructions::i32_trunc_sf64.value(): {
- auto fn = [this](auto& value) { return checked_signed_truncate<double, i32>(value); };
- UNARY_MAP(double, fn, i32);
- }
- case Instructions::i32_trunc_uf64.value(): {
- auto fn = [this](auto& value) { return checked_unsigned_truncate<double, i32>(value); };
- UNARY_MAP(double, fn, i32);
- }
- case Instructions::i64_trunc_sf32.value(): {
- auto fn = [this](auto& value) { return checked_signed_truncate<float, i64>(value); };
- UNARY_MAP(float, fn, i64);
- }
- case Instructions::i64_trunc_uf32.value(): {
- auto fn = [this](auto& value) { return checked_unsigned_truncate<float, i64>(value); };
- UNARY_MAP(float, fn, i64);
- }
- case Instructions::i64_trunc_sf64.value(): {
- auto fn = [this](auto& value) { return checked_signed_truncate<double, i64>(value); };
- UNARY_MAP(double, fn, i64);
- }
- case Instructions::i64_trunc_uf64.value(): {
- auto fn = [this](auto& value) { return checked_unsigned_truncate<double, i64>(value); };
- UNARY_MAP(double, fn, i64);
- }
+ return unary_operation<i64, i32, Operators::Wrap<i32>>(configuration);
+ case Instructions::i32_trunc_sf32.value():
+ return unary_operation<float, i32, Operators::CheckedTruncate<i32>>(configuration);
+ case Instructions::i32_trunc_uf32.value():
+ return unary_operation<float, i32, Operators::CheckedTruncate<u32>>(configuration);
+ case Instructions::i32_trunc_sf64.value():
+ return unary_operation<double, i32, Operators::CheckedTruncate<i32>>(configuration);
+ case Instructions::i32_trunc_uf64.value():
+ return unary_operation<double, i32, Operators::CheckedTruncate<u32>>(configuration);
+ case Instructions::i64_trunc_sf32.value():
+ return unary_operation<float, i64, Operators::CheckedTruncate<i64>>(configuration);
+ case Instructions::i64_trunc_uf32.value():
+ return unary_operation<float, i64, Operators::CheckedTruncate<u64>>(configuration);
+ case Instructions::i64_trunc_sf64.value():
+ return unary_operation<double, i64, Operators::CheckedTruncate<i64>>(configuration);
+ case Instructions::i64_trunc_uf64.value():
+ return unary_operation<double, i64, Operators::CheckedTruncate<u64>>(configuration);
case Instructions::i64_extend_si32.value():
- UNARY_MAP(i32, i64, i64);
+ return unary_operation<i32, i64, Operators::Extend<i64>>(configuration);
case Instructions::i64_extend_ui32.value():
- UNARY_MAP(u32, i64, i64);
+ return unary_operation<u32, i64, Operators::Extend<i64>>(configuration);
case Instructions::f32_convert_si32.value():
- UNARY_MAP(i32, float, float);
+ return unary_operation<i32, float, Operators::Convert<float>>(configuration);
case Instructions::f32_convert_ui32.value():
- UNARY_MAP(u32, float, float);
+ return unary_operation<u32, float, Operators::Convert<float>>(configuration);
case Instructions::f32_convert_si64.value():
- UNARY_MAP(i64, float, float);
+ return unary_operation<i64, float, Operators::Convert<float>>(configuration);
case Instructions::f32_convert_ui64.value():
- UNARY_MAP(u32, float, float);
+ return unary_operation<u64, float, Operators::Convert<float>>(configuration);
case Instructions::f32_demote_f64.value():
- UNARY_MAP(double, float, float);
+ return unary_operation<double, float, Operators::Demote>(configuration);
case Instructions::f64_convert_si32.value():
- UNARY_MAP(i32, double, double);
+ return unary_operation<i32, double, Operators::Convert<double>>(configuration);
case Instructions::f64_convert_ui32.value():
- UNARY_MAP(u32, double, double);
+ return unary_operation<u32, double, Operators::Convert<double>>(configuration);
case Instructions::f64_convert_si64.value():
- UNARY_MAP(i64, double, double);
+ return unary_operation<i64, double, Operators::Convert<double>>(configuration);
case Instructions::f64_convert_ui64.value():
- UNARY_MAP(u64, double, double);
+ return unary_operation<u64, double, Operators::Convert<double>>(configuration);
case Instructions::f64_promote_f32.value():
- UNARY_MAP(float, double, double);
+ return unary_operation<float, double, Operators::Promote>(configuration);
case Instructions::i32_reinterpret_f32.value():
- UNARY_MAP(float, bit_cast<i32>, i32);
+ return unary_operation<float, i32, Operators::Reinterpret<i32>>(configuration);
case Instructions::i64_reinterpret_f64.value():
- UNARY_MAP(double, bit_cast<i64>, i64);
+ return unary_operation<double, i64, Operators::Reinterpret<i64>>(configuration);
case Instructions::f32_reinterpret_i32.value():
- UNARY_MAP(i32, bit_cast<float>, float);
+ return unary_operation<i32, float, Operators::Reinterpret<float>>(configuration);
case Instructions::f64_reinterpret_i64.value():
- UNARY_MAP(i64, bit_cast<double>, double);
+ return unary_operation<i64, double, Operators::Reinterpret<double>>(configuration);
case Instructions::i32_extend8_s.value():
- UNARY_MAP(i32, (extend_signed<i8, i32>), i32);
+ return unary_operation<i32, i32, Operators::SignExtend<i8>>(configuration);
case Instructions::i32_extend16_s.value():
- UNARY_MAP(i32, (extend_signed<i16, i32>), i32);
+ return unary_operation<i32, i32, Operators::SignExtend<i16>>(configuration);
case Instructions::i64_extend8_s.value():
- UNARY_MAP(i64, (extend_signed<i8, i64>), i64);
+ return unary_operation<i64, i64, Operators::SignExtend<i8>>(configuration);
case Instructions::i64_extend16_s.value():
- UNARY_MAP(i64, (extend_signed<i16, i64>), i64);
+ return unary_operation<i64, i64, Operators::SignExtend<i16>>(configuration);
case Instructions::i64_extend32_s.value():
- UNARY_MAP(i64, (extend_signed<i32, i64>), i64);
+ return unary_operation<i64, i64, Operators::SignExtend<i32>>(configuration);
case Instructions::i32_trunc_sat_f32_s.value():
- UNARY_MAP(float, saturating_truncate<i32>, i32);
+ return unary_operation<float, i32, Operators::SaturatingTruncate<i32>>(configuration);
case Instructions::i32_trunc_sat_f32_u.value():
- UNARY_MAP(float, saturating_truncate<u32>, i32);
+ return unary_operation<float, i32, Operators::SaturatingTruncate<u32>>(configuration);
case Instructions::i32_trunc_sat_f64_s.value():
- UNARY_MAP(double, saturating_truncate<i32>, i32);
+ return unary_operation<double, i32, Operators::SaturatingTruncate<i32>>(configuration);
case Instructions::i32_trunc_sat_f64_u.value():
- UNARY_MAP(double, saturating_truncate<u32>, i32);
+ return unary_operation<double, i32, Operators::SaturatingTruncate<u32>>(configuration);
case Instructions::i64_trunc_sat_f32_s.value():
- UNARY_MAP(float, saturating_truncate<i64>, i64);
+ return unary_operation<float, i64, Operators::SaturatingTruncate<i64>>(configuration);
case Instructions::i64_trunc_sat_f32_u.value():
- UNARY_MAP(float, saturating_truncate<u64>, i64);
+ return unary_operation<float, i64, Operators::SaturatingTruncate<u64>>(configuration);
case Instructions::i64_trunc_sat_f64_s.value():
- UNARY_MAP(double, saturating_truncate<i64>, i64);
+ return unary_operation<double, i64, Operators::SaturatingTruncate<i64>>(configuration);
case Instructions::i64_trunc_sat_f64_u.value():
- UNARY_MAP(double, saturating_truncate<u64>, i64);
+ return unary_operation<double, i64, Operators::SaturatingTruncate<u64>>(configuration);
case Instructions::memory_init.value():
case Instructions::data_drop.value():
case Instructions::memory_copy.value():
@@ -1136,5 +985,4 @@ void DebuggerBytecodeInterpreter::interpret(Configuration& configuration, Instru
}
}
}
-
}
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h b/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h
index fc4bcb5c04..9a9912daed 100644
--- a/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h
+++ b/Userland/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h
@@ -40,6 +40,12 @@ protected:
void store_to_memory(Configuration&, Instruction const&, ReadonlyBytes data);
void call_address(Configuration&, FunctionAddress);
+ template<typename PopType, typename PushType, typename Operator>
+ void binary_numeric_operation(Configuration&);
+
+ template<typename PopType, typename PushType, typename Operator>
+ void unary_operation(Configuration&);
+
template<typename V, typename T>
MakeUnsigned<T> checked_unsigned_truncate(V);
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp
index ed7c925b19..ff32e26f3f 100644
--- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp
+++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp
@@ -10,13 +10,13 @@
namespace Wasm {
-Optional<Label> Configuration::nth_label(size_t i)
+Optional<size_t> Configuration::nth_label_index(size_t i)
{
for (size_t index = m_stack.size(); index > 0; --index) {
auto& entry = m_stack.entries()[index - 1];
- if (auto ptr = entry.get_pointer<Label>()) {
+ if (entry.has<Label>()) {
if (i == 0)
- return *ptr;
+ return index - 1;
--i;
}
}
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h
index 580ad526e5..2d3f9fe4d2 100644
--- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h
+++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.h
@@ -17,7 +17,14 @@ public:
{
}
- Optional<Label> nth_label(size_t);
+ Optional<Label> nth_label(size_t label)
+ {
+ auto index = nth_label_index(label);
+ if (index.has_value())
+ return m_stack.entries()[index.value()].get<Label>();
+ return {};
+ }
+ Optional<size_t> nth_label_index(size_t);
void set_frame(Frame&& frame)
{
m_current_frame_index = m_stack.size();
diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Operators.h b/Userland/Libraries/LibWasm/AbstractMachine/Operators.h
new file mode 100644
index 0000000000..ecb13f37e7
--- /dev/null
+++ b/Userland/Libraries/LibWasm/AbstractMachine/Operators.h
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/BitCast.h>
+#include <AK/Result.h>
+#include <AK/StringView.h>
+#include <AK/Types.h>
+#include <limits.h>
+#include <math.h>
+
+namespace Operators {
+
+#define DEFINE_BINARY_OPERATOR(Name, operation) \
+ struct Name { \
+ template<typename Lhs, typename Rhs> \
+ auto operator()(Lhs lhs, Rhs rhs) const { return lhs operation rhs; } \
+ \
+ static StringView name() { return #operation; } \
+ }
+
+DEFINE_BINARY_OPERATOR(Equals, ==);
+DEFINE_BINARY_OPERATOR(NotEquals, !=);
+DEFINE_BINARY_OPERATOR(GreaterThan, >);
+DEFINE_BINARY_OPERATOR(LessThan, <);
+DEFINE_BINARY_OPERATOR(LessThanOrEquals, <=);
+DEFINE_BINARY_OPERATOR(GreaterThanOrEquals, >=);
+DEFINE_BINARY_OPERATOR(Add, +);
+DEFINE_BINARY_OPERATOR(Subtract, -);
+DEFINE_BINARY_OPERATOR(Multiply, *);
+DEFINE_BINARY_OPERATOR(BitAnd, &);
+DEFINE_BINARY_OPERATOR(BitOr, |);
+DEFINE_BINARY_OPERATOR(BitXor, ^);
+
+#undef DEFINE_BINARY_OPERATOR
+
+struct Divide {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const
+ {
+ if constexpr (IsFloatingPoint<Lhs>) {
+ return lhs / rhs;
+ } else {
+ Checked value(lhs);
+ value /= rhs;
+ if (value.has_overflow())
+ return AK::Result<Lhs, StringView>("Integer division overflow"sv);
+ return AK::Result<Lhs, StringView>(value.value());
+ }
+ }
+
+ static StringView name() { return "/"; }
+};
+struct Modulo {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const
+ {
+ if (rhs == 0)
+ return AK::Result<Lhs, StringView>("Integer division overflow"sv);
+ if constexpr (IsSigned<Lhs>) {
+ if (rhs == -1)
+ return AK::Result<Lhs, StringView>(0); // Spec weirdness right here, signed division overflow is ignored.
+ }
+ return AK::Result<Lhs, StringView>(lhs % rhs);
+ }
+
+ static StringView name() { return "%"; }
+};
+struct BitShiftLeft {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const { return lhs << (rhs % (sizeof(lhs) * 8)); }
+
+ static StringView name() { return "<<"; }
+};
+struct BitShiftRight {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const { return lhs >> (rhs % (sizeof(lhs) * 8)); }
+
+ static StringView name() { return ">>"; }
+};
+struct BitRotateLeft {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const
+ {
+ // generates a single 'rol' instruction if shift is positive
+ // otherwise generate a `ror`
+ auto const mask = CHAR_BIT * sizeof(Lhs) - 1;
+ rhs &= mask;
+ return (lhs << rhs) | (lhs >> ((-rhs) & mask));
+ }
+
+ static StringView name() { return "rotate_left"; }
+};
+struct BitRotateRight {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const
+ {
+ // generates a single 'ror' instruction if shift is positive
+ // otherwise generate a `rol`
+ auto const mask = CHAR_BIT * sizeof(Lhs) - 1;
+ rhs &= mask;
+ return (lhs >> rhs) | (lhs << ((-rhs) & mask));
+ }
+
+ static StringView name() { return "rotate_right"; }
+};
+struct Minimum {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const
+ {
+ if constexpr (IsFloatingPoint<Lhs> || IsFloatingPoint<Rhs>) {
+ if (isnan(lhs))
+ return lhs;
+ if (isnan(rhs))
+ return rhs;
+ if (isinf(lhs))
+ return lhs > 0 ? rhs : lhs;
+ if (isinf(rhs))
+ return rhs > 0 ? lhs : rhs;
+ }
+ return min(lhs, rhs);
+ }
+
+ static StringView name() { return "minimum"; }
+};
+struct Maximum {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const
+ {
+ if constexpr (IsFloatingPoint<Lhs> || IsFloatingPoint<Rhs>) {
+ if (isnan(lhs))
+ return lhs;
+ if (isnan(rhs))
+ return rhs;
+ if (isinf(lhs))
+ return lhs > 0 ? lhs : rhs;
+ if (isinf(rhs))
+ return rhs > 0 ? rhs : lhs;
+ }
+ return max(lhs, rhs);
+ }
+
+ static StringView name() { return "maximum"; }
+};
+struct CopySign {
+ template<typename Lhs, typename Rhs>
+ auto operator()(Lhs lhs, Rhs rhs) const
+ {
+ if constexpr (IsSame<Lhs, float>)
+ return copysignf(lhs, rhs);
+ else if constexpr (IsSame<Lhs, double>)
+ return copysign(lhs, rhs);
+ else
+ static_assert(DependentFalse<Lhs, Rhs>, "Invalid types to CopySign");
+ }
+
+ static StringView name() { return "copysign"; }
+};
+
+// Unary
+
+struct EqualsZero {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const { return lhs == 0; }
+
+ static StringView name() { return "== 0"; }
+};
+struct CountLeadingZeros {
+ template<typename Lhs>
+ i32 operator()(Lhs lhs) const
+ {
+ if (lhs == 0)
+ return sizeof(Lhs) * CHAR_BIT;
+
+ if constexpr (sizeof(Lhs) == 4)
+ return __builtin_clz(lhs);
+ else if constexpr (sizeof(Lhs) == 8)
+ return __builtin_clzll(lhs);
+ else
+ VERIFY_NOT_REACHED();
+ }
+
+ static StringView name() { return "clz"; }
+};
+struct CountTrailingZeros {
+ template<typename Lhs>
+ i32 operator()(Lhs lhs) const
+ {
+ if (lhs == 0)
+ return sizeof(Lhs) * CHAR_BIT;
+
+ if constexpr (sizeof(Lhs) == 4)
+ return __builtin_ctz(lhs);
+ else if constexpr (sizeof(Lhs) == 8)
+ return __builtin_ctzll(lhs);
+ else
+ VERIFY_NOT_REACHED();
+ }
+
+ static StringView name() { return "ctz"; }
+};
+struct PopCount {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const
+ {
+ if constexpr (sizeof(Lhs) == 4)
+ return __builtin_popcount(lhs);
+ else if constexpr (sizeof(Lhs) == 8)
+ return __builtin_popcountll(lhs);
+ else
+ VERIFY_NOT_REACHED();
+ }
+
+ static StringView name() { return "popcnt"; }
+};
+struct Absolute {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const { return AK::abs(lhs); }
+
+ static StringView name() { return "abs"; }
+};
+struct Negate {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const { return -lhs; }
+
+ static StringView name() { return "== 0"; }
+};
+struct Ceil {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const
+ {
+ if constexpr (IsSame<Lhs, float>)
+ return ceilf(lhs);
+ else if constexpr (IsSame<Lhs, double>)
+ return ceil(lhs);
+ else
+ VERIFY_NOT_REACHED();
+ }
+
+ static StringView name() { return "ceil"; }
+};
+struct Floor {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const
+ {
+ if constexpr (IsSame<Lhs, float>)
+ return floorf(lhs);
+ else if constexpr (IsSame<Lhs, double>)
+ return floor(lhs);
+ else
+ VERIFY_NOT_REACHED();
+ }
+
+ static StringView name() { return "floor"; }
+};
+struct Truncate {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const
+ {
+ if constexpr (IsSame<Lhs, float>)
+ return truncf(lhs);
+ else if constexpr (IsSame<Lhs, double>)
+ return trunc(lhs);
+ else
+ VERIFY_NOT_REACHED();
+ }
+
+ static StringView name() { return "truncate"; }
+};
+struct Round {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const
+ {
+ if constexpr (IsSame<Lhs, float>)
+ return roundf(lhs);
+ else if constexpr (IsSame<Lhs, double>)
+ return round(lhs);
+ else
+ VERIFY_NOT_REACHED();
+ }
+
+ static StringView name() { return "round"; }
+};
+struct SquareRoot {
+ template<typename Lhs>
+ auto operator()(Lhs lhs) const
+ {
+ if constexpr (IsSame<Lhs, float>)
+ return sqrtf(lhs);
+ else if constexpr (IsSame<Lhs, double>)
+ return sqrt(lhs);
+ else
+ VERIFY_NOT_REACHED();
+ }
+
+ static StringView name() { return "sqrt"; }
+};
+
+template<typename Result>
+struct Wrap {
+ template<typename Lhs>
+ Result operator()(Lhs lhs) const
+ {
+ return static_cast<MakeUnsigned<Result>>(bit_cast<MakeUnsigned<Lhs>>(lhs));
+ }
+
+ static StringView name() { return "wrap"; }
+};
+
+template<typename ResultT>
+struct CheckedTruncate {
+ template<typename Lhs>
+ AK::Result<ResultT, StringView> operator()(Lhs lhs) const
+ {
+ if (isnan(lhs) || isinf(lhs)) // "undefined", let's just trap.
+ return "Truncation undefined behaviour"sv;
+
+ Lhs truncated;
+ if constexpr (IsSame<float, Lhs>)
+ truncated = truncf(lhs);
+ else if constexpr (IsSame<double, Lhs>)
+ truncated = trunc(lhs);
+ else
+ VERIFY_NOT_REACHED();
+
+ if (NumericLimits<ResultT>::min() <= truncated && static_cast<double>(NumericLimits<ResultT>::max()) >= static_cast<double>(truncated))
+ return static_cast<ResultT>(truncated);
+
+ return "Truncation out of range"sv;
+ }
+
+ static StringView name() { return "truncate.checked"; }
+};
+
+template<typename ResultT>
+struct Extend {
+ template<typename Lhs>
+ ResultT operator()(Lhs lhs) const
+ {
+ return lhs;
+ }
+
+ static StringView name() { return "extend"; }
+};
+
+template<typename ResultT>
+struct Convert {
+ template<typename Lhs>
+ ResultT operator()(Lhs lhs) const
+ {
+ auto signed_interpretation = bit_cast<MakeSigned<Lhs>>(lhs);
+ return static_cast<ResultT>(signed_interpretation);
+ }
+
+ static StringView name() { return "convert"; }
+};
+
+template<typename ResultT>
+struct Reinterpret {
+ template<typename Lhs>
+ ResultT operator()(Lhs lhs) const
+ {
+ return bit_cast<ResultT>(lhs);
+ }
+
+ static StringView name() { return "reinterpret"; }
+};
+
+struct Promote {
+ double operator()(float lhs) const
+ {
+ if (isnan(lhs))
+ return nan(""); // FIXME: Ensure canonical NaN remains canonical
+ return static_cast<double>(lhs);
+ }
+
+ static StringView name() { return "promote"; }
+};
+
+struct Demote {
+ float operator()(double lhs) const
+ {
+ if (isnan(lhs))
+ return nanf(""); // FIXME: Ensure canonical NaN remains canonical
+
+ if (isinf(lhs))
+ return __builtin_huge_valf();
+
+ return static_cast<float>(lhs);
+ }
+
+ static StringView name() { return "demote"; }
+};
+
+template<typename InitialType>
+struct SignExtend {
+ template<typename Lhs>
+ Lhs operator()(Lhs lhs) const
+ {
+ auto unsigned_representation = bit_cast<MakeUnsigned<Lhs>>(lhs);
+ auto truncated_unsigned_representation = static_cast<MakeUnsigned<InitialType>>(unsigned_representation);
+ auto initial_value = bit_cast<InitialType>(truncated_unsigned_representation);
+ return static_cast<Lhs>(initial_value);
+ }
+
+ static StringView name() { return "extend"; }
+};
+
+template<typename ResultT>
+struct SaturatingTruncate {
+ template<typename Lhs>
+ ResultT operator()(Lhs lhs) const
+ {
+ if (isnan(lhs))
+ return 0;
+
+ if (isinf(lhs)) {
+ if (lhs < 0)
+ return NumericLimits<ResultT>::min();
+ return NumericLimits<ResultT>::max();
+ }
+
+ constexpr auto convert = [](auto truncated_value) {
+ if (truncated_value < NumericLimits<ResultT>::min())
+ return NumericLimits<ResultT>::min();
+ if (static_cast<double>(truncated_value) > static_cast<double>(NumericLimits<ResultT>::max()))
+ return NumericLimits<ResultT>::max();
+ return static_cast<ResultT>(truncated_value);
+ };
+
+ if constexpr (IsSame<Lhs, float>)
+ return convert(truncf(lhs));
+ else
+ return convert(trunc(lhs));
+ }
+
+ static StringView name() { return "truncate.saturating"; }
+};
+
+}