diff options
Diffstat (limited to 'Userland')
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"; } +}; + +} |