Skip to content
This repository has been archived by the owner on Mar 5, 2024. It is now read-only.

Commit

Permalink
oaknut: Fix edgecases in MOVP2R on +/-4GiB boundary
Browse files Browse the repository at this point in the history
  • Loading branch information
merryhime committed Nov 17, 2023
1 parent d8634ea commit 316d886
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 5 deletions.
6 changes: 6 additions & 0 deletions include/oaknut/impl/offset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ struct PageOffset {
return static_cast<std::uint32_t>(((diff & 3) << (bitsize - 2)) | (diff >> 2));
}

static bool valid(std::uintptr_t current_addr, std::uintptr_t target)
{
std::uint64_t diff = static_cast<std::uint64_t>((static_cast<std::int64_t>(target) >> shift_amount) - (static_cast<std::int64_t>(current_addr) >> shift_amount));
return detail::sign_extend<bitsize>(diff) == diff;
}

private:
template<typename Policy>
friend class BasicCodeGenerator;
Expand Down
4 changes: 2 additions & 2 deletions include/oaknut/oaknut.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ class BasicCodeGenerator : public Policy {
void MOVP2R(XReg xd, const void* addr)
{
if constexpr (Policy::has_absolute_addresses) {
int64_t diff = reinterpret_cast<uint64_t>(addr) - Policy::current_address();
const int64_t diff = reinterpret_cast<std::uint64_t>(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<std::uintptr_t>(addr))) {
ADRL(xd, addr);
} else {
MOV(xd, reinterpret_cast<uint64_t>(addr));
Expand Down
32 changes: 29 additions & 3 deletions tests/basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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::int64_t>(std::numeric_limits<std::int64_t>::min(),
std::numeric_limits<std::int64_t>::max());
const std::intptr_t value = reinterpret_cast<std::intptr_t>(mem.ptr()) + diff;
const std::intptr_t value = reinterpret_cast<std::intptr_t>(mem_ptr) + diff;

CodeGenerator code{mem.ptr()};
CodeGenerator code{mem_ptr};

auto f = code.ptr<std::uint64_t (*)()>();
mem.unprotect();
Expand All @@ -241,3 +242,28 @@ TEST_CASE("MOVP2R", "[slow]")
REQUIRE(f() == static_cast<std::uint64_t>(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<std::intptr_t>(mem_ptr) + diff;

CodeGenerator code{mem_ptr};

auto f = code.ptr<std::uint64_t (*)()>();
mem.unprotect();
code.MOVP2R(X0, reinterpret_cast<void*>(value));
code.RET();
mem.protect();
mem.invalidate_all();

REQUIRE(f() == static_cast<std::uint64_t>(value));
};
test(i);
test(-i);
}
}

0 comments on commit 316d886

Please sign in to comment.