From 9cf3a2b7f8dbb439b31c579000aa780e9fa6b2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 5 Sep 2026 18:37:00 +0200 Subject: [PATCH 1/4] std.zig.system.arm: don't give up just because we can't determine the model For example, OpenBSD doesn't give us MIDR_EL1. But that doesn't mean we should just give up when we can still detect individual features. --- lib/std/zig/system/arm.zig | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/std/zig/system/arm.zig b/lib/std/zig/system/arm.zig index c597ada2079b972ab5ccb847bca0807107a45edd..a02e6167da0d717647332d56944d377af7381c61 100644 --- a/lib/std/zig/system/arm.zig +++ b/lib/std/zig/system/arm.zig @@ -242,6 +242,10 @@ pub const aarch64 = struct { return @as(u4, @truncate(input >> offset)); } + inline fn signedBitField(input: u64, offset: u6) i4 { + return @as(i4, @bitCast(@as(u4, @truncate(input >> offset)))); + } + /// Input array should consist of readouts from 12 system registers such that: /// 0 -> MIDR_EL1 /// 1 -> ID_AA64PFR0_EL1 @@ -255,18 +259,22 @@ pub const aarch64 = struct { /// 9 -> ID_AA64MMFR0_EL1 /// 10 -> ID_AA64MMFR1_EL1 /// 11 -> ID_AA64MMFR2_EL1 - pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, registers: [12]u64) ?Target.Cpu { + pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, registers: [12]u64) Target.Cpu { const info = detectNativeCoreInfo(registers[0]); - const model = cpu_models.isKnown(info, true) orelse return null; + const model = cpu_models.isKnown(info, true) orelse Target.Cpu.Model.generic(arch); var cpu = Target.Cpu{ .arch = arch, .model = model, - .features = Target.Cpu.Feature.Set.empty, + .features = .empty, }; + cpu.features.addFeatureSet(model.features); + detectNativeCpuFeatures(&cpu, registers[1..12]); - addInstructionFusions(&cpu, info); + addInstructionFusions(&cpu); + + cpu.features.populateDependencies(cpu.arch.allFeaturesList()); return cpu; } @@ -318,14 +326,9 @@ pub const aarch64 = struct { setFeature(cpu, .sve, bitField(registers[0], 32) >= 1); setFeature(cpu, .el3, bitField(registers[0], 12) >= 1); setFeature(cpu, .ras, bitField(registers[0], 28) >= 1); - - if (bitField(registers[0], 20) < 0xF) blk: { - if (bitField(registers[0], 16) != bitField(registers[0], 20)) break :blk; // This should never occur - - setFeature(cpu, .neon, true); - setFeature(cpu, .fp_armv8, true); - setFeature(cpu, .fullfp16, bitField(registers[0], 20) > 0); - } + setFeature(cpu, .fp_armv8, signedBitField(registers[0], 16) >= 0); + setFeature(cpu, .neon, signedBitField(registers[0], 20) >= 0); + setFeature(cpu, .fullfp16, signedBitField(registers[0], 16) >= 1 and signedBitField(registers[0], 20) >= 1); // ID_AA64PFR1_EL1 setFeature(cpu, .mpam, bitField(registers[1], 16) > 0 and bitField(registers[0], 40) == 0); // MPAM v0.1 @@ -336,7 +339,7 @@ pub const aarch64 = struct { // ID_AA64DFR0_EL1 setFeature(cpu, .tracev8_4, bitField(registers[2], 40) >= 1); setFeature(cpu, .spe, bitField(registers[2], 32) >= 1); - setFeature(cpu, .perfmon, bitField(registers[2], 8) >= 1 and bitField(registers[2], 8) < 0xF); + setFeature(cpu, .perfmon, signedBitField(registers[2], 8) >= 1); // ID_AA64DFR1_EL1 reserved // ID_AA64AFR0_EL1 reserved / implementation defined @@ -387,17 +390,14 @@ pub const aarch64 = struct { setFeature(cpu, .uaops, bitField(registers[10], 4) >= 1); } - fn addInstructionFusions(cpu: *Target.Cpu, info: CoreInfo) void { - switch (info.implementer) { - 0x41 => switch (info.part) { - 0xd4b, 0xd4c => { - // According to A78C/X1C Core Software Optimization Guide, CPU fuses certain instructions. - setFeature(cpu, .cmp_bcc_fusion, true); - setFeature(cpu, .fuse_aes, true); - }, - else => {}, - }, - else => {}, + fn addInstructionFusions(cpu: *Target.Cpu) void { + const m = cpu.model; + const c = Target.aarch64.cpu; + + if (m == &c.cortex_a78c or m == &c.cortex_x1c) { + // According to A78C/X1C Core Software Optimization Guide, CPU fuses certain instructions. + setFeature(cpu, .cmp_bcc_fusion, true); + setFeature(cpu, .fuse_aes, true); } } }; -- 2.54.0 From 311c9699cb5e7fb81d9d7e74f1d7f40a8da0079b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 5 Sep 2026 18:39:38 +0200 Subject: [PATCH 2/4] std.zig.system.windows: remove some goofy pub consts --- lib/std/zig/system/windows.zig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/std/zig/system/windows.zig b/lib/std/zig/system/windows.zig index ef07ed9d0f71e658198e65ff507a784ed601c021..cd40be514b7e2411d0ccf52e8ecd85ca3657ca1a 100644 --- a/lib/std/zig/system/windows.zig +++ b/lib/std/zig/system/windows.zig @@ -3,11 +3,10 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const mem = std.mem; const Target = std.Target; - -pub const WindowsVersion = std.Target.Os.WindowsVersion; -pub const PF = std.os.windows.PF; -pub const REG = std.os.windows.REG; -pub const IsProcessorFeaturePresent = std.os.windows.IsProcessorFeaturePresent; +const WindowsVersion = std.Target.Os.WindowsVersion; +const PF = std.os.windows.PF; +const REG = std.os.windows.REG; +const IsProcessorFeaturePresent = std.os.windows.IsProcessorFeaturePresent; /// Returns the highest known WindowsVersion deduced from reported runtime information. /// Discards information about in-between versions we don't differentiate. -- 2.54.0 From e2216b0f105adb09e6ef2d33c6ba54002f0de627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 5 Sep 2026 18:39:51 +0200 Subject: [PATCH 3/4] std.zig.system.windows: don't collect feature registers for all cores We're only interested in one anyway! --- lib/std/zig/system/windows.zig | 79 +++++++++++++--------------------- 1 file changed, 31 insertions(+), 48 deletions(-) diff --git a/lib/std/zig/system/windows.zig b/lib/std/zig/system/windows.zig index cd40be514b7e2411d0ccf52e8ecd85ca3657ca1a..31f4b2cd4c2d960f073417db434f7c00d1b0b2d9 100644 --- a/lib/std/zig/system/windows.zig +++ b/lib/std/zig/system/windows.zig @@ -192,19 +192,15 @@ fn setFeature(comptime Feature: type, cpu: *Target.Cpu, feature: Feature, enable if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx); } -fn getCpuCount() usize { - return std.os.windows.peb().NumberOfProcessors; -} - /// If the fine-grained detection of CPU features via Win registry fails, /// we fallback to a generic CPU model but we override the feature set /// using `SharedUserData` contents. /// This is effectively what LLVM does for all ARM chips on Windows. fn genericCpuAndNativeFeatures(arch: Target.Cpu.Arch) Target.Cpu { - var cpu = Target.Cpu{ + var cpu: Target.Cpu = .{ .arch = arch, .model = Target.Cpu.Model.generic(arch), - .features = Target.Cpu.Feature.Set.empty, + .features = .empty, }; switch (arch) { @@ -262,52 +258,39 @@ pub fn detectNativeCpuAndFeatures() ?Target.Cpu { const current_arch = builtin.cpu.arch; const cpu: ?Target.Cpu = switch (current_arch) { .aarch64, .aarch64_be => blk: { - var cores: [128]Target.Cpu = undefined; - const core_count = getCpuCount(); + var registers: [12]u64 = undefined; - if (core_count > cores.len) break :blk null; + // CP 4000 -> MIDR_EL1 + // CP 4020 -> ID_AA64PFR0_EL1 + // CP 4021 -> ID_AA64PFR1_EL1 + // CP 4028 -> ID_AA64DFR0_EL1 + // CP 4029 -> ID_AA64DFR1_EL1 + // CP 402C -> ID_AA64AFR0_EL1 + // CP 402D -> ID_AA64AFR1_EL1 + // CP 4030 -> ID_AA64ISAR0_EL1 + // CP 4031 -> ID_AA64ISAR1_EL1 + // CP 4038 -> ID_AA64MMFR0_EL1 + // CP 4039 -> ID_AA64MMFR1_EL1 + // CP 403A -> ID_AA64MMFR2_EL1 + getCpuInfoFromRegistry(0, .{ + .{ .key = "CP 4000", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[0])) }, + .{ .key = "CP 4020", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[1])) }, + .{ .key = "CP 4021", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[2])) }, + .{ .key = "CP 4028", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[3])) }, + .{ .key = "CP 4029", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[4])) }, + .{ .key = "CP 402C", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[5])) }, + .{ .key = "CP 402D", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[6])) }, + .{ .key = "CP 4030", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[7])) }, + .{ .key = "CP 4031", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[8])) }, + .{ .key = "CP 4038", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[9])) }, + .{ .key = "CP 4039", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[10])) }, + .{ .key = "CP 403A", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[11])) }, + }) catch break :blk null; - var i: usize = 0; - while (i < core_count) : (i += 1) { - // Backing datastore - var registers: [12]u64 = undefined; - - // Registry key to system ID register mapping - // CP 4000 -> MIDR_EL1 - // CP 4020 -> ID_AA64PFR0_EL1 - // CP 4021 -> ID_AA64PFR1_EL1 - // CP 4028 -> ID_AA64DFR0_EL1 - // CP 4029 -> ID_AA64DFR1_EL1 - // CP 402C -> ID_AA64AFR0_EL1 - // CP 402D -> ID_AA64AFR1_EL1 - // CP 4030 -> ID_AA64ISAR0_EL1 - // CP 4031 -> ID_AA64ISAR1_EL1 - // CP 4038 -> ID_AA64MMFR0_EL1 - // CP 4039 -> ID_AA64MMFR1_EL1 - // CP 403A -> ID_AA64MMFR2_EL1 - getCpuInfoFromRegistry(i, .{ - .{ .key = "CP 4000", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[0])) }, - .{ .key = "CP 4020", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[1])) }, - .{ .key = "CP 4021", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[2])) }, - .{ .key = "CP 4028", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[3])) }, - .{ .key = "CP 4029", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[4])) }, - .{ .key = "CP 402C", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[5])) }, - .{ .key = "CP 402D", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[6])) }, - .{ .key = "CP 4030", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[7])) }, - .{ .key = "CP 4031", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[8])) }, - .{ .key = "CP 4038", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[9])) }, - .{ .key = "CP 4039", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[10])) }, - .{ .key = "CP 403A", .value_type = REG.ValueType.QWORD, .value_buf = @as(*[8]u8, @ptrCast(®isters[11])) }, - }) catch break :blk null; - - cores[i] = @import("arm.zig").aarch64.detectNativeCpuAndFeatures(current_arch, registers) orelse - break :blk null; - } - - // Pick the first core, usually LITTLE in big.LITTLE architecture. - break :blk cores[0]; + break :blk @import("arm.zig").aarch64.detectNativeCpuAndFeatures(current_arch, registers); }, else => null, }; + return cpu orelse genericCpuAndNativeFeatures(current_arch); } -- 2.54.0 From 153371f5313c0ffbffe5a430a4e410f3d8608c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sat, 5 Sep 2026 18:58:15 +0200 Subject: [PATCH 4/4] std.zig.system.windows: fix handling of dependencies in aarch64 detection --- lib/std/zig/system/windows.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/std/zig/system/windows.zig b/lib/std/zig/system/windows.zig index 31f4b2cd4c2d960f073417db434f7c00d1b0b2d9..0df7d598572cc91192a7a2b5b11684cfd77dec7c 100644 --- a/lib/std/zig/system/windows.zig +++ b/lib/std/zig/system/windows.zig @@ -203,6 +203,8 @@ fn genericCpuAndNativeFeatures(arch: Target.Cpu.Arch) Target.Cpu { .features = .empty, }; + cpu.features.addFeatureSet(cpu.model.features); + switch (arch) { .aarch64, .aarch64_be => { const Feature = Target.aarch64.Feature; @@ -251,6 +253,8 @@ fn genericCpuAndNativeFeatures(arch: Target.Cpu.Arch) Target.Cpu { else => {}, } + cpu.features.populateDependencies(cpu.arch.allFeaturesList()); + return cpu; } -- 2.54.0