Apple M1 Support for MacOS
This commit adds support for compiling Dolphin for ARM on MacOS so that it can run natively on the M1 processors without running through Rosseta2 emulation providing a 30-50% performance speedup and less hitches from Rosseta2. It consists of several key changes: - Adding support for W^X allocation(MAP_JIT) for the ARM JIT - Adding the machine context and config info to identify the M1 processor - Additions to the build system and docs to support building universal binaries - Adding code signing entitlements to access the MAP_JIT functionality - Updating the MoltenVK libvulkan.dylib to a newer version with M1 support
This commit is contained in:
1 parent
f9b0225b69
commit
4ecb3084b7
18 files changed
+284
-21
No files matched your search
@@ -277,7 +277,8 @@ void DolphinAnalytics::MakeBaseBuilder()
|
||||
builder.AddData("android-version", s_get_val_func("DEVICE_OS"));
|
||||
#elif defined(__APPLE__)
|
||||
builder.AddData("os-type", "osx");
|
||||
|
||||
//objc_msgSend is only available on x86
|
||||
#ifndef _M_ARM_64
|
||||
// id processInfo = [NSProcessInfo processInfo]
|
||||
id processInfo = reinterpret_cast<id (*)(Class, SEL)>(objc_msgSend)(
|
||||
objc_getClass("NSProcessInfo"), sel_getUid("processInfo"));
|
||||
@@ -298,6 +299,8 @@ void DolphinAnalytics::MakeBaseBuilder()
|
||||
builder.AddData("osx-ver-minor", version.minor_version);
|
||||
builder.AddData("osx-ver-bugfix", version.patch_version);
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif defined(__linux__)
|
||||
builder.AddData("os-type", "linux");
|
||||
#elif defined(__FreeBSD__)
|
||||
|
||||
@@ -67,6 +67,12 @@ typedef x86_thread_state64_t SContext;
|
||||
#define CTX_R14 __r14
|
||||
#define CTX_R15 __r15
|
||||
#define CTX_RIP __rip
|
||||
#elif _M_ARM_64
|
||||
typedef arm_thread_state64_t SContext;
|
||||
#define CTX_REG(x) __x[x]
|
||||
#define CTX_LR __x[30]
|
||||
#define CTX_SP __sp
|
||||
#define CTX_PC __pc
|
||||
#else
|
||||
#error No context definition for architecture
|
||||
#endif
|
||||
|
||||
@@ -25,6 +25,20 @@
|
||||
#include <unistd.h> // Needed for _POSIX_VERSION
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#ifdef _M_X86_64
|
||||
#define THREAD_STATE64_COUNT x86_THREAD_STATE64_COUNT
|
||||
#define THREAD_STATE64 x86_THREAD_STATE64
|
||||
#define thread_state64_t x86_thread_state64_t
|
||||
#elif defined(_M_ARM_64)
|
||||
#define THREAD_STATE64_COUNT ARM_THREAD_STATE64_COUNT
|
||||
#define THREAD_STATE64 ARM_THREAD_STATE64
|
||||
#define thread_state64_t arm_thread_state64_t
|
||||
#else
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace EMM
|
||||
{
|
||||
#ifdef _WIN32
|
||||
@@ -123,7 +137,7 @@ static void ExceptionThread(mach_port_t port)
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[x86_THREAD_STATE64_COUNT];
|
||||
natural_t old_state[THREAD_STATE64_COUNT];
|
||||
mach_msg_trailer_t trailer;
|
||||
} msg_in;
|
||||
|
||||
@@ -134,7 +148,7 @@ static void ExceptionThread(mach_port_t port)
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[x86_THREAD_STATE64_COUNT];
|
||||
natural_t new_state[THREAD_STATE64_COUNT];
|
||||
} msg_out;
|
||||
#pragma pack()
|
||||
memset(&msg_in, 0xee, sizeof(msg_in));
|
||||
@@ -165,13 +179,13 @@ static void ExceptionThread(mach_port_t port)
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg_in.flavor != x86_THREAD_STATE64)
|
||||
if (msg_in.flavor != THREAD_STATE64)
|
||||
{
|
||||
PanicAlertFmt("unknown flavor {} (expected {})", msg_in.flavor, x86_THREAD_STATE64);
|
||||
PanicAlertFmt("unknown flavor {} (expected {})", msg_in.flavor, THREAD_STATE64);
|
||||
return;
|
||||
}
|
||||
|
||||
x86_thread_state64_t* state = (x86_thread_state64_t*)msg_in.old_state;
|
||||
thread_state64_t* state = (thread_state64_t*)msg_in.old_state;
|
||||
|
||||
bool ok = JitInterface::HandleFault((uintptr_t)msg_in.code[1], state);
|
||||
|
||||
@@ -184,9 +198,9 @@ static void ExceptionThread(mach_port_t port)
|
||||
if (ok)
|
||||
{
|
||||
msg_out.RetCode = KERN_SUCCESS;
|
||||
msg_out.flavor = x86_THREAD_STATE64;
|
||||
msg_out.new_stateCnt = x86_THREAD_STATE64_COUNT;
|
||||
memcpy(msg_out.new_state, msg_in.old_state, x86_THREAD_STATE64_COUNT * sizeof(natural_t));
|
||||
msg_out.flavor = THREAD_STATE64;
|
||||
msg_out.new_stateCnt = THREAD_STATE64_COUNT;
|
||||
memcpy(msg_out.new_state, msg_in.old_state, THREAD_STATE64_COUNT * sizeof(natural_t));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -218,7 +232,7 @@ void InstallExceptionHandler()
|
||||
// Debuggers set the task port, so we grab the thread port.
|
||||
CheckKR("thread_set_exception_ports",
|
||||
thread_set_exception_ports(mach_thread_self(), EXC_MASK_BAD_ACCESS, port,
|
||||
EXCEPTION_STATE | MACH_EXCEPTION_CODES, x86_THREAD_STATE64));
|
||||
EXCEPTION_STATE | MACH_EXCEPTION_CODES, THREAD_STATE64));
|
||||
// ...and get rid of our copy so that MACH_NOTIFY_NO_SENDERS works.
|
||||
CheckKR("mach_port_mod_refs",
|
||||
mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, -1));
|
||||
|
||||
@@ -73,6 +73,8 @@ void JitArm64::Init()
|
||||
|
||||
bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
|
||||
{
|
||||
//Ifdef this since the exception handler runs on a separate thread on Mac OS X (ARM)
|
||||
#if !defined(__APPLE__) && !defined(_M_ARM_64)
|
||||
// We can't handle any fault from other threads.
|
||||
if (!Core::IsCPUThread())
|
||||
{
|
||||
@@ -80,6 +82,7 @@ bool JitArm64::HandleFault(uintptr_t access_address, SContext* ctx)
|
||||
DoBacktrace(access_address, ctx);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool success = false;
|
||||
|
||||
@@ -124,11 +127,13 @@ void JitArm64::ClearCache()
|
||||
m_handler_to_loc.clear();
|
||||
|
||||
blocks.Clear();
|
||||
Common::JITPageWriteEnableExecuteDisable();
|
||||
ClearCodeSpace();
|
||||
farcode.ClearCodeSpace();
|
||||
UpdateMemoryOptions();
|
||||
|
||||
GenerateAsm();
|
||||
Common::JITPageWriteDisableExecuteEnable();
|
||||
}
|
||||
|
||||
void JitArm64::Shutdown()
|
||||
@@ -596,6 +601,7 @@ void JitArm64::Jit(u32)
|
||||
{
|
||||
ClearCache();
|
||||
}
|
||||
Common::JITPageWriteEnableExecuteDisable();
|
||||
|
||||
std::size_t block_size = m_code_buffer.size();
|
||||
const u32 em_address = PowerPC::ppcState.pc;
|
||||
@@ -624,6 +630,8 @@ void JitArm64::Jit(u32)
|
||||
JitBlock* b = blocks.AllocateBlock(em_address);
|
||||
DoJit(em_address, b, nextPC);
|
||||
blocks.FinalizeBlock(*b, jo.enableBlocklink, code_block.m_physical_addresses);
|
||||
Common::JITPageWriteDisableExecuteEnable();
|
||||
|
||||
}
|
||||
|
||||
void JitArm64::DoJit(u32 em_address, JitBlock* b, u32 nextPC)
|
||||
|
||||
@@ -59,11 +59,12 @@ void JitArm64BlockCache::WriteLinkBlock(Arm64Gen::ARM64XEmitter& emit,
|
||||
|
||||
void JitArm64BlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest)
|
||||
{
|
||||
Common::JITPageWriteEnableExecuteDisable();
|
||||
u8* location = source.exitPtrs;
|
||||
ARM64XEmitter emit(location);
|
||||
|
||||
WriteLinkBlock(emit, source, dest);
|
||||
|
||||
Common::JITPageWriteDisableExecuteEnable();
|
||||
emit.FlushIcache();
|
||||
}
|
||||
|
||||
@@ -71,9 +72,9 @@ void JitArm64BlockCache::WriteDestroyBlock(const JitBlock& block)
|
||||
{
|
||||
// Only clear the entry points as we might still be within this block.
|
||||
ARM64XEmitter emit(block.checkedEntry);
|
||||
|
||||
Common::JITPageWriteEnableExecuteDisable();
|
||||
while (emit.GetWritableCodePtr() <= block.normalEntry)
|
||||
emit.BRK(0x123);
|
||||
|
||||
Common::JITPageWriteDisableExecuteEnable();
|
||||
emit.FlushIcache();
|
||||
}
|
||||
@@ -289,6 +289,7 @@ bool JitArm64::HandleFastmemFault(uintptr_t access_address, SContext* ctx)
|
||||
if ((const u8*)ctx->CTX_PC - fault_location > fastmem_area_length)
|
||||
return false;
|
||||
|
||||
Common::JITPageWriteEnableExecuteDisable();
|
||||
ARM64XEmitter emitter((u8*)fault_location);
|
||||
|
||||
emitter.BL(slow_handler_iter->second.slowmem_code);
|
||||
@@ -300,6 +301,8 @@ bool JitArm64::HandleFastmemFault(uintptr_t access_address, SContext* ctx)
|
||||
m_fault_to_handler.erase(slow_handler_iter);
|
||||
|
||||
emitter.FlushIcache();
|
||||
Common::JITPageWriteDisableExecuteEnable();
|
||||
|
||||
ctx->CTX_PC = reinterpret_cast<std::uintptr_t>(fault_location);
|
||||
return true;
|
||||
}
|
||||
@@ -25,6 +25,8 @@ using namespace Arm64Gen;
|
||||
|
||||
void JitArm64::GenerateAsm()
|
||||
{
|
||||
Common::JITPageWriteEnableExecuteDisable();
|
||||
|
||||
// This value is all of the callee saved registers that we are required to save.
|
||||
// According to the AACPS64 we need to save R19 ~ R30 and Q8 ~ Q15.
|
||||
const u32 ALL_CALLEE_SAVED = 0x7FF80000;
|
||||
@@ -197,6 +199,7 @@ void JitArm64::GenerateAsm()
|
||||
GenerateCommonAsm();
|
||||
|
||||
FlushIcache();
|
||||
Common::JITPageWriteDisableExecuteEnable();
|
||||
}
|
||||
|
||||
void JitArm64::GenerateCommonAsm()
|
||||
|
||||
Reference in new issue
Block a user