Commit 6208e74145

Alex Rønne Petersen <alex@alexrp.com>
2025-09-17 15:20:56
std.zig.system: implement native CPU detection for LoongArch
ref #4591
1 parent f1c2ae2
Changed files (3)
lib
lib/std/zig/system/loongarch.zig
@@ -0,0 +1,48 @@
+const builtin = @import("builtin");
+const std = @import("std");
+
+pub fn detectNativeCpuAndFeatures(
+    arch: std.Target.Cpu.Arch,
+    os: std.Target.Os,
+    query: std.Target.Query,
+) ?std.Target.Cpu {
+    _ = os;
+    _ = query;
+
+    // Clearly this code could do better in the future by actually querying specific CPU features
+    // with the cpucfg instruction like on x86. But with the small number of well-known LoongArch
+    // models that exist at the moment, simply checking the PRID is plenty.
+    var cpu: std.Target.Cpu = .{
+        .arch = arch,
+        .model = switch (cpucfg(0) & 0xf000) {
+            else => return null,
+            0xc000 => &std.Target.loongarch.cpu.la464,
+            0xd000 => &std.Target.loongarch.cpu.la664,
+        },
+        .features = .empty,
+    };
+
+    cpu.features.addFeatureSet(cpu.model.features);
+    cpu.features.populateDependencies(cpu.arch.allFeaturesList());
+
+    return cpu;
+}
+
+/// This is a workaround for the C backend until zig has the ability to put
+/// C code in inline assembly.
+extern fn zig_loongarch_cpucfg(word: u32, result: *u32) callconv(.c) void;
+
+fn cpucfg(word: u32) u32 {
+    var result: u32 = undefined;
+
+    if (builtin.zig_backend == .stage2_c) {
+        zig_loongarch_cpucfg(word, &result);
+    } else {
+        asm ("cpucfg %[result], %[word]"
+            : [result] "=r" (result),
+            : [word] "r" (word),
+        );
+    }
+
+    return result;
+}
lib/std/zig/system.zig
@@ -468,9 +468,8 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T
     // although it is a runtime value, is guaranteed to be one of the architectures in the set
     // of the respective switch prong.
     switch (builtin.cpu.arch) {
-        .x86_64, .x86 => {
-            return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, query);
-        },
+        .loongarch32, .loongarch64 => return @import("system/loongarch.zig").detectNativeCpuAndFeatures(cpu_arch, os, query),
+        .x86_64, .x86 => return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, query),
         else => {},
     }
 
lib/zig.h
@@ -4195,7 +4195,17 @@ static inline void* zig_x86_64_windows_teb(void) {
 
 #endif
 
-#if defined(zig_x86)
+#if defined(zig_loongarch)
+
+static inline void zig_loongarch_cpucfg(uint32_t word, uint32_t* result) {
+#if defined(zig_gnuc_asm)
+    __asm__("cpucfg %[result], %[word]" : [result] "=r" (result) : [word] "r" (word));
+#else
+    *result = 0;
+#endif
+}
+
+#elif defined(zig_x86)
 
 static inline void zig_x86_cpuid(uint32_t leaf_id, uint32_t subid, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) {
 #if defined(zig_msvc)
@@ -4206,7 +4216,7 @@ static inline void zig_x86_cpuid(uint32_t leaf_id, uint32_t subid, uint32_t* eax
     *ecx = (uint32_t)cpu_info[2];
     *edx = (uint32_t)cpu_info[3];
 #elif defined(zig_gnuc_asm)
-    __asm__("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(leaf_id), "c"(subid));
+    __asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "a" (leaf_id), "c" (subid));
 #else
     *eax = 0;
     *ebx = 0;
@@ -4221,7 +4231,7 @@ static inline uint32_t zig_x86_get_xcr0(void) {
 #elif defined(zig_gnuc_asm)
     uint32_t eax;
     uint32_t edx;
-    __asm__("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0));
+    __asm__("xgetbv" : "=a" (eax), "=d" (edx) : "c" (0));
     return eax;
 #else
     *eax = 0;