Skip to content

Commit abf3b8c

Browse files
committed
CPU/CodeCache: Move JIT write protect out of MemMap
Gets rid of the TLS variable, and lazily enables it. Fixes crash on Apple Silicon, regression from 5de2b77.
1 parent f9254e2 commit abf3b8c

3 files changed

Lines changed: 60 additions & 65 deletions

File tree

src/common/memmap.cpp

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
#include "windows_headers.h"
1818
#include <Psapi.h>
1919
#elif defined(__APPLE__)
20-
#ifdef __aarch64__
21-
#include <pthread.h> // pthread_jit_write_protect_np()
22-
#endif
2320
#include <mach-o/dyld.h>
2421
#include <mach-o/getsect.h>
2522
#include <mach/mach_init.h>
@@ -618,34 +615,6 @@ bool SharedMemoryMappingArea::Unmap(void* map_base, size_t map_size)
618615
return true;
619616
}
620617

621-
#ifdef __aarch64__
622-
623-
static thread_local int s_code_write_depth = 0;
624-
625-
void MemMap::BeginCodeWrite()
626-
{
627-
// DEBUG_LOG("BeginCodeWrite(): {}", s_code_write_depth);
628-
if ((s_code_write_depth++) == 0)
629-
{
630-
// DEBUG_LOG(" pthread_jit_write_protect_np(0)");
631-
pthread_jit_write_protect_np(0);
632-
}
633-
}
634-
635-
void MemMap::EndCodeWrite()
636-
{
637-
// DEBUG_LOG("EndCodeWrite(): {}", s_code_write_depth);
638-
639-
DebugAssert(s_code_write_depth > 0);
640-
if ((--s_code_write_depth) == 0)
641-
{
642-
// DEBUG_LOG(" pthread_jit_write_protect_np(1)");
643-
pthread_jit_write_protect_np(1);
644-
}
645-
}
646-
647-
#endif
648-
649618
#else
650619

651620
u32 MemMap::GetRuntimePageSize()

src/common/memmap.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -81,16 +81,6 @@ ALWAYS_INLINE static void FlushInstructionCache(void* address, size_t size) { }
8181
void FlushInstructionCache(void* address, size_t size);
8282
#endif
8383

84-
/// JIT write protect for Apple Silicon. Needs to be called prior to writing to any RWX pages.
85-
#if !defined(__APPLE__) || !defined(__aarch64__)
86-
// clang-format off
87-
ALWAYS_INLINE static void BeginCodeWrite() { }
88-
ALWAYS_INLINE static void EndCodeWrite() { }
89-
// clang-format on
90-
#else
91-
void BeginCodeWrite();
92-
void EndCodeWrite();
93-
#endif
9484
} // namespace MemMap
9585

9686
class SharedMemoryMappingArea

src/core/cpu_code_cache.cpp

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
1+
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
22
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
33

44
#include "bus.h"
@@ -20,6 +20,10 @@
2020
#include "common/log.h"
2121
#include "common/memmap.h"
2222

23+
#if defined(__APPLE__) && defined(CPU_ARCH_ARM64)
24+
#include <pthread.h> // pthread_jit_write_protect_np()
25+
#endif
26+
2327
LOG_CHANNEL(CodeCache);
2428

2529
// Enable dumping of recompiled block code size statistics.
@@ -69,6 +73,11 @@ static constexpr u32 RECOMPILER_FAR_CODE_CACHE_SIZE = 16 * 1024 * 1024;
6973
#define USE_CODE_BUFFER_SECTION 1
7074
#endif
7175

76+
/// JIT write protect for Apple Silicon. Needs to be called prior to writing to any RWX pages.
77+
#if defined(__APPLE__) && defined(CPU_ARCH_ARM64)
78+
#define NEEDS_JIT_WRITE_PROTECT 1
79+
#endif
80+
7281
static void AllocateLUTs();
7382
static void DeallocateLUTs();
7483
static void ResetCodeLUT();
@@ -98,6 +107,7 @@ template<PGXPMode pgxp_mode>
98107
static void BacklinkBlocks(u32 pc, const void* dst);
99108
static void UnlinkBlockExits(Block* block);
100109
static void ResetCodeBuffer();
110+
static void JITWriteProtect(bool enabled);
101111

102112
static void CompileASMFunctions();
103113
static bool CompileBlock(Block* block);
@@ -138,6 +148,10 @@ struct Locals
138148
u32 total_host_instructions_emitted = 0;
139149
u32 total_host_code_used_by_instructions = 0;
140150
#endif
151+
152+
#ifdef NEEDS_JIT_WRITE_PROTECT
153+
bool jit_write_protect_enabled = false;
154+
#endif
141155
};
142156
} // namespace
143157

@@ -200,6 +214,11 @@ bool CPU::CodeCache::ProcessStartup(Error* error)
200214
if (!PageFaultHandler::Install(error))
201215
return false;
202216

217+
#ifdef NEEDS_JIT_WRITE_PROTECT
218+
// Write protect starts enabled.
219+
s_locals.jit_write_protect_enabled = true;
220+
#endif
221+
203222
return true;
204223
}
205224

@@ -219,8 +238,7 @@ void CPU::CodeCache::Reset()
219238
if (IsUsingRecompiler())
220239
{
221240
ResetCodeBuffer();
222-
CompileASMFunctions();
223-
ResetCodeLUT();
241+
JITWriteProtect(true);
224242
}
225243
}
226244

@@ -598,8 +616,6 @@ void CPU::CodeCache::InvalidateBlocksWithPageIndex(u32 index)
598616
if (!ppi.first_block_in_page)
599617
return;
600618

601-
MemMap::BeginCodeWrite();
602-
603619
Block* block = ppi.first_block_in_page;
604620
while (block)
605621
{
@@ -610,7 +626,7 @@ void CPU::CodeCache::InvalidateBlocksWithPageIndex(u32 index)
610626
ppi.first_block_in_page = nullptr;
611627
ppi.last_block_in_page = nullptr;
612628

613-
MemMap::EndCodeWrite();
629+
JITWriteProtect(true);
614630
}
615631

616632
CPU::CodeCache::PageProtectionMode CPU::CodeCache::GetProtectionModeForPC(u32 pc)
@@ -646,7 +662,6 @@ void CPU::CodeCache::InvalidateBlock(Block* block, BlockState new_state)
646662
void CPU::CodeCache::InvalidateAllRAMBlocks()
647663
{
648664
// TODO: maybe combine the backlink into one big instruction flush cache?
649-
MemMap::BeginCodeWrite();
650665

651666
for (Block* block : s_locals.blocks)
652667
{
@@ -657,13 +672,14 @@ void CPU::CodeCache::InvalidateAllRAMBlocks()
657672
}
658673
}
659674

675+
JITWriteProtect(true);
676+
660677
for (PageProtectionInfo& ppi : s_page_protection)
661678
{
662679
ppi.first_block_in_page = nullptr;
663680
ppi.last_block_in_page = nullptr;
664681
}
665682

666-
MemMap::EndCodeWrite();
667683
Bus::ClearRAMCodePageFlags();
668684
}
669685

@@ -1289,7 +1305,6 @@ void CPU::CodeCache::CompileOrRevalidateBlock(u32 start_pc)
12891305
{
12901306
// TODO: this doesn't currently handle when the cache overflows...
12911307
DebugAssert(IsUsingRecompiler());
1292-
MemMap::BeginCodeWrite();
12931308

12941309
Block* block = LookupBlock(start_pc);
12951310
if (block)
@@ -1301,7 +1316,7 @@ void CPU::CodeCache::CompileOrRevalidateBlock(u32 start_pc)
13011316
DebugAssert(block->host_code);
13021317
SetCodeLUT(start_pc, block->host_code);
13031318
BacklinkBlocks(start_pc, block->host_code);
1304-
MemMap::EndCodeWrite();
1319+
JITWriteProtect(true);
13051320
return;
13061321
}
13071322

@@ -1319,7 +1334,7 @@ void CPU::CodeCache::CompileOrRevalidateBlock(u32 start_pc)
13191334
ERROR_LOG("Failed to read block at 0x{:08X}, falling back to uncached interpreter", start_pc);
13201335
SetCodeLUT(start_pc, g_recompiler_functions.interpret_block);
13211336
BacklinkBlocks(start_pc, g_recompiler_functions.interpret_block);
1322-
MemMap::EndCodeWrite();
1337+
JITWriteProtect(true);
13231338
return;
13241339
}
13251340

@@ -1333,22 +1348,31 @@ void CPU::CodeCache::CompileOrRevalidateBlock(u32 start_pc)
13331348
free_far_code_space < Recompiler::MIN_CODE_RESERVE_FOR_BLOCK)
13341349
{
13351350
ERROR_LOG("Out of code space while compiling {:08X}. Resetting code cache.", start_pc);
1336-
CodeCache::Reset();
1351+
ResetCodeBuffer();
13371352
}
13381353

1339-
if ((block = CreateBlock(start_pc, s_locals.block_instructions, metadata)) == nullptr || block->size == 0 ||
1340-
!CompileBlock(block))
1354+
if ((block = CreateBlock(start_pc, s_locals.block_instructions, metadata)) == nullptr || block->size == 0)
1355+
{
1356+
ERROR_LOG("Failed to create block at 0x{:08X}, falling back to uncached interpreter", start_pc);
1357+
SetCodeLUT(start_pc, g_recompiler_functions.interpret_block);
1358+
BacklinkBlocks(start_pc, g_recompiler_functions.interpret_block);
1359+
JITWriteProtect(true);
1360+
return;
1361+
}
1362+
1363+
JITWriteProtect(false);
1364+
if (!CompileBlock(block))
13411365
{
13421366
ERROR_LOG("Failed to compile block at 0x{:08X}, falling back to uncached interpreter", start_pc);
13431367
SetCodeLUT(start_pc, g_recompiler_functions.interpret_block);
13441368
BacklinkBlocks(start_pc, g_recompiler_functions.interpret_block);
1345-
MemMap::EndCodeWrite();
1369+
JITWriteProtect(true);
13461370
return;
13471371
}
13481372

13491373
SetCodeLUT(start_pc, block->host_code);
13501374
BacklinkBlocks(start_pc, block->host_code);
1351-
MemMap::EndCodeWrite();
1375+
JITWriteProtect(true);
13521376
}
13531377

13541378
void CPU::CodeCache::DiscardAndRecompileBlock(u32 start_pc)
@@ -1419,6 +1443,7 @@ void CPU::CodeCache::BacklinkBlocks(u32 pc, const void* dst)
14191443
{
14201444
DEBUG_LOG("Backlinking {} with dst pc {:08X} to {}{}", it->second, pc, dst,
14211445
(dst == g_recompiler_functions.compile_or_revalidate_block) ? "[compiler]" : "");
1446+
JITWriteProtect(false);
14221447
EmitJump(it->second, dst, true);
14231448
}
14241449
}
@@ -1433,10 +1458,10 @@ void CPU::CodeCache::UnlinkBlockExits(Block* block)
14331458

14341459
void CPU::CodeCache::ResetCodeBuffer()
14351460
{
1461+
JITWriteProtect(false);
1462+
14361463
if (s_locals.code_used > 0 || s_locals.far_code_used > 0)
14371464
{
1438-
MemMap::BeginCodeWrite();
1439-
14401465
if (s_locals.code_used > 0)
14411466
{
14421467
std::memset(s_locals.code_ptr, 0, s_locals.code_used);
@@ -1448,8 +1473,6 @@ void CPU::CodeCache::ResetCodeBuffer()
14481473
std::memset(s_locals.far_code_ptr, 0, s_locals.far_code_used);
14491474
MemMap::FlushInstructionCache(s_locals.far_code_ptr, s_locals.far_code_used);
14501475
}
1451-
1452-
MemMap::EndCodeWrite();
14531476
}
14541477

14551478
#ifdef USE_CODE_BUFFER_SECTION
@@ -1468,6 +1491,20 @@ void CPU::CodeCache::ResetCodeBuffer()
14681491
s_locals.far_code_ptr = (far_code_size > 0) ? (static_cast<u8*>(s_locals.code_ptr) + s_locals.code_size) : nullptr;
14691492
s_locals.free_far_code_ptr = s_locals.far_code_ptr;
14701493
s_locals.far_code_used = 0;
1494+
1495+
CompileASMFunctions();
1496+
ResetCodeLUT();
1497+
}
1498+
1499+
ALWAYS_INLINE_RELEASE void CPU::CodeCache::JITWriteProtect(bool enabled)
1500+
{
1501+
#ifdef NEEDS_JIT_WRITE_PROTECT
1502+
if (enabled == s_locals.jit_write_protect_enabled)
1503+
return;
1504+
1505+
s_locals.jit_write_protect_enabled = enabled;
1506+
pthread_jit_write_protect_np(enabled);
1507+
#endif
14711508
}
14721509

14731510
u8* CPU::CodeCache::GetFreeCodePointer()
@@ -1546,7 +1583,7 @@ const void* CPU::CodeCache::GetInterpretUncachedBlockFunction()
15461583

15471584
void CPU::CodeCache::CompileASMFunctions()
15481585
{
1549-
MemMap::BeginCodeWrite();
1586+
JITWriteProtect(false);
15501587

15511588
#ifdef DUMP_CODE_SIZE_STATS
15521589
s_locals.total_instructions_compiled = 0;
@@ -1561,7 +1598,6 @@ void CPU::CodeCache::CompileASMFunctions()
15611598
#endif
15621599

15631600
CommitCode(asm_size);
1564-
MemMap::EndCodeWrite();
15651601
}
15661602

15671603
bool CPU::CodeCache::CompileBlock(Block* block)
@@ -1708,7 +1744,7 @@ PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exc
17081744
static_cast<unsigned>(info.address_register), static_cast<unsigned>(info.data_register),
17091745
info.AccessSizeInBytes(), static_cast<unsigned>(info.is_signed));
17101746

1711-
MemMap::BeginCodeWrite();
1747+
JITWriteProtect(false);
17121748

17131749
BackpatchLoadStore(exception_pc, info);
17141750

@@ -1726,7 +1762,7 @@ PageFaultHandler::HandlerResult CPU::CodeCache::HandleFastmemException(void* exc
17261762
block->compile_count = 1;
17271763
}
17281764

1729-
MemMap::EndCodeWrite();
1765+
JITWriteProtect(true);
17301766

17311767
// and store the pc in the faulting list, so that we don't emit another fastmem loadstore
17321768
s_locals.fastmem_faulting_pcs.insert(info.guest_pc);

0 commit comments

Comments
 (0)