AK+LibX86: Generalize u128/256 to AK::UFixedBigInt

Doing these as custom classes might be faster, especially when writing
them in SSE, but this would cause a lot of Code duplication and due to
the nature of constexprs and the intelligence of the compiler they might
be using SSE/MMX either way
This commit is contained in:
Hendiadyoin1 2021-05-19 23:04:48 +02:00 committed by Ali Mohammad Pur
parent 9270fb1e69
commit 5ffe23e4f3
Notes: sideshowbarker 2024-07-18 12:25:47 +09:00
11 changed files with 905 additions and 665 deletions

771
AK/UFixedBigInt.h Normal file
View file

@ -0,0 +1,771 @@
/*
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Checked.h>
#include <AK/Concepts.h>
#include <AK/Format.h>
#include <AK/NumericLimits.h>
#include <AK/StdLibExtraDetails.h>
#include <AK/StdLibExtras.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
namespace AK {
template<typename T>
requires(sizeof(T) >= sizeof(u64) && IsUnsigned<T>) class UFixedBigInt;
// FIXME: This breaks formatting
// template<typename T>
// constexpr inline bool Detail::IsIntegral<UFixedBigInt<T>> = true;
template<typename T>
constexpr inline bool Detail::IsUnsigned<UFixedBigInt<T>> = true;
template<typename T>
constexpr inline bool Detail::IsSigned<UFixedBigInt<T>> = false;
template<typename T>
struct NumericLimits<UFixedBigInt<T>> {
static constexpr UFixedBigInt<T> min() { return 0; }
static constexpr UFixedBigInt<T> max() { return { NumericLimits<T>::max(), NumericLimits<T>::max() }; }
static constexpr bool is_signed() { return false; }
};
template<typename T>
requires(sizeof(T) >= sizeof(u64) && IsUnsigned<T>) class UFixedBigInt {
public:
using R = UFixedBigInt<T>;
constexpr UFixedBigInt() = default;
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr UFixedBigInt(U low)
: m_low(low)
, m_high(0u)
{
}
template<Unsigned U, Unsigned U2>
requires(sizeof(T) >= sizeof(U) && sizeof(T) >= sizeof(U2)) constexpr UFixedBigInt(U low, U2 high)
: m_low(low)
, m_high(high)
{
}
constexpr T& low()
{
return m_low;
}
constexpr const T& low() const
{
return m_low;
}
constexpr T& high()
{
return m_high;
}
constexpr const T& high() const
{
return m_high;
}
Span<u8> bytes()
{
return Span<u8>(reinterpret_cast<u8*>(this), sizeof(R));
}
Span<const u8> bytes() const
{
return Span<const u8>(reinterpret_cast<const u8*>(this), sizeof(R));
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) explicit operator U() const
{
return static_cast<U>(m_low);
}
// Utils
constexpr size_t clz() const requires(IsSame<T, u64>)
{
if (m_high)
return __builtin_clzll(m_high);
else
return sizeof(T) * 8 + __builtin_clzll(m_low);
}
constexpr size_t clz() const requires(!IsSame<T, u64>)
{
if (m_high)
return m_high.clz();
else
return sizeof(T) * 8 + m_low.clz();
}
constexpr size_t ctz() const requires(IsSame<T, u64>)
{
if (m_low)
return __builtin_ctzll(m_low);
else
return sizeof(T) * 8 + __builtin_ctzll(m_high);
}
constexpr size_t ctz() const requires(!IsSame<T, u64>)
{
if (m_low)
return m_low.ctz();
else
return sizeof(T) * 8 + m_high.ctz();
}
constexpr size_t popcnt() const requires(IsSame<T, u64>)
{
return __builtin_popcntll(m_low) + __builtin_popcntll(m_high);
}
constexpr size_t popcnt() const requires(!IsSame<T, u64>)
{
return m_low.popcnt() + m_high.popcnt();
}
// Comparison Operations
constexpr bool operator!() const
{
return !m_low && !m_high;
}
constexpr explicit operator bool() const
{
return m_low || m_high;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr bool operator==(const T& other) const
{
return !m_high && m_low == other;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr bool operator!=(const T& other) const
{
return m_high || m_low != other;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr bool operator>(const T& other) const
{
return m_high || m_low > other;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr bool operator<(const T& other) const
{
return !m_high && m_low < other;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr bool operator>=(const T& other) const
{
return *this == other || *this > other;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr bool operator<=(const T& other) const
{
return *this == other || *this < other;
}
constexpr bool operator==(const R& other) const
{
return m_low == other.low() && m_high == other.high();
}
constexpr bool operator!=(const R& other) const
{
return m_low != other.low() || m_high != other.high();
}
constexpr bool operator>(const R& other) const
{
return m_high > other.high()
|| (m_high == other.high() && m_low > other.low());
}
constexpr bool operator<(const R& other) const
{
return m_high < other.high()
|| (m_high == other.high() && m_low < other.low());
}
constexpr bool operator>=(const R& other) const
{
return *this == other || *this > other;
}
constexpr bool operator<=(const R& other) const
{
return *this == other || *this < other;
}
// Bitwise operations
constexpr R operator~() const
{
return { ~m_low, ~m_high };
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr U operator&(const U& other) const
{
return static_cast<const U>(m_low) & other;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr R operator|(const U& other) const
{
return { m_low | other, m_high };
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr R operator^(const U& other) const
{
return { m_low ^ other, m_high };
}
template<Unsigned U>
constexpr R operator<<(const U& shift) const
{
if (shift >= sizeof(R) * 8u)
return 0u;
if (shift >= sizeof(T) * 8u)
return R { 0u, m_low << (shift - sizeof(T) * 8u) };
if (!shift)
return *this;
T overflow = m_low >> (sizeof(T) * 8u - shift);
return R { m_low << shift, (m_high << shift) | overflow };
}
template<Unsigned U>
constexpr R operator>>(const U& shift) const
{
if (shift >= sizeof(R) * 8u)
return 0u;
if (shift >= sizeof(T) * 8u)
return m_high >> (shift - sizeof(T) * 8u);
if (!shift)
return *this;
T underflow = m_high << (sizeof(T) * 8u - shift);
return R { (m_low >> shift) | underflow, m_high >> shift };
}
template<Unsigned U>
constexpr R rol(const U& shift) const
{
return (*this >> sizeof(T) * 8u - shift) | (*this << shift);
}
template<Unsigned U>
constexpr R ror(const U& shift) const
{
return (*this << sizeof(T) * 8u - shift) | (*this >> shift);
}
constexpr R operator&(const R& other) const
{
return { m_low & other.low(), m_high & other.high() };
}
constexpr R operator|(const R& other) const
{
return { m_low | other.low(), m_high | other.high() };
}
constexpr R operator^(const R& other) const
{
return { m_low ^ other.low(), m_high ^ other.high() };
}
// Bitwise assignment
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr R& operator&=(const U& other)
{
m_high = 0u;
m_low &= other;
return *this;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr R& operator|=(const U& other)
{
m_low |= other;
return *this;
}
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr R& operator^=(const U& other)
{
m_low ^= other;
return *this;
}
template<Unsigned U>
constexpr R& operator>>=(const U& other)
{
*this = *this >> other;
return *this;
}
template<Unsigned U>
constexpr R& operator<<=(const U& other)
{
*this = *this << other;
return *this;
}
constexpr R& operator&=(const R& other)
{
m_high &= other.high();
m_low &= other.low();
return *this;
}
constexpr R& operator|=(const R& other)
{
m_high |= other.high();
m_low |= other.low();
return *this;
}
constexpr R& operator^=(const R& other)
{
m_high ^= other.high();
m_low ^= other.low();
return *this;
}
// Arithmetics
// implies size of less than u64, so passing references isn't useful
template<Unsigned U>
requires(sizeof(T) >= sizeof(U) && IsSame<T, u64>) constexpr R addc(const U other, bool& carry) const
{
bool low_carry = Checked<T>::addition_would_overflow(m_low, other);
low_carry |= Checked<T>::addition_would_overflow(m_low, carry);
bool high_carry = Checked<T>::addition_would_overflow(m_high, low_carry);
T lower = m_low + other + carry;
T higher = m_high + low_carry;
carry = high_carry;
return {
lower,
higher
};
}
template<Unsigned U>
requires(sizeof(R) > sizeof(U) && sizeof(T) > sizeof(u64)) constexpr R addc(const U& other, bool& carry) const
{
T lower = m_low.addc(other, carry);
T higher = m_high.addc(0u, carry);
return {
lower,
higher
};
}
template<Unsigned U>
requires(IsSame<R, U>&& IsSame<T, u64>) constexpr R addc(const U& other, bool& carry) const
{
bool low_carry = Checked<T>::addition_would_overflow(m_low, other.low());
bool high_carry = Checked<T>::addition_would_overflow(m_high, other.high());
T lower = m_low + other.low();
T higher = m_high + other.high();
low_carry |= Checked<T>::addition_would_overflow(lower, carry);
high_carry |= Checked<T>::addition_would_overflow(higher, low_carry);
lower += carry;
higher += low_carry;
carry = high_carry;
return {
lower,
higher
};
}
template<Unsigned U>
requires(IsSame<R, U> && sizeof(T) > sizeof(u64)) constexpr R addc(const U& other, bool& carry) const
{
T lower = m_low.addc(other.low(), carry);
T higher = m_high.addc(other.high(), carry);
return {
lower,
higher
};
}
template<Unsigned U>
requires(sizeof(R) < sizeof(U)) constexpr U addc(const U& other, bool& carry) const
{
return other.addc(*this, carry);
}
// FIXME: subc for sizeof(T) < sizeof(U)
template<Unsigned U>
requires(sizeof(T) >= sizeof(U)) constexpr R subc(const U& other, bool& carry) const
{
bool low_carry = (!m_low && carry) || (m_low - carry) < other;
bool high_carry = !m_high && low_carry;
T lower = m_low - other - carry;
T higher = m_high - low_carry;
carry = high_carry;
return { lower, higher };
}
constexpr R subc(const R& other, bool& carry) const
{
bool low_carry = (!m_low && carry) || (m_low - carry) < other.low();
bool high_carry = (!m_high && low_carry) || (m_high - low_carry) < other.high();
T lower = m_low - other.low() - carry;
T higher = m_high - other.high() - low_carry;
carry = high_carry;
return { lower, higher };
}
constexpr R operator+(const bool& other) const
{
bool carry = false; // unused
return addc((u8)other, carry);
}
template<Unsigned U>
constexpr R operator+(const U& other) const
{
bool carry = false; // unused
return addc(other, carry);
}
constexpr R operator-(const bool& other) const
{
bool carry = false; // unused
return subc((u8)other, carry);
}
template<Unsigned U>
constexpr R operator-(const U& other) const
{
bool carry = false; // unused
return subc(other, carry);
}
template<Unsigned U>
constexpr R& operator+=(const U& other)
{
*this = *this + other;
return *this;
}
template<Unsigned U>
constexpr R& operator-=(const U& other)
{
*this = *this - other;
return *this;
}
constexpr R operator++()
{
// x++
auto old = *this;
*this += 1;
return old;
}
constexpr R& operator++(int)
{
// ++x
*this += 1;
return *this;
}
constexpr R operator--()
{
// x--
auto old = *this;
*this -= 1;
return old;
}
constexpr R& operator--(int)
{
// --x
*this -= 1;
return *this;
}
// FIXME: no restraints on this
template<Unsigned U>
requires(sizeof(R) >= sizeof(U)) constexpr R div_mod(const U& divisor, U& remainder) const
{
// FIXME: Is there a better way to raise a division by 0?
// Maybe as a compiletime warning?
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdiv-by-zero"
if (!divisor) {
volatile int x = 1;
volatile int y = 0;
[[maybe_unused]] volatile int z = x / y;
}
#pragma GCC diagnostic pop
// fastpaths
if (*this < divisor) {
remainder = static_cast<U>(*this);
return 0u;
}
if (*this == divisor) {
remainder = 0u;
return 1u;
}
if (divisor == 1u) {
remainder = 0u;
return *this;
}
remainder = 0u;
R quotient = 0u;
for (ssize_t i = sizeof(R) * 8 - clz() - 1; i >= 0; --i) {
remainder <<= 1u;
remainder |= (*this >> (size_t)i) & 1u;
if (remainder >= divisor) {
remainder -= divisor;
quotient |= R { 1u } << (size_t)i;
}
}
return quotient;
}
template<Unsigned U>
constexpr R operator*(U other) const
{
R res = 0u;
R that = *this;
for (; other != 0u; other >>= 1u) {
if (other & 1u)
res += that;
that <<= 1u;
}
return res;
}
template<Unsigned U>
constexpr R operator/(const U& other) const
{
U mod { 0u }; // unused
return div_mod(other, mod);
}
template<Unsigned U>
constexpr U operator%(const U& other) const
{
R res { 0u };
div_mod(other, res);
return res;
}
template<Unsigned U>
constexpr R& operator*=(const U& other)
{
*this = *this * other;
return *this;
}
template<Unsigned U>
constexpr R& operator/=(const U& other)
{
*this = *this / other;
return *this;
}
template<Unsigned U>
constexpr R& operator%=(const U& other)
{
*this = *this % other;
return *this;
}
constexpr R sqrt() const
{
// Bitwise method: https://en.wikipedia.org/wiki/Integer_square_root#Using_bitwise_operations
// the bitwise method seems to be way faster then Newtons:
// https://quick-bench.com/q/eXZwW1DVhZxLE0llumeCXkfOK3Q
if (*this == 1u)
return 1u;
ssize_t shift = (sizeof(R) * 8 - clz()) & ~1ULL;
// should be equivalent to:
// long shift = 2;
// while ((val >> shift) != 0)
// shift += 2;
R res = 0u;
while (shift >= 0) {
res = res << 1u;
R large_cand = (res | 1u);
if (*this >> (size_t)shift >= large_cand * large_cand)
res = large_cand;
shift -= 2;
}
return res;
}
constexpr R pow(u64 exp)
{
// Montgomery's Ladder Technique
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Montgomery's_ladder_technique
R x1 = *this;
R x2 = *this * *this;
u64 exp_copy = exp;
for (ssize_t i = sizeof(u64) * 8 - __builtin_clzll(exp) - 2; i >= 0; --i) {
if (exp_copy & 1u) {
x2 *= x1;
x1 *= x1;
} else {
x1 *= x2;
x2 *= x2;
}
exp_copy >>= 1u;
}
return x1;
}
template<Unsigned U>
requires(sizeof(U) > sizeof(u64)) constexpr R pow(U exp)
{
// Montgomery's Ladder Technique
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Montgomery's_ladder_technique
R x1 = *this;
R x2 = *this * *this;
U exp_copy = exp;
for (ssize_t i = sizeof(U) * 8 - exp().clz() - 2; i >= 0; --i) {
if (exp_copy & 1u) {
x2 *= x1;
x1 *= x1;
} else {
x1 *= x2;
x2 *= x2;
}
exp_copy >>= 1u;
}
return x1;
}
template<Unsigned U>
constexpr U pow_mod(u64 exp, U mod)
{
// Left to right binary method:
// https://en.wikipedia.org/wiki/Modular_exponentiation#Left-to-right_binary_method
// FIXME: this is not sidechanel proof
if (!mod)
return 0u;
U res = 1;
u64 exp_copy = exp;
for (size_t i = sizeof(u64) - __builtin_clzll(exp) - 1u; i < exp; ++i) {
res *= res;
res %= mod;
if (exp_copy & 1u) {
res = (*this * res) % mod;
}
exp_copy >>= 1u;
}
return res;
}
template<Unsigned ExpT, Unsigned U>
requires(sizeof(ExpT) > sizeof(u64)) constexpr U pow_mod(ExpT exp, U mod)
{
// Left to right binary method:
// https://en.wikipedia.org/wiki/Modular_exponentiation#Left-to-right_binary_method
// FIXME: this is not side channel proof
if (!mod)
return 0u;
U res = 1;
ExpT exp_copy = exp;
for (size_t i = sizeof(ExpT) - exp.clz() - 1u; i < exp; ++i) {
res *= res;
res %= mod;
if (exp_copy & 1u) {
res = (*this * res) % mod;
}
exp_copy >>= 1u;
}
return res;
}
constexpr size_t log2()
{
// FIXME: propper rounding
return sizeof(R) - clz();
}
constexpr size_t logn(u64 base)
{
// FIXME: propper rounding
return log2() / (sizeof(u64) - __builtin_clzll(base));
}
template<Unsigned U>
requires(sizeof(U) > sizeof(u64)) constexpr size_t logn(U base)
{
// FIXME: propper rounding
return log2() / base.log2();
}
private:
T m_low;
T m_high;
};
// reverse operators
template<Unsigned U, Unsigned T>
requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator<(const U a, const UFixedBigInt<T>& b) { return b >= a; }
template<Unsigned U, Unsigned T>
requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator>(const U a, const UFixedBigInt<T>& b) { return b <= a; }
template<Unsigned U, Unsigned T>
requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator<=(const U a, const UFixedBigInt<T>& b) { return b > a; }
template<Unsigned U, Unsigned T>
requires(sizeof(U) < sizeof(T) * 2) constexpr bool operator>=(const U a, const UFixedBigInt<T>& b) { return b < a; }
template<Unsigned T>
struct Formatter<UFixedBigInt<T>> : StandardFormatter {
Formatter() = default;
explicit Formatter(StandardFormatter formatter)
: StandardFormatter(formatter)
{
}
void format(FormatBuilder& builder, UFixedBigInt<T> value)
{
if (m_precision.has_value())
VERIFY_NOT_REACHED();
if (m_mode == Mode::Pointer) {
// these are way to big for a pointer
VERIFY_NOT_REACHED();
}
if (m_mode == Mode::Default)
m_mode = Mode::Hexadecimal;
if (!value.high()) {
Formatter<T> formatter { *this };
return formatter.format(builder, value.low());
}
u8 base = 0;
if (m_mode == Mode::Binary) {
base = 2;
} else if (m_mode == Mode::BinaryUppercase) {
base = 2;
} else if (m_mode == Mode::Octal) {
TODO();
} else if (m_mode == Mode::Decimal) {
TODO();
} else if (m_mode == Mode::Hexadecimal) {
base = 16;
} else if (m_mode == Mode::HexadecimalUppercase) {
base = 16;
} else {
VERIFY_NOT_REACHED();
}
ssize_t width = m_width.value_or(0);
ssize_t lower_length = ceil_div(sizeof(T) * 8, (ssize_t)base);
Formatter<T> formatter { *this };
formatter.m_width = max(width - lower_length, (ssize_t)0);
formatter.format(builder, value.high());
builder.put_literal("'"sv);
formatter.m_zero_pad = true;
formatter.m_alternative_form = false;
formatter.m_width = lower_length;
formatter.format(builder, value.low());
}
};
}
// Nit: Doing these as custom classes might be faster, especially when writing
// then in SSE, but this would cause a lot of Code duplication and due to
// the nature of constexprs and the intelligence of the compiler they might
// be using SSE/MMX either way
// these sizes should suffice for most usecases
using u128 = AK::UFixedBigInt<u64>;
using u256 = AK::UFixedBigInt<u128>;
using u512 = AK::UFixedBigInt<u256>;
using u1024 = AK::UFixedBigInt<u512>;
using u2048 = AK::UFixedBigInt<u1024>;
using u4096 = AK::UFixedBigInt<u2048>;

View file

@ -58,6 +58,7 @@ set(AK_TEST_SOURCES
TestTuple.cpp
TestTypeTraits.cpp
TestTypedTransfer.cpp
TestUFixedBigInt.cpp
TestURL.cpp
TestUtf8.cpp
TestVariant.cpp

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibTest/TestCase.h>
#include <AK/Format.h>
#include <AK/NumericLimits.h>
#include <AK/Random.h>
#include <AK/UFixedBigInt.h>
#include <time.h>
constexpr int test_iterations = 32;
TEST_CASE(one_plus_one)
{
u256 a = 1u;
u256 b = 1u;
EXPECT_EQ(a + b, u256(2u));
}
TEST_CASE(identities)
{
srand(0);
for (int i = 0; i < test_iterations; ++i) {
auto x = get_random<u256>();
if ((x >> 255u) & 1u) {
// ignore numbers that could overflow
--i;
continue;
}
EXPECT_EQ((x << 0u), x);
EXPECT_EQ((x >> 0u), x);
EXPECT_EQ((x / 1u), x);
EXPECT_EQ((x % (x + 1u)), x);
EXPECT_EQ((x << 1u) >> 1u, x);
EXPECT_EQ((x * 2u) / 2u, x);
EXPECT_EQ((x + 2u) - 2u, x);
}
}
TEST_CASE(sqrt)
{
srand(0);
for (int i = 0; i < test_iterations; ++i) {
u256 x = get_random<u128>();
EXPECT_EQ((x * x).sqrt(), x);
}
}
TEST_CASE(add_overflow_propagation)
{
u256 a = NumericLimits<u128>::max();
u256 b = a + a;
u256 c = a * 2u;
EXPECT_EQ(b.low(), NumericLimits<u128>::max() - 1u);
EXPECT_EQ(b.high(), 1u);
EXPECT_EQ(b, a << 1u);
EXPECT_EQ(b, c);
}
TEST_CASE(simple_multiplication)
{
srand(0);
for (int i = 0; i < test_iterations; ++i) {
u256 a = get_random<u256>();
EXPECT_EQ(a * 0u, 0u);
EXPECT_EQ(a * 1u, a);
EXPECT_EQ(a >> 1u, a / 2u);
if (!(a >> 255u & 1u)) {
EXPECT_EQ(a << 1u, a * 2u);
}
}
}
TEST_CASE(div_mod)
{
srand(0);
for (int i = 0; i < test_iterations; ++i) {
u256 a = get_random<u256>();
u256 b = get_random<u256>();
u256 mod;
u256 div = a.div_mod(b, mod);
EXPECT_EQ(a, div * b + mod);
}
}
TEST_CASE(mod_hardcoded)
{
EXPECT_EQ(u256(u128 { 0x8a4b08d32f8b8e48ULL, 0x8459322f67b8e26dULL }, u128 { 0xeea82af4312d1931ULL, 0x654fb5cfe82dbd58ULL }) % u256(u128 { 0x40a58652868d5d66ULL, 0x81d674bf7d6d6861ULL }, u128 { 0xa8314900e6188a82ULL, 0xc273ca947237b4aaULL }), u256(u128 { 0x8a4b08d32f8b8e48ULL, 0x8459322f67b8e26dULL }, u128 { 0xeea82af4312d1931ULL, 0x654fb5cfe82dbd58ULL }));
EXPECT_EQ(u256(u128 { 0xda06d295caa75a3bULL, 0xe3ae0d460049948eULL }, u128 { 0x9a89d29a0325f27fULL, 0x1c8d90ebadec5607ULL }) % u256(u128 { 0x38bd4d49ff59fdf8ULL, 0xcba9acf09110de14ULL }, u128 { 0x51a376c68c4702feULL, 0x0d1b59dec8d2338bULL }), u256(u128 { 0x688c3801cbf35e4bULL, 0x4c5ab364de27d866ULL }, u128 { 0xf742e50cea97ec82ULL, 0x0256dd2e1c47eef0ULL }));
EXPECT_EQ(u256(u128 { 0xdfb56d42706bdb28ULL, 0x6c3bd5ea790c7ef5ULL }, u128 { 0xfebec271d7c757baULL, 0x7dbd745d56bc9e0eULL }) % u256(u128 { 0x30a309a58aed2c01ULL, 0x64d58c8b485c113dULL }, u128 { 0xfa01f558732e9b78ULL, 0x5862b502ebb2dbe9ULL }), u256(u128 { 0xaf12639ce57eaf27ULL, 0x0766495f30b06db8ULL }, u128 { 0x04bccd196498bc42ULL, 0x255abf5a6b09c225ULL }));
EXPECT_EQ(u256(u128 { 0x0a8473d84131f420ULL, 0x0471632bb018c1a2ULL }, u128 { 0x22865980ccd1014fULL, 0xcade79df2adf8fdfULL }) % u256(u128 { 0xd7da811f35db7de0ULL, 0x4e3d98062eae954fULL }, u128 { 0x23946cd23d470d7eULL, 0x6645d41afdc1f2e8ULL }), u256(u128 { 0x32a9f2b90b567640ULL, 0xb633cb25816a2c52ULL }, u128 { 0xfef1ecae8f89f3d0ULL, 0x6498a5c42d1d9cf6ULL }));
EXPECT_EQ(u256(u128 { 0x68636d8d1b7ac40bULL, 0xcb04084ddc684d42ULL }, u128 { 0xaa43c0f6e4e0178cULL, 0x49edae817f27c32aULL }) % u256(u128 { 0xbcc52d96070b7046ULL, 0x2f8255f3c6f8d4bdULL }, u128 { 0x2423bb472eced919ULL, 0x2ed9534c1570b7faULL }), u256(u128 { 0xab9e3ff7146f53c5ULL, 0x9b81b25a156f7884ULL }, u128 { 0x862005afb6113e73ULL, 0x1b145b3569b70b30ULL }));
EXPECT_EQ(u256(u128 { 0xad34ce382cd00226ULL, 0x39b1986d56a064afULL }, u128 { 0xa9410bbd86d9ab21ULL, 0x0fb980a5a7d4b99fULL }) % u256(u128 { 0xa7561893be8cd299ULL, 0x9c3cb9184f45878aULL }, u128 { 0x1e066270a27414efULL, 0xe0fbaa0b739890b8ULL }), u256(u128 { 0xad34ce382cd00226ULL, 0x39b1986d56a064afULL }, u128 { 0xa9410bbd86d9ab21ULL, 0x0fb980a5a7d4b99fULL }));
EXPECT_EQ(u256(u128 { 0x69a0ab23d9f81040ULL, 0xf509000f44fcadb3ULL }, u128 { 0x544310cc56ea051aULL, 0x968a003529f513c0ULL }) % u256(u128 { 0xd6db169628ba28edULL, 0xcf2417c98b765531ULL }, u128 { 0x27865ebfca2d945aULL, 0xcbd1257363cb86a1ULL }), u256(u128 { 0x69a0ab23d9f81040ULL, 0xf509000f44fcadb3ULL }, u128 { 0x544310cc56ea051aULL, 0x968a003529f513c0ULL }));
EXPECT_EQ(u256(u128 { 0x5d41bcd96e47dfbdULL, 0x623a7c82c903789bULL }, u128 { 0x57c3723bfcfd7eeeULL, 0x8b1f21a0739fa6a8ULL }) % u256(u128 { 0xf918e7d73771d5c4ULL, 0xdd40e701852f4d68ULL }, u128 { 0x7c4ac424e3836a4dULL, 0xcb7a0bcc58701175ULL }), u256(u128 { 0x5d41bcd96e47dfbdULL, 0x623a7c82c903789bULL }, u128 { 0x57c3723bfcfd7eeeULL, 0x8b1f21a0739fa6a8ULL }));
EXPECT_EQ(u256(u128 { 0xa4394401788e848aULL, 0x8a907db529ba2943ULL }, u128 { 0x4f3c13b9058d17d3ULL, 0xf17f01b5c1898104ULL }) % u256(u128 { 0x214097598f92cebeULL, 0x723b873f1f879305ULL }, u128 { 0x5f9352861d92ff91ULL, 0x527c65978f7d12ebULL }), u256(u128 { 0x61b8154e5968e70eULL, 0xa6196f36eaab0339ULL }, u128 { 0x90156eacca6718b0ULL, 0x4c863686a28f5b2dULL }));
EXPECT_EQ(u256(u128 { 0x324e46a2bd4d9c0dULL, 0xfb8980a6353814a8ULL }, u128 { 0x3605ef999901dc37ULL, 0xcc2493941c934b83ULL }) % u256(u128 { 0x45e1b8552ccd49b1ULL, 0xe61bd62768189e42ULL }, u128 { 0x859e83ed2f92c211ULL, 0xc7713b3893031cbdULL }), u256(u128 { 0xec6c8e4d9080525cULL, 0x156daa7ecd1f7665ULL }, u128 { 0xb0676bac696f1a26ULL, 0x04b3585b89902ec5ULL }));
EXPECT_EQ(u256(u128 { 0x9a3b5f7c879d14f4ULL, 0xc437119868072180ULL }, u128 { 0xea395ae2238ada4eULL, 0x1aa5cc44c4c9deb5ULL }) % u256(u128 { 0x9535e4674b364058ULL, 0xbbf3d10e995c610dULL }, u128 { 0x8fac6f8ae200290aULL, 0x7832f747c56ae6dfULL }), u256(u128 { 0x9a3b5f7c879d14f4ULL, 0xc437119868072180ULL }, u128 { 0xea395ae2238ada4eULL, 0x1aa5cc44c4c9deb5ULL }));
EXPECT_EQ(u256(u128 { 0xf2a2d399b73fd0c2ULL, 0x02b7155ee15525ffULL }, u128 { 0xcaaa7daf39923db6ULL, 0x8ccb6244075bb5bbULL }) % u256(u128 { 0xfc002da6ab396d95ULL, 0xd7d0ebd6242b7119ULL }, u128 { 0x7f2ec32021ce7d32ULL, 0x63cef84255b91414ULL }), u256(u128 { 0xf6a2a5f30c06632dULL, 0x2ae62988bd29b4e5ULL }, u128 { 0x4b7bba8f17c3c083ULL, 0x28fc6a01b1a2a1a7ULL }));
EXPECT_EQ(u256(u128 { 0xfef71dab99335163ULL, 0xd1f1bc5f37570d67ULL }, u128 { 0x34bd2c7372eb8c4cULL, 0x15c0d3f1cc1613beULL }) % u256(u128 { 0x3978824c651c6cceULL, 0x5631f4d483e9f3ffULL }, u128 { 0xfd7c47d688e0d50fULL, 0xb3a9f99c7234d772ULL }), u256(u128 { 0xfef71dab99335163ULL, 0xd1f1bc5f37570d67ULL }, u128 { 0x34bd2c7372eb8c4cULL, 0x15c0d3f1cc1613beULL }));
EXPECT_EQ(u256(u128 { 0x19d69d0229db064eULL, 0x612eea6e8d79807bULL }, u128 { 0xe755c10d2b9e25adULL, 0x6a84d397b8e7da54ULL }) % u256(u128 { 0x9db6a18d292bc65fULL, 0xbdc7ccbcdb4f046cULL }, u128 { 0xd5be95d179cc1aa4ULL, 0x77c81421a604eb66ULL }), u256(u128 { 0x19d69d0229db064eULL, 0x612eea6e8d79807bULL }, u128 { 0xe755c10d2b9e25adULL, 0x6a84d397b8e7da54ULL }));
EXPECT_EQ(u256(u128 { 0xcd6a8ed6185d098fULL, 0xcf17b08e6e3836e5ULL }, u128 { 0x52e187a75426d99dULL, 0x562e1c437b33a29dULL }) % u256(u128 { 0x0c3dd1aa87a4bd96ULL, 0xac333d8636735a23ULL }, u128 { 0x1a30abda1015e674ULL, 0xe968125d96bdc2e9ULL }), u256(u128 { 0xcd6a8ed6185d098fULL, 0xcf17b08e6e3836e5ULL }, u128 { 0x52e187a75426d99dULL, 0x562e1c437b33a29dULL }));
EXPECT_EQ(u256(u128 { 0x60151f3f11782d51ULL, 0xeecbc23fa60bd168ULL }, u128 { 0x825b67c89bce81f2ULL, 0x082fe85ba1a09583ULL }) % u256(u128 { 0x438123a283f8133aULL, 0x7b5936b727339a8eULL }, u128 { 0x36f2bc572018588cULL, 0xbdebe2b4033d3209ULL }), u256(u128 { 0x60151f3f11782d51ULL, 0xeecbc23fa60bd168ULL }, u128 { 0x825b67c89bce81f2ULL, 0x082fe85ba1a09583ULL }));
EXPECT_EQ(u256(u128 { 0x6a98f75458b6c9daULL, 0xbe935c50e782e82fULL }, u128 { 0xf8f7479d9ba56379ULL, 0xfd3cb6194bc5966fULL }) % u256(u128 { 0xc0fb2a97d7368d96ULL, 0x306534301d4eadbeULL }, u128 { 0x30b2c8ff81066af6ULL, 0xd23116ef8d5eacf5ULL }), u256(u128 { 0xa99dccbc81803c44ULL, 0x8e2e2820ca343a70ULL }, u128 { 0xc8447e9e1a9ef883ULL, 0x2b0b9f29be66e97aULL }));
EXPECT_EQ(u256(u128 { 0xf90a6805c45be556ULL, 0x1d4a0c204a2dec7dULL }, u128 { 0x4a8c0d194584da59ULL, 0xcd1ab79a84dfccb6ULL }) % u256(u128 { 0xcedf80ed06c339b1ULL, 0x3a18231b09b21a3cULL }, u128 { 0xef2fedb7c3b237ddULL, 0x01d6223300a1f18aULL }), u256(u128 { 0x4621813fd5b5e197ULL, 0xecd2d36715f48c20ULL }, u128 { 0x94c3fa6b6b3ea16cULL, 0x0141e37d3ea81178ULL }));
EXPECT_EQ(u256(u128 { 0x5cced259ff5b73fdULL, 0x223a2bc9d62d3714ULL }, u128 { 0xf1b7b34b45f3608fULL, 0xce2325cbc0e9734fULL }) % u256(u128 { 0xf5dc56158c242575ULL, 0xb3bf8578c1852fdcULL }, u128 { 0xd97725f998d1d289ULL, 0x053baa680c5abb16ULL }), u256(u128 { 0xe83db511a5d9bf2aULL, 0xc00cd6645ae2ec6aULL }, u128 { 0xd090ea44fdfc4d94ULL, 0x020c2ff1df16f2d4ULL }));
EXPECT_EQ(u256(u128 { 0x6ce28a960af0ceb3ULL, 0x2da9808f962b0c43ULL }, u128 { 0x67cdac05a542bd66ULL, 0x5d3eb81aadf9479aULL }) % u256(u128 { 0x45f549795eab7c6cULL, 0x5643e85f6b4399eeULL }, u128 { 0x3b068fa03cb257dfULL, 0x3b42cfa16517b14cULL }), u256(u128 { 0x26ed411cac455247ULL, 0xd76598302ae77255ULL }, u128 { 0x2cc71c6568906586ULL, 0x21fbe87948e1964eULL }));
EXPECT_EQ(u256(u128 { 0x236f8081c4dc0d2aULL, 0xa7da15c4c15e83f3ULL }, u128 { 0x32c0948d497b78f0ULL, 0xf75ddc710601d2d0ULL }) % u256(u128 { 0x326f376465b287beULL, 0x5e24a7c87a45f4ebULL }, u128 { 0x1fa25aecc5a5a1f2ULL, 0x3490287aca77c399ULL }), u256(u128 { 0x59b2a2f02e11ee32ULL, 0x2f4776a2d846b046ULL }, u128 { 0xb43728da32e4f127ULL, 0x251d3a85dc22c46bULL }));
EXPECT_EQ(u256(u128 { 0xf5af1d760c381629ULL, 0x9f4d904501f9f6d6ULL }, u128 { 0xc23fe8d79d015270ULL, 0x3982c8897a86e837ULL }) % u256(u128 { 0xd3cc875eec2d5032ULL, 0x46e392089468f8cfULL }, u128 { 0x91c6762130826cedULL, 0x9e3b011ba58b4705ULL }), u256(u128 { 0xf5af1d760c381629ULL, 0x9f4d904501f9f6d6ULL }, u128 { 0xc23fe8d79d015270ULL, 0x3982c8897a86e837ULL }));
EXPECT_EQ(u256(u128 { 0x6b60c428cac4f505ULL, 0xeac42ae8d7929fb7ULL }, u128 { 0x59a0ce8a7110df27ULL, 0xc0d5952f55096e15ULL }) % u256(u128 { 0x280419bd2d8fe3e8ULL, 0x13b50ec9c2bb7397ULL }, u128 { 0x8d8ef08f3ac8ce5eULL, 0x8912b53aa9279938ULL }), u256(u128 { 0x435caa6b9d35111dULL, 0xd70f1c1f14d72c20ULL }, u128 { 0xcc11ddfb364810c9ULL, 0x37c2dff4abe1d4dcULL }));
EXPECT_EQ(u256(u128 { 0x8068bf135ceead51ULL, 0xadda5b57797a3a27ULL }, u128 { 0x4c4e3fe186af2698ULL, 0xdfbab959987cb289ULL }) % u256(u128 { 0x93c99cb4fa9f36c0ULL, 0xe107948b8bf301d8ULL }, u128 { 0xab4e7570e6e8e177ULL, 0xdb95d36ef24543daULL }), u256(u128 { 0xec9f225e624f7691ULL, 0xccd2c6cbed87384eULL }, u128 { 0xa0ffca709fc64520ULL, 0x0424e5eaa6376eaeULL }));
EXPECT_EQ(u256(u128 { 0x036b4a64b2ab05bbULL, 0x6be175b3549f7440ULL }, u128 { 0x3c6839ecac5d4634ULL, 0x6a1939f6585dd1ddULL }) % u256(u128 { 0x329f61eaf9c14938ULL, 0x6653276323053388ULL }, u128 { 0x7e511a9611463f4dULL, 0x9898a93910722fd8ULL }), u256(u128 { 0x036b4a64b2ab05bbULL, 0x6be175b3549f7440ULL }, u128 { 0x3c6839ecac5d4634ULL, 0x6a1939f6585dd1ddULL }));
EXPECT_EQ(u256(u128 { 0xe5d0db9190bb01c1ULL, 0x20510645c252e9b1ULL }, u128 { 0x3b673f98db9a3038ULL, 0xbda4406d733b1c6cULL }) % u256(u128 { 0x4d67af71063282f2ULL, 0x594aa60bb2360bbdULL }, u128 { 0x4c2759ff1b2ffbd1ULL, 0xe29a2e0962d9bdbfULL }), u256(u128 { 0xe5d0db9190bb01c1ULL, 0x20510645c252e9b1ULL }, u128 { 0x3b673f98db9a3038ULL, 0xbda4406d733b1c6cULL }));
EXPECT_EQ(u256(u128 { 0x2f833c8cd20c43f9ULL, 0x405bd5f257ac19e1ULL }, u128 { 0xd9873917f32ca4adULL, 0x582dda480fecde28ULL }) % u256(u128 { 0xb56564a5a9dbc163ULL, 0x17b4076b2667c703ULL }, u128 { 0xdf0f26d9a66f513eULL, 0xb34c28d1e1a1953cULL }), u256(u128 { 0x2f833c8cd20c43f9ULL, 0x405bd5f257ac19e1ULL }, u128 { 0xd9873917f32ca4adULL, 0x582dda480fecde28ULL }));
EXPECT_EQ(u256(u128 { 0x79ceb31188bc142bULL, 0xb2c083d1b0d1a172ULL }, u128 { 0x87a465799728fe9fULL, 0xe05c1c98eaa03994ULL }) % u256(u128 { 0x548b4f12f104f995ULL, 0x8d1d554e53ebc210ULL }, u128 { 0x4f5238bde10ce04aULL, 0x33da77cfa817ef7cULL }), u256(u128 { 0x27a176c5c4a82dd7ULL, 0x7e4b2e9861229931ULL }, u128 { 0x4a5b828212f57d75ULL, 0x10f23d5a4a407ba3ULL }));
EXPECT_EQ(u256(u128 { 0xfbcfb8e88417af84ULL, 0xfd35ec5ad38f6f00ULL }, u128 { 0x12d5c3e4e108cc62ULL, 0x09370460a41c637fULL }) % u256(u128 { 0x71faedeee5e0bf52ULL, 0x3d17ff54be8d686fULL }, u128 { 0x02e3ab47712e3d11ULL, 0x64da86270055e5eaULL }), u256(u128 { 0xfbcfb8e88417af84ULL, 0xfd35ec5ad38f6f00ULL }, u128 { 0x12d5c3e4e108cc62ULL, 0x09370460a41c637fULL }));
EXPECT_EQ(u256(u128 { 0x690f9145f7b1f8f8ULL, 0xe790aa66a2e08b63ULL }, u128 { 0x1d6ded50aa11aa3cULL, 0x601ec6f81fd1d57aULL }) % u256(u128 { 0x1256bdca9e0d6066ULL, 0xd19119919b026c0cULL }, u128 { 0x17a17b7df7689c40ULL, 0x97baf68d5f5622ddULL }), u256(u128 { 0x690f9145f7b1f8f8ULL, 0xe790aa66a2e08b63ULL }, u128 { 0x1d6ded50aa11aa3cULL, 0x601ec6f81fd1d57aULL }));
EXPECT_EQ(u256(u128 { 0x7f13e232d82a24c6ULL, 0x23d41447dd7f5bc6ULL }, u128 { 0xd89a3ed8b30527caULL, 0xa98ef2cc01e83685ULL }) % u256(u128 { 0x8d4f5b1983fc1f0eULL, 0xf54102ece15fb0faULL }, u128 { 0x17b8aec68556a16dULL, 0x4e1e5bea70cb9398ULL }), u256(u128 { 0x64752bffd031e6aaULL, 0x39520e6e1abff9d1ULL }, u128 { 0xa928e14ba857e4eeULL, 0x0d523af720510f55ULL }));
EXPECT_EQ(u256(u128 { 0x49750d7f39d61607ULL, 0x58bdef1c3e00d18eULL }, u128 { 0xa651479cd1fd1933ULL, 0xd1834bc3d654b633ULL }) % u256(u128 { 0x1bda34f5ec68ef3bULL, 0x12c65ce5363a7616ULL }, u128 { 0x5a79c4d85da0071aULL, 0xffa6b6284559d1aaULL }), u256(u128 { 0x49750d7f39d61607ULL, 0x58bdef1c3e00d18eULL }, u128 { 0xa651479cd1fd1933ULL, 0xd1834bc3d654b633ULL }));
}

View file

@ -10,7 +10,7 @@
#include "ValueWithShadow.h"
#include <AK/TypeCasts.h>
#include <AK/Types.h>
#include <LibX86/Types.h>
#include <AK/UFixedBigInt.h>
namespace UserspaceEmulator {

View file

@ -1855,9 +1855,10 @@ void SoftCPU::FLD_RM80(const X86::Instruction& insn)
// long doubles can be up to 128 bits wide in memory for reasons (alignment) and only uses 80 bits of precision
// GCC uses 12 bytes in 32 bit and 16 bytes in 64 bit mode
// so in the 32 bit case we read a bit to much, but that shouldn't be an issue.
auto new_f80 = insn.modrm().read128(*this, insn);
// FIXME: Respect shadow values
fpu_push(*(long double*)new_f80.value().bytes());
auto new_f80 = insn.modrm().read128(*this, insn).value();
fpu_push(*(long double*)new_f80.bytes().data());
}
void SoftCPU::FUCOMI(const X86::Instruction& insn)
@ -1908,7 +1909,7 @@ void SoftCPU::FSTP_RM80(const X86::Instruction& insn)
if constexpr (sizeof(long double) == 12)
f80 = insn.modrm().read128(*this, insn).value();
*(long double*)f80.bytes() = fpu_pop();
*(long double*)f80.bytes().data() = fpu_pop();
insn.modrm().write128(*this, insn, shadow_wrap_as_initialized(f80));
}

View file

@ -8,7 +8,7 @@
#include <AK/Format.h>
#include <AK/Platform.h>
#include <LibX86/Types.h>
#include <AK/UFixedBigInt.h>
#include <string.h>
namespace UserspaceEmulator {

View file

@ -1,6 +1,5 @@
set(SOURCES
Instruction.cpp
Types/Formatter.cpp
)
serenity_lib(LibX86 x86)

View file

@ -1,10 +0,0 @@
/*
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Types/u128.h"
#include "Types/u256.h"

View file

@ -1,113 +0,0 @@
/*
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "u128.h"
#include "u256.h"
#include <AK/Format.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <serenity.h>
#include <stdio.h>
void AK::Formatter<u128>::format(AK::FormatBuilder& builder, u128 value)
{
if (value.high() == 0) {
AK::Formatter<u64> formatter { *this };
return formatter.format(builder, value.low());
}
if (m_precision.has_value())
VERIFY_NOT_REACHED();
if (m_mode == Mode::Pointer) {
// this is way to big for a pointer
VERIFY_NOT_REACHED();
}
u8 base = 0;
bool upper_case = false;
if (m_mode == Mode::Binary) {
base = 2;
} else if (m_mode == Mode::BinaryUppercase) {
base = 2;
upper_case = true;
} else if (m_mode == Mode::Octal) {
base = 8;
} else if (m_mode == Mode::Decimal || m_mode == Mode::Default) {
// FIXME: implement this
TODO();
} else if (m_mode == Mode::Hexadecimal) {
base = 16;
} else if (m_mode == Mode::HexadecimalUppercase) {
base = 16;
upper_case = true;
} else {
VERIFY_NOT_REACHED();
}
u16 lower_length = sizeof(u64) * 0xFF / base;
if (m_width.value() > lower_length) {
builder.put_u64(value.high(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value() - lower_length, m_fill, m_sign_mode);
builder.put_u64(value.low(), base, false, upper_case, m_zero_pad, m_align, m_width.value(), m_fill, m_sign_mode);
} else {
builder.put_u64(value.low(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value(), m_fill, m_sign_mode);
}
}
void AK::Formatter<u256>::format(AK::FormatBuilder& builder, u256 value)
{
if (value.high() == 0) {
AK::Formatter<u128> formatter { *this };
return formatter.format(builder, value.low());
}
if (m_precision.has_value())
VERIFY_NOT_REACHED();
if (m_mode == Mode::Pointer) {
// this is way to big for a pointer
VERIFY_NOT_REACHED();
}
u8 base = 0;
bool upper_case = false;
if (m_mode == Mode::Binary) {
base = 2;
} else if (m_mode == Mode::BinaryUppercase) {
base = 2;
upper_case = true;
} else if (m_mode == Mode::Octal) {
base = 8;
} else if (m_mode == Mode::Decimal || m_mode == Mode::Default) {
// FIXME: implement this
TODO();
} else if (m_mode == Mode::Hexadecimal) {
base = 16;
} else if (m_mode == Mode::HexadecimalUppercase) {
base = 16;
upper_case = true;
} else {
VERIFY_NOT_REACHED();
}
u16 part_length = sizeof(u128) * 0xFF / base;
if (m_width.value() > part_length * 3) {
builder.put_u64(value.high().high(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value() - part_length * 3, m_fill, m_sign_mode);
builder.put_u64(value.high().low(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
builder.put_u64(value.low().high(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
builder.put_u64(value.low().low(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
} else if (m_width.value() > part_length * 2) {
builder.put_u64(value.high().low(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value() - part_length * 2, m_fill, m_sign_mode);
builder.put_u64(value.low().high(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
builder.put_u64(value.low().low(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
} else if (m_width.value() > part_length) {
builder.put_u64(value.low().high(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value() - part_length, m_fill, m_sign_mode);
builder.put_u64(value.low().low(), base, false, upper_case, m_zero_pad, m_align, part_length, m_fill, m_sign_mode);
} else {
builder.put_u64(value.low().low(), base, m_alternative_form, upper_case, m_zero_pad, m_align, m_width.value(), m_fill, m_sign_mode);
}
}

View file

@ -1,268 +0,0 @@
/*
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Concepts.h>
#include <AK/Format.h>
#include <AK/Types.h>
namespace X86 {
class u128 {
public:
constexpr u128() = default;
template<Unsigned T>
constexpr u128(T val)
: m_low(val)
{
}
constexpr u128(u64 val_low, u64 val_high)
: m_low(val_low)
, m_high(val_high)
{
}
ALWAYS_INLINE u8* bytes()
{
return m_bytes;
}
ALWAYS_INLINE const u8* bytes() const
{
return m_bytes;
}
ALWAYS_INLINE u16* words()
{
return (u16*)m_bytes;
}
ALWAYS_INLINE const u16* words() const
{
return (const u16*)m_bytes;
}
ALWAYS_INLINE u32* double_words()
{
return (u32*)m_bytes;
}
ALWAYS_INLINE const u32* double_words() const
{
return (const u32*)m_bytes;
}
ALWAYS_INLINE constexpr u64& low()
{
return m_low;
}
ALWAYS_INLINE constexpr const u64& low() const
{
return m_low;
}
ALWAYS_INLINE constexpr u64& high()
{
return m_high;
}
ALWAYS_INLINE constexpr const u64& high() const
{
return m_high;
}
// conversion
template<Unsigned T>
ALWAYS_INLINE constexpr operator T() const
{
return m_low;
}
ALWAYS_INLINE constexpr operator bool() const
{
return m_low || m_high;
}
// comparisons
template<Unsigned T>
ALWAYS_INLINE constexpr bool operator==(const T& other) const
{
return (!m_high) && m_low == other;
}
template<Unsigned T>
ALWAYS_INLINE constexpr bool operator!=(const T& other) const
{
return m_high || m_low != other;
}
template<Unsigned T>
ALWAYS_INLINE constexpr bool operator>(const T& other) const
{
return m_high || m_low > other;
}
template<Unsigned T>
ALWAYS_INLINE constexpr bool operator<(const T& other) const
{
return !m_high && m_low < other;
}
template<Unsigned T>
ALWAYS_INLINE constexpr bool operator>=(const T& other) const
{
return *this == other || *this > other;
}
template<Unsigned T>
ALWAYS_INLINE constexpr bool operator<=(const T& other) const
{
return *this == other || *this < other;
}
ALWAYS_INLINE constexpr bool operator==(const u128& other) const
{
return m_low == other.low() && m_high == other.high();
}
ALWAYS_INLINE constexpr bool operator!=(const u128& other) const
{
return m_low != other.low() || m_high != other.high();
}
ALWAYS_INLINE constexpr bool operator>(const u128& other) const
{
return m_high > other.high()
|| (m_high == other.high() && m_low > other.low());
}
ALWAYS_INLINE constexpr bool operator<(const u128& other) const
{
return m_high < other.high()
|| (m_high == other.high() && m_low < other.low());
}
ALWAYS_INLINE constexpr bool operator>=(const u128& other) const
{
return *this == other || *this > other;
}
ALWAYS_INLINE constexpr bool operator<=(const u128& other) const
{
return *this == other || *this < other;
}
// bitwise
template<Unsigned T>
ALWAYS_INLINE constexpr T operator&(const T& other) const
{
return m_low & other;
}
template<Unsigned T>
ALWAYS_INLINE constexpr u128 operator|(const T& other) const
{
return { m_low | other, m_high };
}
template<Unsigned T>
ALWAYS_INLINE constexpr u128 operator^(const T& other) const
{
return { m_low ^ other, m_high };
}
template<Unsigned T>
ALWAYS_INLINE constexpr u128 operator<<(const T& other) const
{
u64 overflow = m_low >> (64 - other);
return { m_low << other, (m_high << other) | overflow };
}
template<Unsigned T>
ALWAYS_INLINE constexpr u128 operator>>(const T& other) const
{
u64 underflow = m_high & other;
return { (m_low >> other) | (underflow << (64 - other)), m_high >> other };
}
ALWAYS_INLINE constexpr u128 operator&(const u128& other) const
{
return { m_low & other.low(), m_high & other.high() };
}
ALWAYS_INLINE constexpr u128 operator|(const u128& other) const
{
return { m_low | other.low(), m_high | other.high() };
}
ALWAYS_INLINE constexpr u128 operator^(const u128& other) const
{
return { m_low ^ other.low(), m_high ^ other.high() };
}
// bitwise assign
template<Unsigned T>
constexpr u128& operator&=(const T& other)
{
m_high = 0;
m_low &= other;
return *this;
}
template<Unsigned T>
constexpr u128& operator|=(const T& other)
{
m_low |= other;
return *this;
}
template<Unsigned T>
constexpr u128& operator^=(const T& other)
{
m_low ^= other;
return *this;
}
template<Unsigned T>
constexpr u128& operator>>=(const T& other)
{
*this = *this >> other;
return *this;
}
template<Unsigned T>
constexpr u128& operator<<=(const T& other)
{
*this = *this << other;
return *this;
}
constexpr u128& operator&=(const u128& other)
{
m_high &= other.high();
m_low &= other.low();
return *this;
}
constexpr u128& operator|=(const u128& other)
{
m_high |= other.high();
m_low |= other.low();
return *this;
}
constexpr u128& operator^=(const u128& other)
{
m_high ^= other.high();
m_low ^= other.low();
return *this;
}
private:
union {
u8 m_bytes[16] = { 0 };
struct {
u64 m_low;
u64 m_high;
};
};
};
static_assert(sizeof(u128) == 16);
template<typename T>
concept Unsigned_128 = IsUnsigned<T> || IsSame<T, u128>;
}
using X86::u128;
using X86::Unsigned_128;
template<>
struct AK::Formatter<u128> : StandardFormatter {
Formatter() = default;
explicit Formatter(StandardFormatter formatter)
: StandardFormatter(formatter)
{
}
void format(AK::FormatBuilder&, u128);
};

View file

@ -1,268 +0,0 @@
/*
* Copyright (c) 2021, Leon Albrecht <leon2002.la@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "u128.h"
#include <AK/Concepts.h>
#include <AK/Format.h>
#include <AK/Types.h>
namespace X86 {
class u256 {
public:
constexpr u256() = default;
constexpr u256(u64 val)
: m_low(val)
{
}
constexpr u256(u128 val)
: m_low(val)
{
}
constexpr u256(u128 val_low, u128 val_high)
: m_low(val_low)
, m_high(val_high)
{
}
ALWAYS_INLINE u8* bytes()
{
return (u8*)this;
}
ALWAYS_INLINE const u8* bytes() const
{
return (const u8*)this;
}
ALWAYS_INLINE u16* words()
{
return (u16*)this;
}
ALWAYS_INLINE const u16* words() const
{
return (const u16*)this;
}
ALWAYS_INLINE u32* double_words()
{
return (u32*)this;
}
ALWAYS_INLINE const u32* double_words() const
{
return (const u32*)this;
}
ALWAYS_INLINE constexpr u128& low()
{
return m_low;
}
ALWAYS_INLINE constexpr const u128& low() const
{
return m_low;
}
ALWAYS_INLINE constexpr u128& high()
{
return m_high;
}
ALWAYS_INLINE constexpr const u128& high() const
{
return m_high;
}
// conversion
template<Unsigned_128 T>
ALWAYS_INLINE constexpr operator T() const
{
return m_low;
}
ALWAYS_INLINE constexpr operator bool() const
{
return m_low || m_high;
}
// comparisons
template<Unsigned_128 T>
ALWAYS_INLINE constexpr bool operator==(const T& other) const
{
return !m_high && m_low == other;
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr bool operator!=(const T& other) const
{
return m_high || m_low != other;
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr bool operator>(const T& other) const
{
return m_high || m_low > other;
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr bool operator<(const T& other) const
{
return !m_high && m_low < other;
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr bool operator>=(const T& other) const
{
return *this == other || *this > other;
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr bool operator<=(const T& other) const
{
return *this == other || *this < other;
}
ALWAYS_INLINE constexpr bool operator==(const u256& other) const
{
return m_low == other.low() && m_high == other.high();
}
ALWAYS_INLINE constexpr bool operator!=(const u256& other) const
{
return m_low != other.low() || m_high != other.high();
}
ALWAYS_INLINE constexpr bool operator>(const u256& other) const
{
return m_high > other.high()
|| (m_high == other.high() && m_low > other.low());
}
ALWAYS_INLINE constexpr bool operator<(const u256& other) const
{
return m_high < other.high()
|| (m_high == other.high() && m_low < other.low());
}
ALWAYS_INLINE constexpr bool operator>=(const u256& other) const
{
return *this == other || *this > other;
}
ALWAYS_INLINE constexpr bool operator<=(const u256& other) const
{
return *this == other || *this < other;
}
// bitwise
template<Unsigned_128 T>
ALWAYS_INLINE constexpr T operator&(const T& other) const
{
return m_low & other;
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr u256 operator|(const T& other) const
{
return { m_low | other, m_high };
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr u256 operator^(const T& other) const
{
return { m_low ^ other, m_high };
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr u256 operator<<(const T& other) const
{
u128 overflow = m_low >> (128 - other);
return { m_low << other, (m_high << other) | overflow };
}
template<Unsigned_128 T>
ALWAYS_INLINE constexpr u256 operator>>(const T& other) const
{
u128 underflow = m_high & other;
return { (m_low >> other) | (underflow << (128 - other)), m_high >> other };
}
ALWAYS_INLINE constexpr u256 operator&(const u256& other) const
{
return { m_low & other.low(), m_high & other.high() };
}
ALWAYS_INLINE constexpr u256 operator|(const u256& other) const
{
return { m_low | other.low(), m_high | other.high() };
}
ALWAYS_INLINE constexpr u256 operator^(const u256& other) const
{
return { m_low ^ other.low(), m_high ^ other.high() };
}
// bitwise assign
template<Unsigned_128 T>
constexpr u256& operator&=(const T& other)
{
m_high = 0;
m_low &= other;
return *this;
}
template<Unsigned_128 T>
constexpr u256& operator|=(const T& other)
{
m_low |= other;
return *this;
}
template<Unsigned_128 T>
constexpr u256& operator^=(const T& other)
{
m_low ^= other;
return *this;
}
template<Unsigned_128 T>
constexpr u256& operator>>=(const T& other)
{
*this = *this >> other;
return *this;
}
template<Unsigned_128 T>
constexpr u256& operator<<=(const T& other)
{
*this = *this << other;
return *this;
}
constexpr u256& operator&=(const u256& other)
{
m_high &= other.high();
m_low &= other.low();
return *this;
}
constexpr u256& operator|=(const u256& other)
{
m_high |= other.high();
m_low |= other.low();
return *this;
}
constexpr u256& operator^=(const u256& other)
{
m_high ^= other.high();
m_low ^= other.low();
return *this;
}
private:
// FIXME: Somehow make this a union to directly expose the bytes (see u128)
u128 m_low {};
u128 m_high {};
};
static_assert(sizeof(u256) == 32);
template<typename T>
concept Unsigned_256 = IsUnsigned<T> || IsSame<T, u128> || IsSame<T, u256>;
}
using X86::u256;
using X86::Unsigned_256;
template<>
struct AK::Formatter<u256> : StandardFormatter {
Formatter() = default;
explicit Formatter(StandardFormatter formatter)
: StandardFormatter(formatter)
{
}
void format(AK::FormatBuilder&, u256);
};