From c8e42173ff91b2e39a0f5e0416d8bed68b27feae Mon Sep 17 00:00:00 2001
From: Andrew Udvare <audvare@gmail.com>
Date: Wed, 11 Feb 2026 10:21:15 -0500
Subject: [PATCH 01/23] Fix build with GCC (Clang unchanged)

Add compiler guards and small changes so xenia-canary builds with GCC
while keeping Clang behavior unchanged.

- memory.cc: Guard movdir64b usage so the Clang path is unchanged;
  GCC uses an alternate implementation (or skips) where the intrinsic
  differs.
- memory.h: Use const_cast for _mm_stream_load_si128 source pointers;
  GCC's intrinsic takes a non-const pointer although the load does
  not modify the source.
- kernel_state.h: Give the anonymous struct inside KernelVersion a
  name (e.g. parts) so both Clang and GCC accept it; GCC does not
  support the anonymous struct extension used there.
- command_processor.h: Do not force-inline the virtual
  WriteRegisterRangeFromRing on GCC (omit XE_FORCEINLINE when
  __GNUC__). The definition lives in a .cc file, so inlining fails
  with "function body not available" when callers in headers try
  to inline it.
- xbdm_misc.cc: Use KernelVersion.parts only where needed for the
  new named struct.

Builds verified with both Clang and GCC.

Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit ea1a25fab7b05b16e6c75acd29b3a0472991c1f7)
---
 src/xenia/base/memory.cc           |  2 +-
 src/xenia/base/memory.h            | 13 +++++++++----
 src/xenia/gpu/command_processor.h  |  6 ++++++
 src/xenia/kernel/kernel_state.h    | 23 ++++++++++++-----------
 src/xenia/kernel/xbdm/xbdm_misc.cc |  2 +-
 5 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/src/xenia/base/memory.cc b/src/xenia/base/memory.cc
index 7f135c0b8..e0902c69a 100644
--- a/src/xenia/base/memory.cc
+++ b/src/xenia/base/memory.cc
@@ -35,7 +35,7 @@ using xe::swcache::CacheLine;
 #if XE_ARCH_AMD64
 static constexpr unsigned NUM_CACHELINES_IN_PAGE = 4096 / sizeof(CacheLine);
 
-#if defined(__clang__)
+#if defined(__clang__) || defined(__GNUC__)
 XE_FORCEINLINE
 static void mvdir64b(void* to, const void* from) {
   __asm__("movdir64b %1, %0" : : "r"(to), "m"(*(char*)from) : "memory");
diff --git a/src/xenia/base/memory.h b/src/xenia/base/memory.h
index 8ef40bbff..f14111352 100644
--- a/src/xenia/base/memory.h
+++ b/src/xenia/base/memory.h
@@ -724,10 +724,15 @@ static void ReadLineNT(CacheLine* XE_RESTRICT destination,
                        const CacheLine* XE_RESTRICT source) {
   assert_true((reinterpret_cast<uintptr_t>(source) & 63ULL) == 0);
 
-  __m128i first = _mm_stream_load_si128(&source->xmms[0]);
-  __m128i second = _mm_stream_load_si128(&source->xmms[1]);
-  __m128i third = _mm_stream_load_si128(&source->xmms[2]);
-  __m128i fourth = _mm_stream_load_si128(&source->xmms[3]);
+  // GCC's intrinsic takes non-const pointer; stream load does not modify source.
+  __m128i first =
+      _mm_stream_load_si128(const_cast<__m128i*>(&source->xmms[0]));
+  __m128i second =
+      _mm_stream_load_si128(const_cast<__m128i*>(&source->xmms[1]));
+  __m128i third =
+      _mm_stream_load_si128(const_cast<__m128i*>(&source->xmms[2]));
+  __m128i fourth =
+      _mm_stream_load_si128(const_cast<__m128i*>(&source->xmms[3]));
 
   destination->xmms[0] = first;
   destination->xmms[1] = second;
diff --git a/src/xenia/gpu/command_processor.h b/src/xenia/gpu/command_processor.h
index 9fce69114..42afe1ea6 100644
--- a/src/xenia/gpu/command_processor.h
+++ b/src/xenia/gpu/command_processor.h
@@ -235,7 +235,13 @@ class CommandProcessor {
   virtual void WriteRegistersFromMem(uint32_t start_index, uint32_t* base,
                                      uint32_t num_registers);
 
+  // GCC fails with "inlining failed: function body not available" when the
+  // virtual definition is in a .cc and callers (e.g. pm4 template) are in
+  // headers. Omit XE_FORCEINLINE for GCC only so the call uses the out-of-line
+  // definition; Clang and others keep the hint.
+#if !defined(__GNUC__) || defined(__clang__)
   XE_FORCEINLINE
+#endif
   virtual void WriteRegisterRangeFromRing(xe::RingBuffer* ring, uint32_t base,
                                           uint32_t num_registers);
 
diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h
index 32da7ddbe..f7ff01b36 100644
--- a/src/xenia/kernel/kernel_state.h
+++ b/src/xenia/kernel/kernel_state.h
@@ -142,23 +142,24 @@ struct DPCImpersonationScope {
   uint8_t previous_irql_;
 };
 
+struct KernelVersionParts {
+  xe::be<uint16_t> major;
+  xe::be<uint16_t> minor;
+  xe::be<uint16_t> build;
+  xe::be<uint16_t> qfe;
+};
+
 struct KernelVersion {
   union {
     xe::be<uint64_t> value;
-
-    struct {
-      xe::be<uint16_t> major;
-      xe::be<uint16_t> minor;
-      xe::be<uint16_t> build;
-      xe::be<uint16_t> qfe;
-    };
+    KernelVersionParts parts;
   };
 
   KernelVersion(uint16_t build_ver = kBaseKernelBuildVersion) {
-    major = 2;
-    minor = 0;
-    build = std::max(kBaseKernelBuildVersion, build_ver);
-    qfe = 0;
+    parts.major = 2;
+    parts.minor = 0;
+    parts.build = std::max(kBaseKernelBuildVersion, build_ver);
+    parts.qfe = 0;
   }
 };
 
diff --git a/src/xenia/kernel/xbdm/xbdm_misc.cc b/src/xenia/kernel/xbdm/xbdm_misc.cc
index e371c0009..841a91f63 100644
--- a/src/xenia/kernel/xbdm/xbdm_misc.cc
+++ b/src/xenia/kernel/xbdm/xbdm_misc.cc
@@ -220,7 +220,7 @@ dword_result_t DmGetSystemInfo_entry(pointer_t<XBDM_SYSTEM_INFO> info) {
   info->base_kernel_version.qfe = info->kernel_version.qfe = 0;
 
   info->base_kernel_version.build = kBaseKernelBuildVersion;
-  info->kernel_version.build = kernel_state()->GetKernelVersion()->build;
+  info->kernel_version.build = kernel_state()->GetKernelVersion()->parts.build;
 
   return XBDM_SUCCESSFUL;
 }
-- 
2.54.0

