mirror of
https://git.suyu.dev/suyu/dynarmic.git
synced 2026-03-23 22:58:40 +00:00
Squashed 'externals/oaknut/' content from commit 86f2ca872
git-subtree-dir: externals/oaknut git-subtree-split: 86f2ca87222e59fb0b89b2f2a6b422a58a2e0892
This commit is contained in:
155
include/oaknut/impl/arm64_encode_helpers.inc.hpp
Normal file
155
include/oaknut/impl/arm64_encode_helpers.inc.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
template<std::uint32_t mask_>
|
||||
static constexpr std::uint32_t pdep(std::uint32_t val)
|
||||
{
|
||||
std::uint32_t mask = mask_;
|
||||
std::uint32_t res = 0;
|
||||
for (std::uint32_t bb = 1; mask; bb += bb) {
|
||||
if (val & bb)
|
||||
res |= mask & -mask;
|
||||
mask &= mask - 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define OAKNUT_STD_ENCODE(TYPE, ACCESS, SIZE) \
|
||||
template<std::uint32_t splat> \
|
||||
std::uint32_t encode(TYPE v) \
|
||||
{ \
|
||||
static_assert(std::popcount(splat) == SIZE); \
|
||||
return pdep<splat>(static_cast<std::uint32_t>(ACCESS)); \
|
||||
}
|
||||
|
||||
OAKNUT_STD_ENCODE(RReg, v.index() & 31, 5)
|
||||
OAKNUT_STD_ENCODE(VReg, v.index() & 31, 5)
|
||||
OAKNUT_STD_ENCODE(VRegArranged, v.index() & 31, 5)
|
||||
|
||||
OAKNUT_STD_ENCODE(AddSubImm, v.m_encoded, 13)
|
||||
OAKNUT_STD_ENCODE(BitImm32, v.m_encoded, 12)
|
||||
OAKNUT_STD_ENCODE(BitImm64, v.m_encoded, 13)
|
||||
OAKNUT_STD_ENCODE(LslShift<32>, v.m_encoded, 12)
|
||||
OAKNUT_STD_ENCODE(LslShift<64>, v.m_encoded, 12)
|
||||
OAKNUT_STD_ENCODE(FImm8, v.m_encoded, 8)
|
||||
OAKNUT_STD_ENCODE(RepImm, v.m_encoded, 8)
|
||||
|
||||
OAKNUT_STD_ENCODE(Cond, v, 4)
|
||||
OAKNUT_STD_ENCODE(AddSubExt, v, 3)
|
||||
OAKNUT_STD_ENCODE(IndexExt, v, 3)
|
||||
OAKNUT_STD_ENCODE(AddSubShift, v, 2)
|
||||
OAKNUT_STD_ENCODE(LogShift, v, 2)
|
||||
OAKNUT_STD_ENCODE(PstateField, v, 6)
|
||||
OAKNUT_STD_ENCODE(SystemReg, v, 15)
|
||||
OAKNUT_STD_ENCODE(AtOp, v, 7)
|
||||
OAKNUT_STD_ENCODE(BarrierOp, v, 4)
|
||||
OAKNUT_STD_ENCODE(DcOp, v, 10)
|
||||
OAKNUT_STD_ENCODE(IcOp, v, 10)
|
||||
OAKNUT_STD_ENCODE(PrfOp, v, 5)
|
||||
OAKNUT_STD_ENCODE(TlbiOp, v, 10)
|
||||
|
||||
template<std::uint32_t splat>
|
||||
std::uint32_t encode(MovImm16 v)
|
||||
{
|
||||
static_assert(std::popcount(splat) == 17 || std::popcount(splat) == 18);
|
||||
if constexpr (std::popcount(splat) == 17) {
|
||||
constexpr std::uint32_t mask = (1 << std::popcount(splat)) - 1;
|
||||
if ((v.m_encoded & mask) != v.m_encoded)
|
||||
throw "invalid MovImm16";
|
||||
}
|
||||
return pdep<splat>(v.m_encoded);
|
||||
}
|
||||
|
||||
template<std::uint32_t splat, std::size_t imm_size>
|
||||
std::uint32_t encode(Imm<imm_size> v)
|
||||
{
|
||||
static_assert(std::popcount(splat) >= imm_size);
|
||||
return pdep<splat>(v.value());
|
||||
}
|
||||
|
||||
template<std::uint32_t splat, int A, int B>
|
||||
std::uint32_t encode(ImmChoice<A, B> v)
|
||||
{
|
||||
static_assert(std::popcount(splat) == 1);
|
||||
return pdep<splat>(v.m_encoded);
|
||||
}
|
||||
|
||||
template<std::uint32_t splat, int A, int B, int C, int D>
|
||||
std::uint32_t encode(ImmChoice<A, B, C, D> v)
|
||||
{
|
||||
static_assert(std::popcount(splat) == 2);
|
||||
return pdep<splat>(v.m_encoded);
|
||||
}
|
||||
|
||||
template<std::uint32_t splat, std::size_t size, std::size_t align>
|
||||
std::uint32_t encode(SOffset<size, align> v)
|
||||
{
|
||||
static_assert(std::popcount(splat) == size - align);
|
||||
return pdep<splat>(v.m_encoded);
|
||||
}
|
||||
|
||||
template<std::uint32_t splat, std::size_t size, std::size_t align>
|
||||
std::uint32_t encode(POffset<size, align> v)
|
||||
{
|
||||
static_assert(std::popcount(splat) == size - align);
|
||||
return pdep<splat>(v.m_encoded);
|
||||
}
|
||||
|
||||
template<std::uint32_t splat>
|
||||
std::uint32_t encode(std::uint32_t v)
|
||||
{
|
||||
return pdep<splat>(v);
|
||||
}
|
||||
|
||||
template<std::uint32_t splat, typename T, size_t N>
|
||||
std::uint32_t encode(List<T, N> v)
|
||||
{
|
||||
return encode<splat>(v.m_base);
|
||||
}
|
||||
|
||||
#undef OAKNUT_STD_ENCODE
|
||||
|
||||
void addsubext_lsl_correction(AddSubExt& ext, XRegSp)
|
||||
{
|
||||
if (ext == AddSubExt::LSL)
|
||||
ext = AddSubExt::UXTX;
|
||||
}
|
||||
void addsubext_lsl_correction(AddSubExt& ext, WRegWsp)
|
||||
{
|
||||
if (ext == AddSubExt::LSL)
|
||||
ext = AddSubExt::UXTW;
|
||||
}
|
||||
void addsubext_lsl_correction(AddSubExt& ext, XReg)
|
||||
{
|
||||
if (ext == AddSubExt::LSL)
|
||||
ext = AddSubExt::UXTX;
|
||||
}
|
||||
void addsubext_lsl_correction(AddSubExt& ext, WReg)
|
||||
{
|
||||
if (ext == AddSubExt::LSL)
|
||||
ext = AddSubExt::UXTW;
|
||||
}
|
||||
|
||||
void addsubext_verify_reg_size(AddSubExt ext, RReg rm)
|
||||
{
|
||||
if (rm.bitsize() == 32 && (static_cast<int>(ext) & 0b011) != 0b011)
|
||||
return;
|
||||
if (rm.bitsize() == 64 && (static_cast<int>(ext) & 0b011) == 0b011)
|
||||
return;
|
||||
throw "invalid AddSubExt choice for rm size";
|
||||
}
|
||||
|
||||
void indexext_verify_reg_size(IndexExt ext, RReg rm)
|
||||
{
|
||||
if (rm.bitsize() == 32 && (static_cast<int>(ext) & 1) == 0)
|
||||
return;
|
||||
if (rm.bitsize() == 64 && (static_cast<int>(ext) & 1) == 1)
|
||||
return;
|
||||
throw "invalid IndexExt choice for rm size";
|
||||
}
|
||||
|
||||
void tbz_verify_reg_size(RReg rt, Imm<6> imm)
|
||||
{
|
||||
if (rt.bitsize() == 32 && imm.value() >= 32)
|
||||
throw "invalid imm choice for rt size";
|
||||
}
|
||||
1709
include/oaknut/impl/arm64_mnemonics.inc.hpp
Normal file
1709
include/oaknut/impl/arm64_mnemonics.inc.hpp
Normal file
File diff suppressed because it is too large
Load Diff
242
include/oaknut/impl/enum.hpp
Normal file
242
include/oaknut/impl/enum.hpp
Normal file
@@ -0,0 +1,242 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
struct PostIndexed {};
|
||||
|
||||
struct PreIndexed {};
|
||||
|
||||
enum class LslSymbol {
|
||||
LSL,
|
||||
};
|
||||
|
||||
enum class MslSymbol {
|
||||
MSL,
|
||||
};
|
||||
|
||||
enum class Cond {
|
||||
EQ,
|
||||
NE,
|
||||
CS,
|
||||
CC,
|
||||
MI,
|
||||
PL,
|
||||
VS,
|
||||
VC,
|
||||
HI,
|
||||
LS,
|
||||
GE,
|
||||
LT,
|
||||
GT,
|
||||
LE,
|
||||
AL,
|
||||
NV,
|
||||
HS = CS,
|
||||
LO = CC,
|
||||
};
|
||||
|
||||
constexpr Cond invert(Cond c)
|
||||
{
|
||||
return static_cast<Cond>(static_cast<unsigned>(c) ^ 1);
|
||||
}
|
||||
|
||||
enum class AddSubExt {
|
||||
UXTB,
|
||||
UXTH,
|
||||
UXTW,
|
||||
UXTX,
|
||||
SXTB,
|
||||
SXTH,
|
||||
SXTW,
|
||||
SXTX,
|
||||
LSL, // UXTW (32-bit) or UXTX (64-bit)
|
||||
};
|
||||
|
||||
enum class IndexExt {
|
||||
UXTW = 0b010,
|
||||
LSL = 0b011,
|
||||
SXTW = 0b110,
|
||||
SXTX = 0b111,
|
||||
};
|
||||
|
||||
enum class AddSubShift {
|
||||
LSL,
|
||||
LSR,
|
||||
ASR,
|
||||
};
|
||||
|
||||
enum class LogShift {
|
||||
LSL,
|
||||
LSR,
|
||||
ASR,
|
||||
ROR,
|
||||
};
|
||||
|
||||
enum class PstateField {
|
||||
UAO = 0b000'011, // ARMv8.2-UAO
|
||||
PAN = 0b000'100, // ARMv8.1-PAN
|
||||
SPSel = 0b000'101,
|
||||
DIT = 0b011'010, // ARMv8.4-DIT
|
||||
DAIFSet = 0b011'110,
|
||||
DAIFClr = 0b011'111,
|
||||
};
|
||||
|
||||
enum class SystemReg {
|
||||
};
|
||||
|
||||
enum class AtOp {
|
||||
S1E1R = 0b000'0'000,
|
||||
S1E1W = 0b000'0'001,
|
||||
S1E0R = 0b000'0'010,
|
||||
S1E0W = 0b000'0'011,
|
||||
S1E1RP = 0b000'1'000, // ARMv8.2-ATS1E1
|
||||
S1E1WP = 0b000'1'001, // ARMv8.2-ATS1E1
|
||||
S1E2R = 0b100'0'000,
|
||||
S1E2W = 0b100'0'001,
|
||||
S12E1R = 0b100'0'100,
|
||||
S12E1W = 0b100'0'101,
|
||||
S12E0R = 0b100'0'110,
|
||||
S12E0W = 0b100'0'111,
|
||||
S1E3R = 0b110'0'000,
|
||||
S1E3W = 0b110'0'001,
|
||||
};
|
||||
|
||||
enum class BarrierOp {
|
||||
SY = 0b1111,
|
||||
ST = 0b1110,
|
||||
LD = 0b1101,
|
||||
ISH = 0b1011,
|
||||
ISHST = 0b1010,
|
||||
ISHLD = 0b1001,
|
||||
NSH = 0b0111,
|
||||
NSHST = 0b0110,
|
||||
NSHLD = 0b0101,
|
||||
OSH = 0b0011,
|
||||
OSHST = 0b0010,
|
||||
OSHLD = 0b0001,
|
||||
};
|
||||
|
||||
enum class DcOp {
|
||||
IVAC = 0b000'0110'001,
|
||||
ISW = 0b000'0110'010,
|
||||
CSW = 0b000'1010'010,
|
||||
CISW = 0b000'1110'010,
|
||||
ZVA = 0b011'0100'001,
|
||||
CVAC = 0b011'1010'001,
|
||||
CVAU = 0b011'1011'001,
|
||||
CVAP = 0b011'1100'001, // ARMv8.2-DCPoP
|
||||
CIVAC = 0b011'1110'001,
|
||||
};
|
||||
|
||||
enum class IcOp {
|
||||
IALLUIS = 0b000'0001'000,
|
||||
IALLU = 0b000'0101'000,
|
||||
IVAU = 0b011'0101'001,
|
||||
};
|
||||
|
||||
enum class PrfOp {
|
||||
PLDL1KEEP = 0b00'00'0,
|
||||
PLDL1STRM = 0b00'00'1,
|
||||
PLDL2KEEP = 0b00'01'0,
|
||||
PLDL2STRM = 0b00'01'1,
|
||||
PLDL3KEEP = 0b00'10'0,
|
||||
PLDL3STRM = 0b00'10'1,
|
||||
PLIL1KEEP = 0b01'00'0,
|
||||
PLIL1STRM = 0b01'00'1,
|
||||
PLIL2KEEP = 0b01'01'0,
|
||||
PLIL2STRM = 0b01'01'1,
|
||||
PLIL3KEEP = 0b01'10'0,
|
||||
PLIL3STRM = 0b01'10'1,
|
||||
PSTL1KEEP = 0b10'00'0,
|
||||
PSTL1STRM = 0b10'00'1,
|
||||
PSTL2KEEP = 0b10'01'0,
|
||||
PSTL2STRM = 0b10'01'1,
|
||||
PSTL3KEEP = 0b10'10'0,
|
||||
PSTL3STRM = 0b10'10'1,
|
||||
};
|
||||
|
||||
enum class TlbiOp {
|
||||
VMALLE1OS = 0b000'0001'000, // ARMv8.4-TLBI
|
||||
VAE1OS = 0b000'0001'001, // ARMv8.4-TLBI
|
||||
ASIDE1OS = 0b000'0001'010, // ARMv8.4-TLBI
|
||||
VAAE1OS = 0b000'0001'011, // ARMv8.4-TLBI
|
||||
VALE1OS = 0b000'0001'101, // ARMv8.4-TLBI
|
||||
VAALE1OS = 0b000'0001'111, // ARMv8.4-TLBI
|
||||
RVAE1IS = 0b000'0010'001, // ARMv8.4-TLBI
|
||||
RVAAE1IS = 0b000'0010'011, // ARMv8.4-TLBI
|
||||
RVALE1IS = 0b000'0010'101, // ARMv8.4-TLBI
|
||||
RVAALE1IS = 0b000'0010'111, // ARMv8.4-TLBI
|
||||
VMALLE1IS = 0b000'0011'000,
|
||||
VAE1IS = 0b000'0011'001,
|
||||
ASIDE1IS = 0b000'0011'010,
|
||||
VAAE1IS = 0b000'0011'011,
|
||||
VALE1IS = 0b000'0011'101,
|
||||
VAALE1IS = 0b000'0011'111,
|
||||
RVAE1OS = 0b000'0101'001, // ARMv8.4-TLBI
|
||||
RVAAE1OS = 0b000'0101'011, // ARMv8.4-TLBI
|
||||
RVALE1OS = 0b000'0101'101, // ARMv8.4-TLBI
|
||||
RVAALE1OS = 0b000'0101'111, // ARMv8.4-TLBI
|
||||
RVAE1 = 0b000'0110'001, // ARMv8.4-TLBI
|
||||
RVAAE1 = 0b000'0110'011, // ARMv8.4-TLBI
|
||||
RVALE1 = 0b000'0110'101, // ARMv8.4-TLBI
|
||||
RVAALE1 = 0b000'0110'111, // ARMv8.4-TLBI
|
||||
VMALLE1 = 0b000'0111'000,
|
||||
VAE1 = 0b000'0111'001,
|
||||
ASIDE1 = 0b000'0111'010,
|
||||
VAAE1 = 0b000'0111'011,
|
||||
VALE1 = 0b000'0111'101,
|
||||
VAALE1 = 0b000'0111'111,
|
||||
IPAS2E1IS = 0b100'0000'001,
|
||||
RIPAS2E1IS = 0b100'0000'010, // ARMv8.4-TLBI
|
||||
IPAS2LE1IS = 0b100'0000'101,
|
||||
RIPAS2LE1IS = 0b100'0000'110, // ARMv8.4-TLBI
|
||||
ALLE2OS = 0b100'0001'000, // ARMv8.4-TLBI
|
||||
VAE2OS = 0b100'0001'001, // ARMv8.4-TLBI
|
||||
ALLE1OS = 0b100'0001'100, // ARMv8.4-TLBI
|
||||
VALE2OS = 0b100'0001'101, // ARMv8.4-TLBI
|
||||
VMALLS12E1OS = 0b100'0001'110, // ARMv8.4-TLBI
|
||||
RVAE2IS = 0b100'0010'001, // ARMv8.4-TLBI
|
||||
RVALE2IS = 0b100'0010'101, // ARMv8.4-TLBI
|
||||
ALLE2IS = 0b100'0011'000,
|
||||
VAE2IS = 0b100'0011'001,
|
||||
ALLE1IS = 0b100'0011'100,
|
||||
VALE2IS = 0b100'0011'101,
|
||||
VMALLS12E1IS = 0b100'0011'110,
|
||||
IPAS2E1OS = 0b100'0100'000, // ARMv8.4-TLBI
|
||||
IPAS2E1 = 0b100'0100'001,
|
||||
RIPAS2E1 = 0b100'0100'010, // ARMv8.4-TLBI
|
||||
RIPAS2E1OS = 0b100'0100'011, // ARMv8.4-TLBI
|
||||
IPAS2LE1OS = 0b100'0100'100, // ARMv8.4-TLBI
|
||||
IPAS2LE1 = 0b100'0100'101,
|
||||
RIPAS2LE1 = 0b100'0100'110, // ARMv8.4-TLBI
|
||||
RIPAS2LE1OS = 0b100'0100'111, // ARMv8.4-TLBI
|
||||
RVAE2OS = 0b100'0101'001, // ARMv8.4-TLBI
|
||||
RVALE2OS = 0b100'0101'101, // ARMv8.4-TLBI
|
||||
RVAE2 = 0b100'0110'001, // ARMv8.4-TLBI
|
||||
RVALE2 = 0b100'0110'101, // ARMv8.4-TLBI
|
||||
ALLE2 = 0b100'0111'000,
|
||||
VAE2 = 0b100'0111'001,
|
||||
ALLE1 = 0b100'0111'100,
|
||||
VALE2 = 0b100'0111'101,
|
||||
VMALLS12E1 = 0b100'0111'110,
|
||||
ALLE3OS = 0b110'0001'000, // ARMv8.4-TLBI
|
||||
VAE3OS = 0b110'0001'001, // ARMv8.4-TLBI
|
||||
VALE3OS = 0b110'0001'101, // ARMv8.4-TLBI
|
||||
RVAE3IS = 0b110'0010'001, // ARMv8.4-TLBI
|
||||
RVALE3IS = 0b110'0010'101, // ARMv8.4-TLBI
|
||||
ALLE3IS = 0b110'0011'000,
|
||||
VAE3IS = 0b110'0011'001,
|
||||
VALE3IS = 0b110'0011'101,
|
||||
RVAE3OS = 0b110'0101'001, // ARMv8.4-TLBI
|
||||
RVALE3OS = 0b110'0101'101, // ARMv8.4-TLBI
|
||||
RVAE3 = 0b110'0110'001, // ARMv8.4-TLBI
|
||||
RVALE3 = 0b110'0110'101, // ARMv8.4-TLBI
|
||||
ALLE3 = 0b110'0111'000,
|
||||
VAE3 = 0b110'0111'001,
|
||||
VALE3 = 0b110'0111'101,
|
||||
};
|
||||
|
||||
} // namespace oaknut
|
||||
9163
include/oaknut/impl/fpsimd_mnemonics.inc.hpp
Normal file
9163
include/oaknut/impl/fpsimd_mnemonics.inc.hpp
Normal file
File diff suppressed because it is too large
Load Diff
317
include/oaknut/impl/imm.hpp
Normal file
317
include/oaknut/impl/imm.hpp
Normal file
@@ -0,0 +1,317 @@
|
||||
// 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>
|
||||
|
||||
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 "outsized Imm value";
|
||||
}
|
||||
|
||||
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 "invalid AddSubImm";
|
||||
}
|
||||
|
||||
constexpr /* implicit */ AddSubImm(std::uint64_t value_)
|
||||
{
|
||||
if ((value_ & 0xFFF) == value_) {
|
||||
m_encoded = value_;
|
||||
} else if ((value_ & 0xFFF000) == value_) {
|
||||
m_encoded = (value_ >> 12) | (1 << 12);
|
||||
} else {
|
||||
throw "invalid AddSubImm";
|
||||
}
|
||||
}
|
||||
|
||||
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 "invalid MovImm16";
|
||||
}
|
||||
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 std::size_t rotation = std::countr_zero(value & (value + 1));
|
||||
const std::uint64_t rot_value = std::rotr(value, rotation);
|
||||
|
||||
const std::size_t esize = std::countr_zero(rot_value & (rot_value + 1));
|
||||
const std::size_t ones = std::countr_one(rot_value);
|
||||
|
||||
if (std::rotr(value, esize) != value)
|
||||
return std::nullopt;
|
||||
|
||||
const std::uint32_t S = ((-esize) << 1) | (ones - 1);
|
||||
const std::uint32_t R = (esize - rotation) & (esize - 1);
|
||||
const std::uint32_t 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 "invalid BitImm32";
|
||||
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 "invalid BitImm64";
|
||||
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 "invalid ImmConst";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ImmConstFZero {
|
||||
constexpr /* implicit */ ImmConstFZero(double value)
|
||||
{
|
||||
if (value != 0) {
|
||||
throw "invalid ImmConstFZero";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 "invalid ImmChoice";
|
||||
}
|
||||
}
|
||||
|
||||
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 "invalid ImmChoice";
|
||||
}
|
||||
}
|
||||
|
||||
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 "invalid ImmRange";
|
||||
}
|
||||
}
|
||||
|
||||
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 "LslShift out of range";
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
std::uint32_t m_encoded;
|
||||
};
|
||||
|
||||
} // namespace oaknut
|
||||
80
include/oaknut/impl/list.hpp
Normal file
80
include/oaknut/impl/list.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
struct Elem;
|
||||
template<typename>
|
||||
struct ElemSelector;
|
||||
struct VRegArranged;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename>
|
||||
struct is_instance_of_ElemSelector : std::false_type {};
|
||||
|
||||
template<typename E>
|
||||
struct is_instance_of_ElemSelector<ElemSelector<E>> : std::true_type {};
|
||||
|
||||
template<class T>
|
||||
constexpr bool is_instance_of_ElemSelector_v = is_instance_of_ElemSelector<T>::value;
|
||||
|
||||
struct BaseOnlyTag {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T, std::size_t N>
|
||||
struct List {
|
||||
template<typename... U>
|
||||
constexpr explicit List(U... args)
|
||||
: m_base(std::get<0>(std::tie(args...)))
|
||||
{
|
||||
static_assert((std::is_same_v<T, U> && ...));
|
||||
static_assert(sizeof...(args) == N);
|
||||
static_assert(std::is_base_of_v<VRegArranged, T> || std::is_base_of_v<Elem, T> || detail::is_instance_of_ElemSelector_v<T>);
|
||||
|
||||
if (!verify(std::index_sequence_for<U...>{}, args...))
|
||||
throw "invalid List";
|
||||
}
|
||||
|
||||
constexpr auto operator[](unsigned elem_index) const
|
||||
{
|
||||
using S = decltype(m_base[elem_index]);
|
||||
return List<S, N>(detail::BaseOnlyTag{}, m_base[elem_index]);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename>
|
||||
friend class BasicCodeGenerator;
|
||||
template<typename, std::size_t>
|
||||
friend struct List;
|
||||
|
||||
constexpr explicit List(detail::BaseOnlyTag, T base_)
|
||||
: m_base(base_)
|
||||
{}
|
||||
|
||||
template<typename... U, std::size_t... indexes>
|
||||
constexpr bool verify(std::index_sequence<indexes...>, U... args)
|
||||
{
|
||||
if constexpr (std::is_base_of_v<VRegArranged, T>) {
|
||||
return (((m_base.index() + indexes) % 32 == static_cast<std::size_t>(args.index())) && ...);
|
||||
} else if constexpr (std::is_base_of_v<Elem, T>) {
|
||||
return (((m_base.reg_index() + indexes) % 32 == static_cast<std::size_t>(args.reg_index()) && m_base.elem_index() == args.elem_index()) && ...);
|
||||
} else {
|
||||
return (((m_base.reg_index() + indexes) % 32 == static_cast<std::size_t>(args.reg_index())) && ...);
|
||||
}
|
||||
}
|
||||
|
||||
T m_base;
|
||||
};
|
||||
|
||||
template<typename... U>
|
||||
List(U...) -> List<std::common_type_t<U...>, sizeof...(U)>;
|
||||
|
||||
} // namespace oaknut
|
||||
19
include/oaknut/impl/multi_typed_name.hpp
Normal file
19
include/oaknut/impl/multi_typed_name.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
template<auto... Vs>
|
||||
struct MultiTypedName;
|
||||
|
||||
template<>
|
||||
struct MultiTypedName<> {};
|
||||
|
||||
template<auto V, auto... Vs>
|
||||
struct MultiTypedName<V, Vs...> : public MultiTypedName<Vs...> {
|
||||
constexpr operator decltype(V)() const { return V; }
|
||||
};
|
||||
|
||||
} // namespace oaknut
|
||||
129
include/oaknut/impl/offset.hpp
Normal file
129
include/oaknut/impl/offset.hpp
Normal file
@@ -0,0 +1,129 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <variant>
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
struct Label;
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr std::uint64_t inverse_mask_from_size(std::size_t size)
|
||||
{
|
||||
return (~std::uint64_t{0}) << size;
|
||||
}
|
||||
|
||||
constexpr std::uint64_t mask_from_size(std::size_t size)
|
||||
{
|
||||
return (~std::uint64_t{0}) >> (64 - size);
|
||||
}
|
||||
|
||||
template<std::size_t bit_count>
|
||||
constexpr std::uint64_t sign_extend(std::uint64_t value)
|
||||
{
|
||||
static_assert(bit_count != 0, "cannot sign-extend zero-sized value");
|
||||
constexpr size_t shift_amount = 64 - bit_count;
|
||||
return static_cast<std::uint64_t>(static_cast<std::int64_t>(value << shift_amount) >> shift_amount);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<std::size_t bitsize, std::size_t alignment>
|
||||
struct AddrOffset {
|
||||
AddrOffset(std::ptrdiff_t diff)
|
||||
: m_payload(encode(diff))
|
||||
{}
|
||||
|
||||
AddrOffset(Label& label)
|
||||
: m_payload(&label)
|
||||
{}
|
||||
|
||||
AddrOffset(void* ptr)
|
||||
: m_payload(ptr)
|
||||
{}
|
||||
|
||||
static std::uint32_t encode(std::ptrdiff_t diff)
|
||||
{
|
||||
const std::uint64_t diff_u64 = static_cast<std::uint64_t>(diff);
|
||||
if (detail::sign_extend<bitsize>(diff_u64) != diff_u64)
|
||||
throw "out of range";
|
||||
if (diff_u64 != (diff_u64 & detail::inverse_mask_from_size(alignment)))
|
||||
throw "misalignment";
|
||||
|
||||
return static_cast<std::uint32_t>((diff_u64 & detail::mask_from_size(bitsize)) >> alignment);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
std::variant<std::uint32_t, Label*, void*> m_payload;
|
||||
};
|
||||
|
||||
template<std::size_t bitsize>
|
||||
struct PageOffset {
|
||||
PageOffset(void* ptr)
|
||||
: m_payload(ptr)
|
||||
{}
|
||||
|
||||
PageOffset(Label& label)
|
||||
: m_payload(&label)
|
||||
{}
|
||||
|
||||
static std::uint32_t encode(std::uintptr_t current_addr, std::uintptr_t target)
|
||||
{
|
||||
const std::int64_t page_diff = (static_cast<std::int64_t>(target) >> 12) - (static_cast<std::int64_t>(current_addr) >> 12);
|
||||
if (detail::sign_extend<bitsize>(page_diff) != page_diff)
|
||||
throw "out of range";
|
||||
return static_cast<std::uint32_t>(page_diff & detail::mask_from_size(bitsize));
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
std::variant<Label*, void*> m_payload;
|
||||
};
|
||||
|
||||
template<std::size_t bitsize, std::size_t alignment>
|
||||
struct SOffset {
|
||||
SOffset(std::int64_t offset)
|
||||
{
|
||||
const std::uint64_t diff_u64 = static_cast<std::uint64_t>(offset);
|
||||
if (detail::sign_extend<bitsize>(diff_u64) != diff_u64)
|
||||
throw "out of range";
|
||||
if (diff_u64 != (diff_u64 & detail::inverse_mask_from_size(alignment)))
|
||||
throw "misalignment";
|
||||
|
||||
m_encoded = static_cast<std::uint32_t>((diff_u64 & detail::mask_from_size(bitsize)) >> alignment);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
std::uint32_t m_encoded;
|
||||
};
|
||||
|
||||
template<std::size_t bitsize, std::size_t alignment>
|
||||
struct POffset {
|
||||
POffset(std::int64_t offset)
|
||||
{
|
||||
const std::uint64_t diff_u64 = static_cast<std::uint64_t>(offset);
|
||||
if (diff_u64 > detail::mask_from_size(bitsize))
|
||||
throw "out of range";
|
||||
if (diff_u64 != (diff_u64 & detail::inverse_mask_from_size(alignment)))
|
||||
throw "misalignment";
|
||||
|
||||
m_encoded = static_cast<std::uint32_t>((diff_u64 & detail::mask_from_size(bitsize)) >> alignment);
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
std::uint32_t m_encoded;
|
||||
};
|
||||
|
||||
} // namespace oaknut
|
||||
441
include/oaknut/impl/reg.hpp
Normal file
441
include/oaknut/impl/reg.hpp
Normal file
@@ -0,0 +1,441 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
struct Reg;
|
||||
|
||||
struct RReg;
|
||||
struct ZrReg;
|
||||
struct WzrReg;
|
||||
struct XReg;
|
||||
struct WReg;
|
||||
struct SpReg;
|
||||
struct WspReg;
|
||||
struct XRegSp;
|
||||
struct XRegWsp;
|
||||
|
||||
struct VReg;
|
||||
struct VRegArranged;
|
||||
struct BReg;
|
||||
struct HReg;
|
||||
struct SReg;
|
||||
struct DReg;
|
||||
struct QReg;
|
||||
struct VReg_8B;
|
||||
struct VReg_4H;
|
||||
struct VReg_2S;
|
||||
struct VReg_1D;
|
||||
struct VReg_16B;
|
||||
struct VReg_8H;
|
||||
struct VReg_4S;
|
||||
struct VReg_2D;
|
||||
struct VReg_1Q;
|
||||
|
||||
struct VRegSelector;
|
||||
|
||||
template<typename Elem>
|
||||
struct ElemSelector;
|
||||
struct BElem;
|
||||
struct HElem;
|
||||
struct SElem;
|
||||
struct DElem;
|
||||
|
||||
struct Reg {
|
||||
constexpr explicit Reg(bool is_vector_, unsigned bitsize_, int index_)
|
||||
: m_index(index_)
|
||||
, m_bitsize(bitsize_)
|
||||
, m_is_vector(is_vector_)
|
||||
{
|
||||
assert(index_ >= -1 && index_ <= 31);
|
||||
assert(bitsize_ != 0 && (bitsize_ & (bitsize_ - 1)) == 0 && "Bitsize must be a power of two");
|
||||
}
|
||||
|
||||
constexpr int index() const { return m_index; }
|
||||
constexpr unsigned bitsize() const { return m_bitsize; }
|
||||
constexpr bool is_vector() const { return m_is_vector; }
|
||||
|
||||
private:
|
||||
int m_index : 8;
|
||||
unsigned m_bitsize : 8;
|
||||
bool m_is_vector;
|
||||
};
|
||||
|
||||
struct RReg : public Reg {
|
||||
constexpr explicit RReg(unsigned bitsize_, int index_)
|
||||
: Reg(false, bitsize_, index_)
|
||||
{
|
||||
assert(bitsize_ == 32 || bitsize_ == 64);
|
||||
}
|
||||
|
||||
XReg toX() const;
|
||||
WReg toW() const;
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct ZrReg : public RReg {
|
||||
constexpr explicit ZrReg()
|
||||
: RReg(64, 31) {}
|
||||
};
|
||||
|
||||
struct WzrReg : public RReg {
|
||||
constexpr explicit WzrReg()
|
||||
: RReg(32, 31) {}
|
||||
};
|
||||
|
||||
struct XReg : public RReg {
|
||||
constexpr explicit XReg(int index_)
|
||||
: RReg(64, index_) {}
|
||||
|
||||
constexpr /* implicit */ XReg(ZrReg)
|
||||
: RReg(64, 31) {}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct WReg : public RReg {
|
||||
constexpr explicit WReg(int index_)
|
||||
: RReg(32, index_) {}
|
||||
|
||||
constexpr /* implicit */ WReg(WzrReg)
|
||||
: RReg(32, 31) {}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
inline XReg RReg::toX() const
|
||||
{
|
||||
if (index() == -1)
|
||||
throw "cannot convert SP/WSP to XReg";
|
||||
return XReg{index()};
|
||||
}
|
||||
|
||||
inline WReg RReg::toW() const
|
||||
{
|
||||
if (index() == -1)
|
||||
throw "cannot convert SP/WSP to WReg";
|
||||
return WReg{index()};
|
||||
}
|
||||
|
||||
struct SpReg : public RReg {
|
||||
constexpr explicit SpReg()
|
||||
: RReg(64, -1) {}
|
||||
};
|
||||
|
||||
struct WspReg : public RReg {
|
||||
constexpr explicit WspReg()
|
||||
: RReg(64, -1) {}
|
||||
};
|
||||
|
||||
struct XRegSp : public RReg {
|
||||
constexpr /* implict */ XRegSp(SpReg)
|
||||
: RReg(64, -1) {}
|
||||
|
||||
constexpr /* implict */ XRegSp(XReg xr)
|
||||
: RReg(64, xr.index())
|
||||
{
|
||||
if (xr.index() == 31)
|
||||
throw "unexpected ZR passed into an XRegSp";
|
||||
}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct WRegWsp : public RReg {
|
||||
constexpr /* implict */ WRegWsp(WspReg)
|
||||
: RReg(32, -1) {}
|
||||
|
||||
constexpr /* implict */ WRegWsp(WReg wr)
|
||||
: RReg(32, wr.index())
|
||||
{
|
||||
if (wr.index() == 31)
|
||||
throw "unexpected WZR passed into an WRegWsp";
|
||||
}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg : public Reg {
|
||||
constexpr explicit VReg(unsigned bitsize_, int index_)
|
||||
: Reg(true, bitsize_, index_)
|
||||
{
|
||||
assert(bitsize_ == 8 || bitsize_ == 16 || bitsize_ == 32 || bitsize_ == 64 || bitsize_ == 128);
|
||||
}
|
||||
|
||||
constexpr BReg toB() const;
|
||||
constexpr HReg toH() const;
|
||||
constexpr SReg toS() const;
|
||||
constexpr DReg toD() const;
|
||||
constexpr QReg toQ() const;
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VRegArranged : public Reg {
|
||||
constexpr explicit VRegArranged(unsigned bitsize_, int index_, unsigned esize_)
|
||||
: Reg(true, bitsize_, index_), m_esize(esize_)
|
||||
{
|
||||
assert(bitsize_ == 64 || bitsize_ == 128);
|
||||
assert(esize_ != 0 && (esize_ & (esize_ - 1)) == 0 && "esize must be a power of two");
|
||||
assert(esize_ <= bitsize_);
|
||||
}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
|
||||
private:
|
||||
int m_esize : 8;
|
||||
};
|
||||
|
||||
struct BReg : public VReg {
|
||||
constexpr explicit BReg(int index_)
|
||||
: VReg(8, index_)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct HReg : public VReg {
|
||||
constexpr explicit HReg(int index_)
|
||||
: VReg(16, index_)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct SReg : public VReg {
|
||||
constexpr explicit SReg(int index_)
|
||||
: VReg(32, index_)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct DReg : public VReg {
|
||||
constexpr explicit DReg(int index_)
|
||||
: VReg(64, index_)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct QReg : public VReg {
|
||||
constexpr explicit QReg(int index_)
|
||||
: VReg(128, index_)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_8B : public VRegArranged {
|
||||
constexpr explicit VReg_8B(int reg_index_)
|
||||
: VRegArranged(64, reg_index_, 64 / 8)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_4H : public VRegArranged {
|
||||
constexpr explicit VReg_4H(int reg_index_)
|
||||
: VRegArranged(64, reg_index_, 64 / 4)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_2S : public VRegArranged {
|
||||
constexpr explicit VReg_2S(int reg_index_)
|
||||
: VRegArranged(64, reg_index_, 64 / 2)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_1D : public VRegArranged {
|
||||
constexpr explicit VReg_1D(int reg_index_)
|
||||
: VRegArranged(64, reg_index_, 64 / 1)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_16B : public VRegArranged {
|
||||
constexpr explicit VReg_16B(int reg_index_)
|
||||
: VRegArranged(128, reg_index_, 128 / 16)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_8H : public VRegArranged {
|
||||
constexpr explicit VReg_8H(int reg_index_)
|
||||
: VRegArranged(128, reg_index_, 128 / 8)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_4S : public VRegArranged {
|
||||
constexpr explicit VReg_4S(int reg_index_)
|
||||
: VRegArranged(128, reg_index_, 128 / 4)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_2D : public VRegArranged {
|
||||
constexpr explicit VReg_2D(int reg_index_)
|
||||
: VRegArranged(128, reg_index_, 128 / 2)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct VReg_1Q : public VRegArranged {
|
||||
constexpr explicit VReg_1Q(int reg_index_)
|
||||
: VRegArranged(128, reg_index_, 128 / 1)
|
||||
{}
|
||||
|
||||
template<typename Policy>
|
||||
friend class BasicCodeGenerator;
|
||||
};
|
||||
|
||||
struct Elem {
|
||||
constexpr explicit Elem(unsigned esize_, int reg_, unsigned elem_index_)
|
||||
: m_esize(esize_), m_reg(reg_), m_elem_index(elem_index_)
|
||||
{
|
||||
if (elem_index_ >= 128 / esize_)
|
||||
throw "invalid elem_index";
|
||||
}
|
||||
|
||||
constexpr unsigned esize() const { return m_esize; }
|
||||
constexpr int reg_index() const { return m_reg; }
|
||||
constexpr unsigned elem_index() const { return m_elem_index; }
|
||||
|
||||
private:
|
||||
unsigned m_esize;
|
||||
int m_reg;
|
||||
unsigned m_elem_index;
|
||||
};
|
||||
|
||||
template<typename E>
|
||||
struct ElemSelector {
|
||||
constexpr explicit ElemSelector(int reg_index_)
|
||||
: m_reg_index(reg_index_)
|
||||
{}
|
||||
|
||||
constexpr int reg_index() const { return m_reg_index; }
|
||||
|
||||
constexpr E operator[](unsigned elem_index) const { return E{m_reg_index, elem_index}; }
|
||||
|
||||
private:
|
||||
int m_reg_index;
|
||||
};
|
||||
|
||||
struct BElem : public Elem {
|
||||
constexpr explicit BElem(int reg_, unsigned elem_index_)
|
||||
: Elem(2, reg_, elem_index_)
|
||||
{}
|
||||
};
|
||||
|
||||
struct HElem : public Elem {
|
||||
constexpr explicit HElem(int reg_, unsigned elem_index_)
|
||||
: Elem(2, reg_, elem_index_)
|
||||
{}
|
||||
};
|
||||
|
||||
struct SElem : public Elem {
|
||||
constexpr explicit SElem(int reg_, unsigned elem_index_)
|
||||
: Elem(4, reg_, elem_index_)
|
||||
{}
|
||||
};
|
||||
|
||||
struct DElem : public Elem {
|
||||
constexpr explicit DElem(int reg_, unsigned elem_index_)
|
||||
: Elem(8, reg_, elem_index_)
|
||||
{}
|
||||
};
|
||||
|
||||
struct DElem_1 : public DElem {
|
||||
constexpr /* implict */ DElem_1(DElem inner)
|
||||
: DElem(inner)
|
||||
{
|
||||
if (inner.elem_index() != 1)
|
||||
throw "invalid DElem_1";
|
||||
}
|
||||
};
|
||||
|
||||
constexpr BReg VReg::toB() const
|
||||
{
|
||||
return BReg{index()};
|
||||
}
|
||||
constexpr HReg VReg::toH() const
|
||||
{
|
||||
return HReg{index()};
|
||||
}
|
||||
constexpr SReg VReg::toS() const
|
||||
{
|
||||
return SReg{index()};
|
||||
}
|
||||
constexpr DReg VReg::toD() const
|
||||
{
|
||||
return DReg{index()};
|
||||
}
|
||||
constexpr QReg VReg::toQ() const
|
||||
{
|
||||
return QReg{index()};
|
||||
}
|
||||
|
||||
struct VRegSelector {
|
||||
constexpr explicit VRegSelector(int reg_index)
|
||||
: m_reg_index(reg_index)
|
||||
{}
|
||||
|
||||
constexpr int index() const { return m_reg_index; }
|
||||
|
||||
constexpr ElemSelector<BElem> B() const { return ElemSelector<BElem>(index()); }
|
||||
constexpr ElemSelector<HElem> H() const { return ElemSelector<HElem>(index()); }
|
||||
constexpr ElemSelector<SElem> S() const { return ElemSelector<SElem>(index()); }
|
||||
constexpr ElemSelector<DElem> D() const { return ElemSelector<DElem>(index()); }
|
||||
|
||||
constexpr VReg_8B B8() const { return VReg_8B{index()}; }
|
||||
constexpr VReg_4H H4() const { return VReg_4H{index()}; }
|
||||
constexpr VReg_2S S2() const { return VReg_2S{index()}; }
|
||||
constexpr VReg_1D D1() const { return VReg_1D{index()}; }
|
||||
constexpr VReg_16B B16() const { return VReg_16B{index()}; }
|
||||
constexpr VReg_8H H8() const { return VReg_8H{index()}; }
|
||||
constexpr VReg_4S S4() const { return VReg_4S{index()}; }
|
||||
constexpr VReg_2D D2() const { return VReg_2D{index()}; }
|
||||
constexpr VReg_1Q Q1() const { return VReg_1Q{index()}; }
|
||||
|
||||
private:
|
||||
int m_reg_index;
|
||||
};
|
||||
|
||||
} // namespace oaknut
|
||||
24
include/oaknut/impl/string_literal.hpp
Normal file
24
include/oaknut/impl/string_literal.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 merryhime <https://mary.rs>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
namespace oaknut {
|
||||
|
||||
template<size_t N>
|
||||
struct StringLiteral {
|
||||
constexpr StringLiteral(const char (&str)[N])
|
||||
{
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
static constexpr std::size_t strlen = N - 1;
|
||||
static constexpr std::size_t size = N;
|
||||
|
||||
char value[N];
|
||||
};
|
||||
|
||||
} // namespace oaknut
|
||||
Reference in New Issue
Block a user