diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index c2b3a1b..cb868e8 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -1,10 +1,13 @@ cmake_minimum_required(VERSION 3.16) project(mkw_recompiled) -if(NOT WIN32 OR NOT MINGW OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR +if((NOT (WIN32 AND MINGW)) AND (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")) + message(FATAL_ERROR "WiiCompiled requires Windows (LLVM-MinGW) or native Linux") +endif() +if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8 OR NOT CMAKE_SYSTEM_PROCESSOR MATCHES "^(AMD64|amd64|x86_64|X86_64)$") - message(FATAL_ERROR "WiiCompiled requires 64-bit LLVM-MinGW Clang on Windows") + message(FATAL_ERROR "WiiCompiled requires 64-bit Clang targeting x86_64") endif() if(NOT CMAKE_BUILD_TYPE STREQUAL "Release") message(FATAL_ERROR "WiiCompiled only supports Release builds") @@ -110,12 +113,22 @@ else() message(FATAL_ERROR "Requested aurora-main but ${MKW_AURORA_DIR} is missing") endif() set(DAWN_ENABLE_D3D11 OFF CACHE BOOL "" FORCE) - set(DAWN_ENABLE_D3D12 ON CACHE BOOL "" FORCE) + if(WIN32) + set(DAWN_ENABLE_D3D12 ON CACHE BOOL "" FORCE) + set(TINT_BUILD_HLSL_WRITER ON CACHE BOOL "" FORCE) + set(DAWN_USE_WINDOWS_UI OFF CACHE BOOL "" FORCE) + else() + # Non-Windows (Linux): mirrors aurora-main's own + # _aurora_dawn_set_platform_backends() choice for this platform - Vulkan only, no + # D3D/HLSL. Kept in sync here because this project's own CMake FORCEs these cache + # variables before aurora-main's add_subdirectory() runs, which pre-empts aurora's + # auto-detection (CACHE ... INTERNAL "" without FORCE never overrides an existing value). + set(DAWN_ENABLE_D3D12 OFF CACHE BOOL "" FORCE) + set(TINT_BUILD_HLSL_WRITER OFF CACHE BOOL "" FORCE) + endif() set(DAWN_ENABLE_VULKAN ON CACHE BOOL "" FORCE) - set(TINT_BUILD_HLSL_WRITER ON CACHE BOOL "" FORCE) set(DAWN_BUILD_SAMPLES OFF CACHE BOOL "" FORCE) set(DAWN_BUILD_TESTS OFF CACHE BOOL "" FORCE) - set(DAWN_USE_WINDOWS_UI OFF CACHE BOOL "" FORCE) # Provide a tiny stub for DXProgrammableCapture when the SDK/PIX headers are # missing (common on MinGW). Dawn only includes the header; no symbols are diff --git a/runtime/cmake/PublicProducts.cmake b/runtime/cmake/PublicProducts.cmake index 111d354..ff843b1 100644 --- a/runtime/cmake/PublicProducts.cmake +++ b/runtime/cmake/PublicProducts.cmake @@ -77,7 +77,9 @@ target_compile_definitions(mkw_runtime_common PRIVATE target_link_libraries(mkw_runtime_common PRIVATE aurora::gx aurora::pad aurora::si aurora::vi aurora::mtx) target_link_libraries(mkw_runtime_common PRIVATE mkw::pugixml mkw::toml11 mkw::cryptopp) -target_link_libraries(mkw_runtime_common PRIVATE shell32 windowsapp) +if(WIN32) + target_link_libraries(mkw_runtime_common PRIVATE shell32 windowsapp) +endif() if(MKW_CPPWINRT_INCLUDE_DIR) if(NOT EXISTS "${MKW_CPPWINRT_INCLUDE_DIR}/winrt/base.h") message(FATAL_ERROR @@ -204,26 +206,28 @@ function(mkw_configure_product target) $ $) endif() - target_link_libraries(${target} PRIVATE - dbghelp user32 winmm ws2_32 iphlpapi secur32 crypt32 windowsapp setupapi winusb) + if(WIN32) + target_link_libraries(${target} PRIVATE + dbghelp user32 winmm ws2_32 iphlpapi secur32 crypt32 windowsapp setupapi winusb) - set_target_properties(${target} PROPERTIES WIN32_EXECUTABLE TRUE) - foreach(runtime_dll libc++.dll libunwind.dll) - execute_process( - COMMAND "${CMAKE_CXX_COMPILER}" "--print-file-name=${runtime_dll}" - OUTPUT_VARIABLE runtime_dll_path - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT EXISTS "${runtime_dll_path}") - get_filename_component(mkw_compiler_bin "${CMAKE_CXX_COMPILER}" DIRECTORY) - set(runtime_dll_path "${mkw_compiler_bin}/${runtime_dll}") - endif() - if(NOT EXISTS "${runtime_dll_path}") - message(FATAL_ERROR "llvm-mingw runtime DLL not found: ${runtime_dll}") - endif() - add_custom_command(TARGET ${target} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${runtime_dll_path}" $) - endforeach() + set_target_properties(${target} PROPERTIES WIN32_EXECUTABLE TRUE) + foreach(runtime_dll libc++.dll libunwind.dll) + execute_process( + COMMAND "${CMAKE_CXX_COMPILER}" "--print-file-name=${runtime_dll}" + OUTPUT_VARIABLE runtime_dll_path + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT EXISTS "${runtime_dll_path}") + get_filename_component(mkw_compiler_bin "${CMAKE_CXX_COMPILER}" DIRECTORY) + set(runtime_dll_path "${mkw_compiler_bin}/${runtime_dll}") + endif() + if(NOT EXISTS "${runtime_dll_path}") + message(FATAL_ERROR "llvm-mingw runtime DLL not found: ${runtime_dll}") + endif() + add_custom_command(TARGET ${target} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${runtime_dll_path}" $) + endforeach() + endif() set(MKW_WII_BOOTSTRAP_SOURCE_DIR "${MKW_RUNTIME_SOURCE_DIR}/assets/wii") if(NOT EXISTS "${MKW_WII_BOOTSTRAP_SOURCE_DIR}/shared2/wc24") diff --git a/runtime/include/isa/ppc_isa_config.h b/runtime/include/isa/ppc_isa_config.h index 663b576..f4c41ea 100644 --- a/runtime/include/isa/ppc_isa_config.h +++ b/runtime/include/isa/ppc_isa_config.h @@ -11,11 +11,22 @@ inline constexpr bool MkwStateFreeAbiEnabled(uint32_t) noexcept return true; } +#if defined(_WIN32) #define MKW_PPC_FORCE_INLINE __forceinline #define MKW_PPC_NO_INLINE __declspec(noinline) +#define MKW_PPC_INTERNAL_CALL __regcall +#else +// __forceinline/__declspec are MS-extension keywords Clang only recognizes when targeting +// Windows (MSVC or mingw); native Linux Clang needs the GNU-attribute spellings instead. +// __regcall has no portable non-Windows equivalent worth chasing here - the extra register +// args it saves matter for the hot PPC interpreter loop on Windows, but plain calls are fine +// elsewhere. +#define MKW_PPC_FORCE_INLINE __attribute__((always_inline)) inline +#define MKW_PPC_NO_INLINE __attribute__((noinline)) +#define MKW_PPC_INTERNAL_CALL +#endif #define MKW_PPC_ALWAYS_INLINE_BODY __attribute__((always_inline)) #define MKW_PPC_COLD __attribute__((cold)) -#define MKW_PPC_INTERNAL_CALL __regcall using MkwStateFreeResult2 = uint64_t __attribute__((ext_vector_type(2))); diff --git a/runtime/include/memory_access.h b/runtime/include/memory_access.h index c1cca16..8b19e02 100644 --- a/runtime/include/memory_access.h +++ b/runtime/include/memory_access.h @@ -18,8 +18,15 @@ extern "C" { } namespace MemoryInline { +#if defined(_WIN32) #define MKW_MEMORY_FORCE_INLINE __forceinline #define MKW_MEMORY_NO_INLINE __declspec(noinline) +#else +// See runtime/include/isa/ppc_isa_config.h for why non-Windows Clang needs the GNU-attribute +// spellings instead of the MS-extension keywords. +#define MKW_MEMORY_FORCE_INLINE __attribute__((always_inline)) inline +#define MKW_MEMORY_NO_INLINE __attribute__((noinline)) +#endif #define MKW_MEMORY_COLD __attribute__((cold)) inline constexpr uint32_t kPageShift = 20; inline constexpr uint32_t kPageSize = 1u << kPageShift; diff --git a/runtime/src/host_cpu_baseline.cpp b/runtime/src/host_cpu_baseline.cpp index 7271f7e..9d8157e 100644 --- a/runtime/src/host_cpu_baseline.cpp +++ b/runtime/src/host_cpu_baseline.cpp @@ -10,7 +10,11 @@ #include #include +#if defined(_WIN32) #include +#else +#include +#endif namespace { @@ -137,7 +141,12 @@ bool CollectMissingBaselineFeatures(TextBuffer& missing) { // up yet, and fprintf(stderr, ...) faults there. Verified on this toolchain: // WriteFile on the raw standard-error handle and MessageBoxA both work, printf // does not. Anything added to this reporting path has to respect that. +// +// The POSIX path runs from an __attribute__((constructor)) instead, ahead of libc's own startup +// guarantees; ::write() on the raw fd is the same kind of allocation-free, libc-init-independent +// primitive as WriteFile is on Windows, so the same restriction is honored here. void WriteStdErrEarly(const char* text) { +#if defined(_WIN32) const HANDLE handle = ::GetStdHandle(STD_ERROR_HANDLE); if (handle == nullptr || handle == INVALID_HANDLE_VALUE) { return; @@ -148,6 +157,13 @@ void WriteStdErrEarly(const char* text) { } DWORD written = 0; ::WriteFile(handle, text, static_cast(length), &written, nullptr); +#else + size_t length = 0; + while (text[length] != '\0') { + ++length; + } + (void)::write(STDERR_FILENO, text, length); +#endif } [[noreturn]] void ReportUnsupportedCpu(const char* missing) { @@ -168,12 +184,18 @@ void WriteStdErrEarly(const char* text) { WriteStdErrEarly(message.data); WriteStdErrEarly("\n"); +#if defined(_WIN32) ::MessageBoxA(nullptr, message.data, "WiiCompiled - Unsupported Processor", MB_OK | MB_ICONERROR | MB_SETFOREGROUND | MB_TASKMODAL); // Leave through the OS rather than exit(): the C++ dynamic initializers // have not run yet, so there is no constructed program state to unwind and // the teardown path itself lives in AVX2 translation units. ::ExitProcess(1u); +#else + // Same reasoning as the Windows path above: no C++ dynamic initializer has run yet, so + // _exit() (skips atexit/global destructors, unlike exit()) is the correct way out. + ::_exit(1); +#endif } } // namespace