dynarmic/include/oaknut/impl/imm.hpp
Merry 99c0a73f91 Squashed 'externals/oaknut/' changes from c24f918e5..d0488d932
d0488d932 oaknut: 2.0.0
40ad78bbf oaknut: Implement DualCodeBlock and related support
9f131cfb5 oaknut: add configuration for standalone installation
69799b43c oaknut: Test building for Android on CI
1d51f5512 oaknut: 1.2.2
918bd94f0 oaknut: Eliminate -Wconversion warnings
316d8869e oaknut: Fix edgecases in MOVP2R on +/-4GiB boundary
d8634eaa1 oaknut: Fix page boundary error in ADP
d0ca9a24e oaknut: Update README examples for CPU feature detection
dbeec268b oaknut: feature_detection_freebsd: Warn about incompatibility with earlier FreeBSD versions
86e5386e2 oaknut: feature_detect: Support NetBSD
df4cf2d48 oaknut: feature_detect: Support OpenBSD
99dfff25a oaknut: feature_detection: Read ID registers
319b3d2c9 oaknut: Add basic CPU feature detection
23e9ddb4c oaknut: CI: Don't run slow tests on OpenBSD
734f1bdb4 oaknut: CI: Use up-to-date qemu
f462c9774 oaknut: CI: Build on OpenBSD
19cd42204 oaknut: code_block: Add NetBSD and OpenBSD support
18b86a3ec oaknut: SystemReg: Add more EL0 accessible registers
53c43bf0c oaknut/tests: Reduce iterations for MOVP2R
cc37df19e oaknut: Test on FreeBSD
a66b32d26 oaknut: Fix crossing sign boundary in PageOffset
206468d72 oaknut: CI: Add macos-arm64 build
e6eecc3f9 oaknut: 1.2.1
4252d8f4a oaknut: CMakeLists: Warnings are errors on MSVC
408eed65f oaknut: arm64_encode_helpers: remove unreachable code
bfc8eedfb oaknut: arm64_encode_helpers: p maybe unused
ff4456eca oaknut: Avoid negation of unsigned values
b4ac8fd6c oaknut: Fix MOV for applications of MOVN
0575cadc4 oaknut: Disable certain functionality where absolute addressing is not available
394a3c8f0 oaknut: Appease MSVC
011183670 oaknut: 1.2.0
e83c9f327 oaknut: Add VectorCodeGenerator
5eb122cc5 oaknut: Tidy up public header
45c5a7b25 oaknut: Fix clang-format errors
36243256f oaknut: Add `const` qualifier to `AddrOffset` ctor
4af500cb5 oaknut: Add `ptr` accessor to `Label`
bccb06669 oaknut: CodeGenerator const correctness
da0590a86 oaknut: github: Update package repositories

git-subtree-dir: externals/oaknut
git-subtree-split: d0488d9320ae673167dd9117223e3453d5ff102f
2024-01-28 14:56:59 +00:00

319 lines
8.1 KiB
C++

// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
// SPDX-License-Identifier: MIT
#pragma once
#include <bit>
#include <compare>
#include <cstddef>
#include <cstdint>
#include <optional>
#include "oaknut/oaknut_exception.hpp"
namespace oaknut {
template<std::size_t bit_size_>
struct Imm {
public:
static_assert(bit_size_ != 0 && bit_size_ <= 32, "Invalid bit_size");
static constexpr std::size_t bit_size = bit_size_;
static constexpr std::uint32_t mask = (1 << bit_size) - 1;
constexpr /* implicit */ Imm(std::uint32_t value_)
: m_value(value_)
{
if (!is_valid(value_))
throw OaknutException{ExceptionType::ImmOutOfRange};
}
constexpr auto operator<=>(const Imm& other) const { return m_value <=> other.m_value; }
constexpr auto operator<=>(std::uint32_t other) const { return operator<=>(Imm{other}); }
constexpr std::uint32_t value() const { return m_value; }
static bool is_valid(std::uint32_t value_)
{
return ((value_ & mask) == value_);
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_value;
};
enum class AddSubImmShift {
SHL_0,
SHL_12,
};
struct AddSubImm {
public:
constexpr AddSubImm(std::uint32_t value_, AddSubImmShift shift_)
: m_encoded(value_ | ((shift_ == AddSubImmShift::SHL_12) ? 1 << 12 : 0))
{
if ((value_ & 0xFFF) != value_)
throw OaknutException{ExceptionType::InvalidAddSubImm};
}
constexpr /* implicit */ AddSubImm(std::uint64_t value_)
{
if ((value_ & 0xFFF) == value_) {
m_encoded = static_cast<std::uint32_t>(value_);
} else if ((value_ & 0xFFF000) == value_) {
m_encoded = static_cast<std::uint32_t>((value_ >> 12) | (1 << 12));
} else {
throw OaknutException{ExceptionType::InvalidAddSubImm};
}
}
static constexpr bool is_valid(std::uint64_t value_)
{
return ((value_ & 0xFFF) == value_) || ((value_ & 0xFFF000) == value_);
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded;
};
enum class MovImm16Shift {
SHL_0,
SHL_16,
SHL_32,
SHL_48,
};
struct MovImm16 {
public:
MovImm16(std::uint16_t value_, MovImm16Shift shift_)
: m_encoded(static_cast<std::uint32_t>(value_) | (static_cast<std::uint32_t>(shift_) << 16))
{}
constexpr /* implict */ MovImm16(std::uint64_t value_)
{
std::uint32_t shift = 0;
while (value_ != 0) {
const std::uint32_t lsw = static_cast<std::uint16_t>(value_ & 0xFFFF);
if (value_ == lsw) {
m_encoded = lsw | (shift << 16);
return;
} else if (lsw != 0) {
throw OaknutException{ExceptionType::InvalidMovImm16};
}
value_ >>= 16;
shift++;
}
}
static constexpr bool is_valid(std::uint64_t value_)
{
return ((value_ & 0xFFFF) == value_) || ((value_ & 0xFFFF0000) == value_) || ((value_ & 0xFFFF00000000) == value_) || ((value_ & 0xFFFF000000000000) == value_);
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded = 0;
};
namespace detail {
constexpr std::optional<std::uint32_t> encode_bit_imm(std::uint64_t value)
{
if (value == 0 || (~value) == 0)
return std::nullopt;
const int rotation = std::countr_zero(value & (value + 1));
const std::uint64_t rot_value = std::rotr(value, rotation);
const int esize = std::countr_zero(rot_value & (rot_value + 1));
const int ones = std::countr_one(rot_value);
if (std::rotr(value, esize) != value)
return std::nullopt;
const int S = ((-esize) << 1) | (ones - 1);
const int R = (esize - rotation) & (esize - 1);
const int N = (~S >> 6) & 1;
return static_cast<std::uint32_t>((S & 0b111111) | (R << 6) | (N << 12));
}
constexpr std::optional<std::uint32_t> encode_bit_imm(std::uint32_t value)
{
const std::uint64_t value_u64 = (static_cast<std::uint64_t>(value) << 32) | static_cast<std::uint64_t>(value);
const auto result = encode_bit_imm(value_u64);
if (result && (*result & 0b0'111111'111111) != *result)
return std::nullopt;
return result;
}
} // namespace detail
struct BitImm32 {
public:
constexpr BitImm32(Imm<6> imms, Imm<6> immr)
: m_encoded((imms.value() << 6) | immr.value())
{}
constexpr /* implicit */ BitImm32(std::uint32_t value)
{
const auto encoded = detail::encode_bit_imm(value);
if (!encoded || (*encoded & 0x1000) != 0)
throw OaknutException{ExceptionType::InvalidBitImm32};
m_encoded = *encoded;
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded;
};
struct BitImm64 {
public:
constexpr BitImm64(bool N, Imm<6> imms, Imm<6> immr)
: m_encoded((N ? 1 << 12 : 0) | (imms.value() << 6) | immr.value())
{}
constexpr /* implicit */ BitImm64(std::uint64_t value)
{
const auto encoded = detail::encode_bit_imm(value);
if (!encoded)
throw OaknutException{ExceptionType::InvalidBitImm64};
m_encoded = *encoded;
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded;
};
struct FImm8 {
public:
constexpr explicit FImm8(std::uint8_t encoded)
: m_encoded(encoded)
{}
constexpr FImm8(bool sign, Imm<3> exp, Imm<4> mantissa)
: m_encoded((sign ? 1 << 7 : 0) | (exp.value() << 4) | (mantissa.value()))
{}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded;
};
struct RepImm {
public:
constexpr explicit RepImm(std::uint8_t encoded)
: m_encoded(encoded)
{}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded;
};
template<int A>
struct ImmConst {
constexpr /* implicit */ ImmConst(int value)
{
if (value != A) {
throw OaknutException{ExceptionType::InvalidImmConst};
}
}
};
struct ImmConstFZero {
constexpr /* implicit */ ImmConstFZero(double value)
{
if (value != 0) {
throw OaknutException{ExceptionType::InvalidImmConstFZero};
}
}
};
template<int...>
struct ImmChoice;
template<int A, int B>
struct ImmChoice<A, B> {
constexpr /* implicit */ ImmChoice(int value)
{
if (value == A) {
m_encoded = 0;
} else if (value == B) {
m_encoded = 1;
} else {
throw OaknutException{ExceptionType::InvalidImmChoice};
}
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded;
};
template<int A, int B, int C, int D>
struct ImmChoice<A, B, C, D> {
constexpr /* implicit */ ImmChoice(int value)
{
if (value == A) {
m_encoded = 0;
} else if (value == B) {
m_encoded = 1;
} else if (value == C) {
m_encoded = 2;
} else if (value == D) {
m_encoded = 3;
} else {
throw OaknutException{ExceptionType::InvalidImmChoice};
}
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded;
};
template<unsigned Start, unsigned End>
struct ImmRange {
constexpr /* implicit */ ImmRange(unsigned value_)
: m_value(value_)
{
if (value_ < Start || value_ > End) {
throw OaknutException{ExceptionType::InvalidImmRange};
}
}
constexpr unsigned value() const { return m_value; }
private:
unsigned m_value;
};
template<std::size_t max_value>
struct LslShift {
constexpr /* implicit */ LslShift(std::size_t amount)
: m_encoded((((-amount) & (max_value - 1)) << 6) | (max_value - amount - 1))
{
if (amount >= max_value)
throw OaknutException{ExceptionType::LslShiftOutOfRange};
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::uint32_t m_encoded;
};
} // namespace oaknut