dynarmic/include/oaknut/impl/offset.hpp
Merry b65b07d566 Squashed 'externals/oaknut/' changes from 72f7ccd94..816481f10
816481f10 oaknut: 1.1.4
a75c1144e cmake: add export rules
ff4d78861 oaknut: CI: Add msvc-arm64 build
d0c317831 oaknut: CI: Pin catch2 version to v3.2.0
caf9cbbdc oaknut: Implement ADRL and MOVP2R pseudo instructions (#2)

git-subtree-dir: externals/oaknut
git-subtree-split: 816481f1036fb4177455ffa0c7b71e6ac9193d6c
2022-11-26 15:17:46 +00:00

130 lines
3.6 KiB
C++

// 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, std::size_t shift_amount>
struct PageOffset {
PageOffset(const 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)
{
std::uint64_t diff = (static_cast<std::uint64_t>(target) >> shift_amount) - (static_cast<std::uint64_t>(current_addr) >> shift_amount);
if (detail::sign_extend<bitsize>(diff) != diff)
throw "out of range";
diff &= detail::mask_from_size(bitsize);
return static_cast<std::uint32_t>(((diff & 3) << (bitsize - 2)) | (diff >> 2));
}
private:
template<typename Policy>
friend class BasicCodeGenerator;
std::variant<Label*, const 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