mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-06-26 14:44:59 -06:00
* common/aarch64: Allow generic code generator types Use the templated `BasicCodeGenerator` type rather than the specialized `CodeGenerator` type. Allows `VectorCodeGenerator` to work with these functions. * common/aarch64: Add `VectorCodeGenerator` to `CallFarFunction` `VectorCodeGenerator` will always do far-calls since we cannot resolve any absolute addresses here. * shader_jit_a64: Implement position-independent VectorCodeGenerator Generates more position-independent assembly to allow for code to be generated within a resizable vector before copying into executable memory, allowing for more compact memory allocations and usage rather than a statically defined worst-case for all-cases. `VectorCodeGenerator` will need to generate position-independent code rather than use absolute addresses. Assumes all far function calls in the case of `VectorCodeGenerator` to use absolute addresses rather than potentially use a relative `BL` branch after memory relocation.
54 lines
1.9 KiB
C++
54 lines
1.9 KiB
C++
// Copyright 2023 Citra Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#pragma once
|
|
|
|
#include "common/arch.h"
|
|
#if CITRA_ARCH(arm64)
|
|
|
|
#include <type_traits>
|
|
#include <oaknut/oaknut.hpp>
|
|
#include "common/aarch64/oaknut_abi.h"
|
|
|
|
namespace Common::A64 {
|
|
|
|
// BL can only reach targets within +-128MiB(24 bits)
|
|
inline bool IsWithin128M(uintptr_t ref, uintptr_t target) {
|
|
const u64 distance = target - (ref + 4);
|
|
return !(distance >= 0x800'0000ULL && distance <= ~0x800'0000ULL);
|
|
}
|
|
|
|
inline bool IsWithin128M(const oaknut::CodeGenerator& code, uintptr_t target) {
|
|
return IsWithin128M(code.xptr<uintptr_t>(), target);
|
|
}
|
|
|
|
template <typename T>
|
|
inline void CallFarFunction(oaknut::CodeGenerator& code, const T f) {
|
|
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
|
const std::uintptr_t addr = reinterpret_cast<std::uintptr_t>(f);
|
|
if (IsWithin128M(code, addr)) {
|
|
code.BL(reinterpret_cast<const void*>(f));
|
|
} else {
|
|
// X16(IP0) and X17(IP1) is the standard veneer register
|
|
// LR is also available as an intermediate register
|
|
// https://developer.arm.com/documentation/102374/0101/Procedure-Call-Standard
|
|
code.MOVP2R(oaknut::util::X16, reinterpret_cast<const void*>(f));
|
|
code.BLR(oaknut::util::X16);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
inline void CallFarFunction(oaknut::VectorCodeGenerator& code, const T f) {
|
|
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
|
// X16(IP0) and X17(IP1) is the standard veneer register
|
|
// LR is also available as an intermediate register
|
|
// https://developer.arm.com/documentation/102374/0101/Procedure-Call-Standard
|
|
code.MOVP2R(oaknut::util::X16, reinterpret_cast<const void*>(f));
|
|
code.BLR(oaknut::util::X16);
|
|
}
|
|
|
|
} // namespace Common::A64
|
|
|
|
#endif // CITRA_ARCH(arm64)
|