From aeb17ae00acec1f51c2bb31a2fcde53cda793fde Mon Sep 17 00:00:00 2001 From: Alex Vanderveen Date: Thu, 27 Aug 2026 16:30:26 -0400 Subject: [PATCH] runtime: give ten more cross-language globals C linkage in main.cpp Second report on #244: main.cpp references C-defined globals that MSVC cannot resolve. MSVC mangles namespace-scope variables; the Itanium ABI does not, so a C++ reference to a C global links on GCC/Clang and fails under MSVC with an unresolved external. main.cpp already has the fix pattern -- the file-scope extern "C" block added by the earlier MSVC pass, with a comment saying exactly this. Ten globals added since then never made it into the block: memory.c i_stat, i_mask, g_guest_store_count, g_vblank_ack_count interrupts.c g_vblank_raise_count, g_vblank_deliver_count dirty_ram_interp.c g_dirty_ram_blocks_run, g_dirty_pump_count overlay_loader.c g_call_unit_depth psx_bios_backend.c g_psx_dispatch_depth Each is referenced only through a block-scope `extern` inside a function. With no namespace-scope declaration in view, that block-scope declaration gives the entity C++ linkage, which is the failure. Declaring them in the file-scope extern "C" block fixes it without touching the use sites: the block-scope redeclarations now inherit C linkage, as the block's comment already describes for the symbols that were there. Which ten was measured, not eyeballed. Every block-scope extern in main.cpp (31 distinct symbols) was probed by appending `extern "C++" { extern T sym; }` to a copy of the file compiled with the target's real command line: a conflicting-linkage diagnostic means the symbol already has C linkage from an extern "C" header, silence means it does not. 21 came back already-C (declared in psx_cycles.h, cpu_state.h, debug_server.h and friends, or already in this block); these 10 came back C++. After the change all 31 probe as C linkage. Audited beyond main.cpp: it is the only C++ TU in the runtime with this pattern. beetle_libretro.cpp's `extern PS_GPU GPU` / `extern PS_CDC *PSX_CDC` are genuine C++ symbols from the Beetle core, correctly left with C++ linkage, and psx-beetle is not built under MSVC in any case. No behaviour change on the existing toolchains: main.cpp.o is byte-identical to its origin/master build. Full runtime suite on a clean build dir is 47/48, the single failure being gte_register_access_test, which fails identically at origin/master (43/44 there) -- it links gte.cpp + pgxp.cpp but references gpu_ws_precise_nclip_enabled, defined in gpu.c, which is not in that target. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_014YD7rrVK63Kncki6prB65S --- runtime/src/main.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/runtime/src/main.cpp b/runtime/src/main.cpp index 8f401795b..51d401958 100644 --- a/runtime/src/main.cpp +++ b/runtime/src/main.cpp @@ -187,6 +187,19 @@ extern "C" { extern uint64_t g_dirty_window_dispatches; extern uint32_t g_slice_exit_pc, g_slice_exit_reason, g_slice_exit_iter; extern uint32_t g_slice_exit_dispatchable, g_slice_exit_dirty, g_slice_exit_in_text, g_slice_exit_want; + /* memory.c */ + extern uint32_t i_stat, i_mask; + extern uint64_t g_guest_store_count; + extern uint64_t g_vblank_ack_count; + /* interrupts.c */ + extern uint64_t g_vblank_raise_count, g_vblank_deliver_count; + /* dirty_ram_interp.c */ + extern uint64_t g_dirty_ram_blocks_run; + extern uint64_t g_dirty_pump_count; + /* overlay_loader.c */ + extern int g_call_unit_depth; + /* psx_bios_backend.c */ + extern int g_psx_dispatch_depth; } /* memory.c */