1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-10 09:47:35 +00:00

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.
This commit is contained in:
Ali Mohammad Pur 2021-08-09 02:55:01 +04:30 committed by Andreas Kling
parent 799471d16f
commit 563b402f04
5 changed files with 662 additions and 357 deletions

View file

@ -8,10 +8,9 @@
#include <LibWasm/AbstractMachine/AbstractMachine.h> #include <LibWasm/AbstractMachine/AbstractMachine.h>
#include <LibWasm/AbstractMachine/BytecodeInterpreter.h> #include <LibWasm/AbstractMachine/BytecodeInterpreter.h>
#include <LibWasm/AbstractMachine/Configuration.h> #include <LibWasm/AbstractMachine/Configuration.h>
#include <LibWasm/AbstractMachine/Operators.h>
#include <LibWasm/Opcode.h> #include <LibWasm/Opcode.h>
#include <LibWasm/Printer/Printer.h> #include <LibWasm/Printer/Printer.h>
#include <limits.h>
#include <math.h>
namespace Wasm { namespace Wasm {
@ -168,79 +167,58 @@ void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAdd
configuration.stack().entries().unchecked_append(move(entry)); configuration.stack().entries().unchecked_append(move(entry));
} }
#define BINARY_NUMERIC_OPERATION(type, operator, cast, ...) \ template<typename PopType, typename PushType, typename Operator>
do { \ void BytecodeInterpreter::binary_numeric_operation(Configuration& configuration)
TRAP_IF_NOT(!configuration.stack().is_empty()); \ {
auto rhs_entry = configuration.stack().pop(); \ TRAP_IF_NOT(!configuration.stack().is_empty());
auto& lhs_entry = configuration.stack().peek(); \ auto rhs_entry = configuration.stack().pop();
TRAP_IF_NOT(rhs_entry.has<Value>()); \ auto& lhs_entry = configuration.stack().peek();
TRAP_IF_NOT(lhs_entry.has<Value>()); \ auto rhs_ptr = rhs_entry.get_pointer<Value>();
auto rhs = rhs_entry.get<Value>().to<type>(); \ auto lhs_ptr = lhs_entry.get_pointer<Value>();
auto lhs = lhs_entry.get<Value>().to<type>(); \ TRAP_IF_NOT(rhs_ptr);
TRAP_IF_NOT(lhs.has_value()); \ TRAP_IF_NOT(lhs_ptr);
TRAP_IF_NOT(rhs.has_value()); \ auto rhs = rhs_ptr->to<PopType>();
__VA_ARGS__; \ auto lhs = lhs_ptr->to<PopType>();
auto result = lhs.value() operator rhs.value(); \ TRAP_IF_NOT(lhs.has_value());
dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = {}", lhs.value(), #operator, rhs.value(), result); \ TRAP_IF_NOT(rhs.has_value());
configuration.stack().peek() = Value(cast(result)); \ PushType result;
return; \ auto call_result = Operator {}(lhs.value(), rhs.value());
} while (false) 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 OVF_CHECKED_BINARY_NUMERIC_OPERATION(type, operator, cast, ...) \ template<typename PopType, typename PushType, typename Operator>
do { \ void BytecodeInterpreter::unary_operation(Configuration& configuration)
TRAP_IF_NOT(!configuration.stack().is_empty()); \ {
auto rhs_entry = configuration.stack().pop(); \ TRAP_IF_NOT(!configuration.stack().is_empty());
auto& lhs_entry = configuration.stack().peek(); \ auto& entry = configuration.stack().peek();
TRAP_IF_NOT(rhs_entry.has<Value>()); \ auto entry_ptr = entry.get_pointer<Value>();
TRAP_IF_NOT(lhs_entry.has<Value>()); \ TRAP_IF_NOT(entry_ptr);
auto rhs = rhs_entry.get<Value>().to<type>(); \ auto value = entry_ptr->to<PopType>();
auto ulhs = lhs_entry.get<Value>().to<type>(); \ TRAP_IF_NOT(value.has_value());
TRAP_IF_NOT(ulhs.has_value()); \ auto call_result = Operator {}(*value);
TRAP_IF_NOT(rhs.has_value()); \ PushType result;
dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = ??", ulhs.value(), #operator, rhs.value()); \ if constexpr (IsSpecializationOf<decltype(call_result), AK::Result>) {
__VA_ARGS__; \ if (call_result.is_error()) {
Checked<type> lhs = ulhs.value(); \ trap_if_not(false, call_result.error());
lhs operator##= rhs.value(); \ return;
TRAP_IF_NOT(!lhs.has_overflow()); \ }
auto result = lhs.value(); \ result = call_result.release_value();
dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = {}", ulhs.value(), #operator, rhs.value(), result); \ } else {
configuration.stack().peek() = Value(cast(result)); \ result = call_result;
return; \ }
} while (false) dbgln_if(WASM_TRACE_DEBUG, "map({}) {} = {}", Operator::name(), *value, result);
configuration.stack().peek() = Value(result);
#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)
#define UNARY_NUMERIC_OPERATION(type, operation) \
UNARY_MAP(type, operation, type)
#define LOAD_AND_PUSH(read_type, push_type) \ #define LOAD_AND_PUSH(read_type, push_type) \
do { \ do { \
@ -387,115 +365,6 @@ Vector<Value> BytecodeInterpreter::pop_values(Configuration& configuration, size
return results; 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) 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()); 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_end.value():
case Instructions::structured_else.value(): { case Instructions::structured_else.value(): {
auto label = configuration.nth_label(0); auto index = configuration.nth_label_index(0);
TRAP_IF_NOT(label.has_value()); TRAP_IF_NOT(index.has_value());
size_t end = configuration.stack().size() - label->arity() - 1; auto label = configuration.stack().entries()[*index].get<Label>();
size_t start = end; configuration.stack().entries().remove(*index, 1);
while (start > 0 && start < configuration.stack().size() && !configuration.stack().entries()[start].has<Label>())
--start;
configuration.stack().entries().remove(start, end - start + 1);
if (instruction.opcode() == Instructions::structured_end) if (instruction.opcode() == Instructions::structured_end)
return; return;
// Jump to the end label // Jump to the end label
configuration.ip() = label->continuation(); configuration.ip() = label.continuation();
return; return;
} }
case Instructions::return_.value(): { case Instructions::return_.value(): {
@ -811,293 +676,277 @@ void BytecodeInterpreter::interpret(Configuration& configuration, InstructionPoi
return; return;
} }
case Instructions::i32_eqz.value(): case Instructions::i32_eqz.value():
UNARY_NUMERIC_OPERATION(i32, 0 ==); return unary_operation<i32, i32, Operators::EqualsZero>(configuration);
case Instructions::i32_eq.value(): case Instructions::i32_eq.value():
BINARY_NUMERIC_OPERATION(i32, ==, i32); return binary_numeric_operation<i32, i32, Operators::Equals>(configuration);
case Instructions::i32_ne.value(): case Instructions::i32_ne.value():
BINARY_NUMERIC_OPERATION(i32, !=, i32); return binary_numeric_operation<i32, i32, Operators::NotEquals>(configuration);
case Instructions::i32_lts.value(): case Instructions::i32_lts.value():
BINARY_NUMERIC_OPERATION(i32, <, i32); return binary_numeric_operation<i32, i32, Operators::LessThan>(configuration);
case Instructions::i32_ltu.value(): case Instructions::i32_ltu.value():
BINARY_NUMERIC_OPERATION(u32, <, i32); return binary_numeric_operation<u32, i32, Operators::LessThan>(configuration);
case Instructions::i32_gts.value(): case Instructions::i32_gts.value():
BINARY_NUMERIC_OPERATION(i32, >, i32); return binary_numeric_operation<i32, i32, Operators::GreaterThan>(configuration);
case Instructions::i32_gtu.value(): case Instructions::i32_gtu.value():
BINARY_NUMERIC_OPERATION(u32, >, i32); return binary_numeric_operation<u32, i32, Operators::GreaterThan>(configuration);
case Instructions::i32_les.value(): case Instructions::i32_les.value():
BINARY_NUMERIC_OPERATION(i32, <=, i32); return binary_numeric_operation<i32, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::i32_leu.value(): case Instructions::i32_leu.value():
BINARY_NUMERIC_OPERATION(u32, <=, i32); return binary_numeric_operation<u32, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::i32_ges.value(): case Instructions::i32_ges.value():
BINARY_NUMERIC_OPERATION(i32, >=, i32); return binary_numeric_operation<i32, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::i32_geu.value(): case Instructions::i32_geu.value():
BINARY_NUMERIC_OPERATION(u32, >=, i32); return binary_numeric_operation<u32, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::i64_eqz.value(): case Instructions::i64_eqz.value():
UNARY_NUMERIC_OPERATION(i64, 0ull ==); return unary_operation<i64, i32, Operators::EqualsZero>(configuration);
case Instructions::i64_eq.value(): case Instructions::i64_eq.value():
BINARY_NUMERIC_OPERATION(i64, ==, i32); return binary_numeric_operation<i64, i32, Operators::Equals>(configuration);
case Instructions::i64_ne.value(): case Instructions::i64_ne.value():
BINARY_NUMERIC_OPERATION(i64, !=, i32); return binary_numeric_operation<i64, i32, Operators::NotEquals>(configuration);
case Instructions::i64_lts.value(): case Instructions::i64_lts.value():
BINARY_NUMERIC_OPERATION(i64, <, i32); return binary_numeric_operation<i64, i32, Operators::LessThan>(configuration);
case Instructions::i64_ltu.value(): case Instructions::i64_ltu.value():
BINARY_NUMERIC_OPERATION(u64, <, i32); return binary_numeric_operation<u64, i32, Operators::LessThan>(configuration);
case Instructions::i64_gts.value(): case Instructions::i64_gts.value():
BINARY_NUMERIC_OPERATION(i64, >, i32); return binary_numeric_operation<i64, i32, Operators::GreaterThan>(configuration);
case Instructions::i64_gtu.value(): case Instructions::i64_gtu.value():
BINARY_NUMERIC_OPERATION(u64, >, i32); return binary_numeric_operation<u64, i32, Operators::GreaterThan>(configuration);
case Instructions::i64_les.value(): case Instructions::i64_les.value():
BINARY_NUMERIC_OPERATION(i64, <=, i32); return binary_numeric_operation<i64, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::i64_leu.value(): case Instructions::i64_leu.value():
BINARY_NUMERIC_OPERATION(u64, <=, i32); return binary_numeric_operation<u64, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::i64_ges.value(): case Instructions::i64_ges.value():
BINARY_NUMERIC_OPERATION(i64, >=, i32); return binary_numeric_operation<i64, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::i64_geu.value(): case Instructions::i64_geu.value():
BINARY_NUMERIC_OPERATION(u64, >=, i32); return binary_numeric_operation<u64, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::f32_eq.value(): case Instructions::f32_eq.value():
BINARY_NUMERIC_OPERATION(float, ==, i32); return binary_numeric_operation<float, i32, Operators::Equals>(configuration);
case Instructions::f32_ne.value(): case Instructions::f32_ne.value():
BINARY_NUMERIC_OPERATION(float, !=, i32); return binary_numeric_operation<float, i32, Operators::NotEquals>(configuration);
case Instructions::f32_lt.value(): case Instructions::f32_lt.value():
BINARY_NUMERIC_OPERATION(float, <, i32); return binary_numeric_operation<float, i32, Operators::LessThan>(configuration);
case Instructions::f32_gt.value(): case Instructions::f32_gt.value():
BINARY_NUMERIC_OPERATION(float, >, i32); return binary_numeric_operation<float, i32, Operators::GreaterThan>(configuration);
case Instructions::f32_le.value(): case Instructions::f32_le.value():
BINARY_NUMERIC_OPERATION(float, <=, i32); return binary_numeric_operation<float, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::f32_ge.value(): case Instructions::f32_ge.value():
BINARY_NUMERIC_OPERATION(float, >=, i32); return binary_numeric_operation<float, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::f64_eq.value(): case Instructions::f64_eq.value():
BINARY_NUMERIC_OPERATION(double, ==, i32); return binary_numeric_operation<double, i32, Operators::Equals>(configuration);
case Instructions::f64_ne.value(): case Instructions::f64_ne.value():
BINARY_NUMERIC_OPERATION(double, !=, i32); return binary_numeric_operation<double, i32, Operators::NotEquals>(configuration);
case Instructions::f64_lt.value(): case Instructions::f64_lt.value():
BINARY_NUMERIC_OPERATION(double, <, i32); return binary_numeric_operation<double, i32, Operators::LessThan>(configuration);
case Instructions::f64_gt.value(): case Instructions::f64_gt.value():
BINARY_NUMERIC_OPERATION(double, >, i32); return binary_numeric_operation<double, i32, Operators::GreaterThan>(configuration);
case Instructions::f64_le.value(): case Instructions::f64_le.value():
BINARY_NUMERIC_OPERATION(double, <=, i32); return binary_numeric_operation<double, i32, Operators::LessThanOrEquals>(configuration);
case Instructions::f64_ge.value(): case Instructions::f64_ge.value():
BINARY_NUMERIC_OPERATION(double, >, i32); return binary_numeric_operation<double, i32, Operators::GreaterThanOrEquals>(configuration);
case Instructions::i32_clz.value(): case Instructions::i32_clz.value():
UNARY_NUMERIC_OPERATION(i32, clz); return unary_operation<i32, i32, Operators::CountLeadingZeros>(configuration);
case Instructions::i32_ctz.value(): case Instructions::i32_ctz.value():
UNARY_NUMERIC_OPERATION(i32, ctz); return unary_operation<i32, i32, Operators::CountTrailingZeros>(configuration);
case Instructions::i32_popcnt.value(): case Instructions::i32_popcnt.value():
UNARY_NUMERIC_OPERATION(i32, __builtin_popcount); return unary_operation<i32, i32, Operators::PopCount>(configuration);
case Instructions::i32_add.value(): case Instructions::i32_add.value():
BINARY_NUMERIC_OPERATION(u32, +, i32); return binary_numeric_operation<u32, i32, Operators::Add>(configuration);
case Instructions::i32_sub.value(): case Instructions::i32_sub.value():
BINARY_NUMERIC_OPERATION(u32, -, i32); return binary_numeric_operation<u32, i32, Operators::Subtract>(configuration);
case Instructions::i32_mul.value(): case Instructions::i32_mul.value():
BINARY_NUMERIC_OPERATION(u32, *, i32); return binary_numeric_operation<u32, i32, Operators::Multiply>(configuration);
case Instructions::i32_divs.value(): 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(): 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(): 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(): 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(): case Instructions::i32_and.value():
BINARY_NUMERIC_OPERATION(i32, &, i32); return binary_numeric_operation<i32, i32, Operators::BitAnd>(configuration);
case Instructions::i32_or.value(): case Instructions::i32_or.value():
BINARY_NUMERIC_OPERATION(i32, |, i32); return binary_numeric_operation<i32, i32, Operators::BitOr>(configuration);
case Instructions::i32_xor.value(): case Instructions::i32_xor.value():
BINARY_NUMERIC_OPERATION(i32, ^, i32); return binary_numeric_operation<i32, i32, Operators::BitXor>(configuration);
case Instructions::i32_shl.value(): 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(): 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(): 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(): 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(): 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(): case Instructions::i64_clz.value():
UNARY_NUMERIC_OPERATION(i64, clz); return unary_operation<i64, i64, Operators::CountLeadingZeros>(configuration);
case Instructions::i64_ctz.value(): case Instructions::i64_ctz.value():
UNARY_NUMERIC_OPERATION(i64, ctz); return unary_operation<i64, i64, Operators::CountTrailingZeros>(configuration);
case Instructions::i64_popcnt.value(): case Instructions::i64_popcnt.value():
UNARY_NUMERIC_OPERATION(i64, __builtin_popcountll); return unary_operation<i64, i64, Operators::PopCount>(configuration);
case Instructions::i64_add.value(): case Instructions::i64_add.value():
BINARY_NUMERIC_OPERATION(u64, +, i64); return binary_numeric_operation<u64, i64, Operators::Add>(configuration);
case Instructions::i64_sub.value(): case Instructions::i64_sub.value():
BINARY_NUMERIC_OPERATION(u64, -, i64); return binary_numeric_operation<u64, i64, Operators::Subtract>(configuration);
case Instructions::i64_mul.value(): case Instructions::i64_mul.value():
BINARY_NUMERIC_OPERATION(u64, *, i64); return binary_numeric_operation<u64, i64, Operators::Multiply>(configuration);
case Instructions::i64_divs.value(): 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(): 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(): 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(): 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(): case Instructions::i64_and.value():
BINARY_NUMERIC_OPERATION(i64, &, i64); return binary_numeric_operation<i64, i64, Operators::BitAnd>(configuration);
case Instructions::i64_or.value(): case Instructions::i64_or.value():
BINARY_NUMERIC_OPERATION(i64, |, i64); return binary_numeric_operation<i64, i64, Operators::BitOr>(configuration);
case Instructions::i64_xor.value(): case Instructions::i64_xor.value():
BINARY_NUMERIC_OPERATION(i64, ^, i64); return binary_numeric_operation<i64, i64, Operators::BitXor>(configuration);
case Instructions::i64_shl.value(): 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(): 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(): 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(): 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(): 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(): case Instructions::f32_abs.value():
UNARY_NUMERIC_OPERATION(float, fabsf); return unary_operation<float, float, Operators::Absolute>(configuration);
case Instructions::f32_neg.value(): case Instructions::f32_neg.value():
UNARY_NUMERIC_OPERATION(float, -); return unary_operation<float, float, Operators::Negate>(configuration);
case Instructions::f32_ceil.value(): case Instructions::f32_ceil.value():
UNARY_NUMERIC_OPERATION(float, ceilf); return unary_operation<float, float, Operators::Ceil>(configuration);
case Instructions::f32_floor.value(): case Instructions::f32_floor.value():
UNARY_NUMERIC_OPERATION(float, floorf); return unary_operation<float, float, Operators::Floor>(configuration);
case Instructions::f32_trunc.value(): case Instructions::f32_trunc.value():
UNARY_NUMERIC_OPERATION(float, truncf); return unary_operation<float, float, Operators::Truncate>(configuration);
case Instructions::f32_nearest.value(): case Instructions::f32_nearest.value():
UNARY_NUMERIC_OPERATION(float, roundf); return unary_operation<float, float, Operators::Round>(configuration);
case Instructions::f32_sqrt.value(): case Instructions::f32_sqrt.value():
UNARY_NUMERIC_OPERATION(float, sqrtf); return unary_operation<float, float, Operators::SquareRoot>(configuration);
case Instructions::f32_add.value(): case Instructions::f32_add.value():
BINARY_NUMERIC_OPERATION(float, +, float); return binary_numeric_operation<float, float, Operators::Add>(configuration);
case Instructions::f32_sub.value(): case Instructions::f32_sub.value():
BINARY_NUMERIC_OPERATION(float, -, float); return binary_numeric_operation<float, float, Operators::Subtract>(configuration);
case Instructions::f32_mul.value(): case Instructions::f32_mul.value():
BINARY_NUMERIC_OPERATION(float, *, float); return binary_numeric_operation<float, float, Operators::Multiply>(configuration);
case Instructions::f32_div.value(): case Instructions::f32_div.value():
BINARY_NUMERIC_OPERATION(float, /, float); return binary_numeric_operation<float, float, Operators::Divide>(configuration);
case Instructions::f32_min.value(): 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(): 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(): 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(): case Instructions::f64_abs.value():
UNARY_NUMERIC_OPERATION(double, fabs); return unary_operation<double, double, Operators::Absolute>(configuration);
case Instructions::f64_neg.value(): case Instructions::f64_neg.value():
UNARY_NUMERIC_OPERATION(double, -); return unary_operation<double, double, Operators::Negate>(configuration);
case Instructions::f64_ceil.value(): case Instructions::f64_ceil.value():
UNARY_NUMERIC_OPERATION(double, ceil); return unary_operation<double, double, Operators::Ceil>(configuration);
case Instructions::f64_floor.value(): case Instructions::f64_floor.value():
UNARY_NUMERIC_OPERATION(double, floor); return unary_operation<double, double, Operators::Floor>(configuration);
case Instructions::f64_trunc.value(): case Instructions::f64_trunc.value():
UNARY_NUMERIC_OPERATION(double, trunc); return unary_operation<double, double, Operators::Truncate>(configuration);
case Instructions::f64_nearest.value(): case Instructions::f64_nearest.value():
UNARY_NUMERIC_OPERATION(double, round); return unary_operation<double, double, Operators::Round>(configuration);
case Instructions::f64_sqrt.value(): case Instructions::f64_sqrt.value():
UNARY_NUMERIC_OPERATION(double, sqrt); return unary_operation<double, double, Operators::SquareRoot>(configuration);
case Instructions::f64_add.value(): case Instructions::f64_add.value():
BINARY_NUMERIC_OPERATION(double, +, double); return binary_numeric_operation<double, double, Operators::Add>(configuration);
case Instructions::f64_sub.value(): case Instructions::f64_sub.value():
BINARY_NUMERIC_OPERATION(double, -, double); return binary_numeric_operation<double, double, Operators::Subtract>(configuration);
case Instructions::f64_mul.value(): case Instructions::f64_mul.value():
BINARY_NUMERIC_OPERATION(double, *, double); return binary_numeric_operation<double, double, Operators::Multiply>(configuration);
case Instructions::f64_div.value(): case Instructions::f64_div.value():
BINARY_NUMERIC_OPERATION(double, /, double); return binary_numeric_operation<double, double, Operators::Divide>(configuration);
case Instructions::f64_min.value(): 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(): 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(): 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(): case Instructions::i32_wrap_i64.value():
UNARY_MAP(i64, i32, i32); return unary_operation<i64, i32, Operators::Wrap<i32>>(configuration);
case Instructions::i32_trunc_sf32.value(): { case Instructions::i32_trunc_sf32.value():
auto fn = [this](auto& v) { return checked_signed_truncate<float, i32>(v); }; return unary_operation<float, i32, Operators::CheckedTruncate<i32>>(configuration);
UNARY_MAP(float, fn, i32); case Instructions::i32_trunc_uf32.value():
} return unary_operation<float, i32, Operators::CheckedTruncate<u32>>(configuration);
case Instructions::i32_trunc_uf32.value(): { case Instructions::i32_trunc_sf64.value():
auto fn = [this](auto& value) { return checked_unsigned_truncate<float, i32>(value); }; return unary_operation<double, i32, Operators::CheckedTruncate<i32>>(configuration);
UNARY_MAP(float, fn, i32); case Instructions::i32_trunc_uf64.value():
} return unary_operation<double, i32, Operators::CheckedTruncate<u32>>(configuration);
case Instructions::i32_trunc_sf64.value(): { case Instructions::i64_trunc_sf32.value():
auto fn = [this](auto& value) { return checked_signed_truncate<double, i32>(value); }; return unary_operation<float, i64, Operators::CheckedTruncate<i64>>(configuration);
UNARY_MAP(double, fn, i32); case Instructions::i64_trunc_uf32.value():
} return unary_operation<float, i64, Operators::CheckedTruncate<u64>>(configuration);
case Instructions::i32_trunc_uf64.value(): { case Instructions::i64_trunc_sf64.value():
auto fn = [this](auto& value) { return checked_unsigned_truncate<double, i32>(value); }; return unary_operation<double, i64, Operators::CheckedTruncate<i64>>(configuration);
UNARY_MAP(double, fn, i32); case Instructions::i64_trunc_uf64.value():
} return unary_operation<double, i64, Operators::CheckedTruncate<u64>>(configuration);
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);
}
case Instructions::i64_extend_si32.value(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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(): 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::memory_init.value():
case Instructions::data_drop.value(): case Instructions::data_drop.value():
case Instructions::memory_copy.value(): case Instructions::memory_copy.value():
@ -1136,5 +985,4 @@ void DebuggerBytecodeInterpreter::interpret(Configuration& configuration, Instru
} }
} }
} }
} }

View file

@ -40,6 +40,12 @@ protected:
void store_to_memory(Configuration&, Instruction const&, ReadonlyBytes data); void store_to_memory(Configuration&, Instruction const&, ReadonlyBytes data);
void call_address(Configuration&, FunctionAddress); 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> template<typename V, typename T>
MakeUnsigned<T> checked_unsigned_truncate(V); MakeUnsigned<T> checked_unsigned_truncate(V);

View file

@ -10,13 +10,13 @@
namespace Wasm { 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) { for (size_t index = m_stack.size(); index > 0; --index) {
auto& entry = m_stack.entries()[index - 1]; auto& entry = m_stack.entries()[index - 1];
if (auto ptr = entry.get_pointer<Label>()) { if (entry.has<Label>()) {
if (i == 0) if (i == 0)
return *ptr; return index - 1;
--i; --i;
} }
} }

View file

@ -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) void set_frame(Frame&& frame)
{ {
m_current_frame_index = m_stack.size(); m_current_frame_index = m_stack.size();

View file

@ -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"; }
};
}