diff --git a/include/oaknut/impl/offset.hpp b/include/oaknut/impl/offset.hpp index 9a223d1..a70941f 100644 --- a/include/oaknut/impl/offset.hpp +++ b/include/oaknut/impl/offset.hpp @@ -85,6 +85,12 @@ struct PageOffset { return static_cast(((diff & 3) << (bitsize - 2)) | (diff >> 2)); } + static bool valid(std::uintptr_t current_addr, std::uintptr_t target) + { + std::uint64_t diff = static_cast((static_cast(target) >> shift_amount) - (static_cast(current_addr) >> shift_amount)); + return detail::sign_extend(diff) == diff; + } + private: template friend class BasicCodeGenerator; diff --git a/include/oaknut/oaknut.hpp b/include/oaknut/oaknut.hpp index fc0ffcc..39b9a03 100644 --- a/include/oaknut/oaknut.hpp +++ b/include/oaknut/oaknut.hpp @@ -161,10 +161,10 @@ public: void MOVP2R(XReg xd, const void* addr) { if constexpr (Policy::has_absolute_addresses) { - int64_t diff = reinterpret_cast(addr) - Policy::current_address(); + const int64_t diff = reinterpret_cast(addr) - Policy::current_address(); if (diff >= -0xF'FFFF && diff <= 0xF'FFFF) { ADR(xd, addr); - } else if (diff >= -int64_t{0xFFFF'FFFF} && diff <= int64_t{0xFFFF'FFFF}) { + } else if (PageOffset<21, 12>::valid(Policy::current_address(), reinterpret_cast(addr))) { ADRL(xd, addr); } else { MOV(xd, reinterpret_cast(addr)); diff --git a/tests/basic.cpp b/tests/basic.cpp index eef6280..91bc846 100644 --- a/tests/basic.cpp +++ b/tests/basic.cpp @@ -220,16 +220,17 @@ TEST_CASE("ADRL (far)", "[slow]") } } -TEST_CASE("MOVP2R", "[slow]") +TEST_CASE("MOVP2R (far)", "[slow]") { CodeBlock mem{4096}; + std::uint32_t* const mem_ptr = mem.ptr() + 42; // create small offset for testing for (int i = 0; i < 0x200000; i++) { const std::int64_t diff = RandInt(std::numeric_limits::min(), std::numeric_limits::max()); - const std::intptr_t value = reinterpret_cast(mem.ptr()) + diff; + const std::intptr_t value = reinterpret_cast(mem_ptr) + diff; - CodeGenerator code{mem.ptr()}; + CodeGenerator code{mem_ptr}; auto f = code.ptr(); mem.unprotect(); @@ -241,3 +242,28 @@ TEST_CASE("MOVP2R", "[slow]") REQUIRE(f() == static_cast(value)); } } + +TEST_CASE("MOVP2R (4GiB boundary)") +{ + CodeBlock mem{4096}; + std::uint32_t* const mem_ptr = mem.ptr() + 42; // create small offset for testing + + for (std::int64_t i = 0xFFFF'F000; i < 0x1'0000'1000; i++) { + const auto test = [&](std::int64_t diff) { + const std::intptr_t value = reinterpret_cast(mem_ptr) + diff; + + CodeGenerator code{mem_ptr}; + + auto f = code.ptr(); + mem.unprotect(); + code.MOVP2R(X0, reinterpret_cast(value)); + code.RET(); + mem.protect(); + mem.invalidate_all(); + + REQUIRE(f() == static_cast(value)); + }; + test(i); + test(-i); + } +}