| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Target = std.Target; |
| 2 | 3 | |
| 3 | 4 | pub const CoreInfo = struct { |
| 4 | 5 | architecture: u8 = 0, |
| ... | ... | @@ -9,14 +10,14 @@ pub const CoreInfo = struct { |
| 9 | 10 | |
| 10 | 11 | pub const cpu_models = struct { |
| 11 | 12 | // Shorthands to simplify the tables below. |
| 12 | | const A32 = std.Target.arm.cpu; |
| 13 | | const A64 = std.Target.aarch64.cpu; |
| 13 | const A32 = Target.arm.cpu; |
| 14 | const A64 = Target.aarch64.cpu; |
| 14 | 15 | |
| 15 | 16 | const E = struct { |
| 16 | 17 | part: u16, |
| 17 | 18 | variant: ?u8 = null, // null if matches any variant |
| 18 | | m32: ?*const std.Target.Cpu.Model = null, |
| 19 | | m64: ?*const std.Target.Cpu.Model = null, |
| 19 | m32: ?*const Target.Cpu.Model = null, |
| 20 | m64: ?*const Target.Cpu.Model = null, |
| 20 | 21 | }; |
| 21 | 22 | |
| 22 | 23 | // implementer = 0x41 |
| ... | ... | @@ -59,7 +60,6 @@ pub const cpu_models = struct { |
| 59 | 60 | E{ .part = 0xd21, .m32 = &A32.cortex_m33, .m64 = null }, |
| 60 | 61 | E{ .part = 0xd41, .m32 = &A32.cortex_a78, .m64 = &A64.cortex_a78 }, |
| 61 | 62 | E{ .part = 0xd4b, .m32 = &A32.cortex_a78c, .m64 = &A64.cortex_a78c }, |
| 62 | | // This is a guess based on https://www.notebookcheck.net/Qualcomm-Snapdragon-8cx-Gen-3-Processor-Benchmarks-and-Specs.652916.0.html |
| 63 | 63 | E{ .part = 0xd4c, .m32 = &A32.cortex_x1c, .m64 = &A64.cortex_x1c }, |
| 64 | 64 | E{ .part = 0xd44, .m32 = &A32.cortex_x1, .m64 = &A64.cortex_x1 }, |
| 65 | 65 | E{ .part = 0xd02, .m64 = &A64.cortex_a34 }, |
| ... | ... | @@ -111,7 +111,7 @@ pub const cpu_models = struct { |
| 111 | 111 | E{ .part = 0xc01, .m64 = &A64.saphira }, |
| 112 | 112 | }; |
| 113 | 113 | |
| 114 | | pub fn isKnown(core: CoreInfo, is_64bit: bool) ?*const std.Target.Cpu.Model { |
| 114 | pub fn isKnown(core: CoreInfo, is_64bit: bool) ?*const Target.Cpu.Model { |
| 115 | 115 | const models = switch (core.implementer) { |
| 116 | 116 | 0x41 => &ARM, |
| 117 | 117 | 0x42 => &Broadcom, |
| ... | ... | @@ -132,3 +132,174 @@ pub const cpu_models = struct { |
| 132 | 132 | return null; |
| 133 | 133 | } |
| 134 | 134 | }; |
| 135 | |
| 136 | pub const aarch64 = struct { |
| 137 | fn setFeature(cpu: *Target.Cpu, feature: Target.aarch64.Feature, enabled: bool) void { |
| 138 | const idx = @as(Target.Cpu.Feature.Set.Index, @enumToInt(feature)); |
| 139 | |
| 140 | if (enabled) cpu.features.addFeature(idx) else cpu.features.removeFeature(idx); |
| 141 | } |
| 142 | |
| 143 | inline fn bitField(input: u64, offset: u6) u4 { |
| 144 | return @truncate(u4, input >> offset); |
| 145 | } |
| 146 | |
| 147 | /// Input array should consist of readouts from 12 system registers such that: |
| 148 | /// 0 -> MIDR_EL1 |
| 149 | /// 1 -> ID_AA64PFR0_EL1 |
| 150 | /// 2 -> ID_AA64PFR1_EL1 |
| 151 | /// 3 -> ID_AA64DFR0_EL1 |
| 152 | /// 4 -> ID_AA64DFR1_EL1 |
| 153 | /// 5 -> ID_AA64AFR0_EL1 |
| 154 | /// 6 -> ID_AA64AFR1_EL1 |
| 155 | /// 7 -> ID_AA64ISAR0_EL1 |
| 156 | /// 8 -> ID_AA64ISAR1_EL1 |
| 157 | /// 9 -> ID_AA64MMFR0_EL1 |
| 158 | /// 10 -> ID_AA64MMFR1_EL1 |
| 159 | /// 11 -> ID_AA64MMFR2_EL1 |
| 160 | pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, registers: [12]u64) ?Target.Cpu { |
| 161 | const info = detectNativeCoreInfo(registers[0]); |
| 162 | const model = cpu_models.isKnown(info, true) orelse return null; |
| 163 | |
| 164 | var cpu = Target.Cpu{ |
| 165 | .arch = arch, |
| 166 | .model = model, |
| 167 | .features = Target.Cpu.Feature.Set.empty, |
| 168 | }; |
| 169 | |
| 170 | detectNativeCpuFeatures(&cpu, registers[1..12]); |
| 171 | addInstructionFusions(&cpu, info); |
| 172 | |
| 173 | return cpu; |
| 174 | } |
| 175 | |
| 176 | /// Takes readout of MIDR_EL1 register as input. |
| 177 | fn detectNativeCoreInfo(midr: u64) CoreInfo { |
| 178 | var info = CoreInfo{ |
| 179 | .implementer = @truncate(u8, midr >> 24), |
| 180 | .part = @truncate(u12, midr >> 4), |
| 181 | }; |
| 182 | |
| 183 | blk: { |
| 184 | if (info.implementer == 0x41) { |
| 185 | // ARM Ltd. |
| 186 | const special_bits = @truncate(u4, info.part >> 8); |
| 187 | if (special_bits == 0x0 or special_bits == 0x7) { |
| 188 | // TODO Variant and arch encoded differently. |
| 189 | break :blk; |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | info.variant |= @intCast(u8, @truncate(u4, midr >> 20)) << 4; |
| 194 | info.variant |= @truncate(u4, midr); |
| 195 | info.architecture = @truncate(u4, midr >> 16); |
| 196 | } |
| 197 | |
| 198 | return info; |
| 199 | } |
| 200 | |
| 201 | /// Input array should consist of readouts from 11 system registers such that: |
| 202 | /// 0 -> ID_AA64PFR0_EL1 |
| 203 | /// 1 -> ID_AA64PFR1_EL1 |
| 204 | /// 2 -> ID_AA64DFR0_EL1 |
| 205 | /// 3 -> ID_AA64DFR1_EL1 |
| 206 | /// 4 -> ID_AA64AFR0_EL1 |
| 207 | /// 5 -> ID_AA64AFR1_EL1 |
| 208 | /// 6 -> ID_AA64ISAR0_EL1 |
| 209 | /// 7 -> ID_AA64ISAR1_EL1 |
| 210 | /// 8 -> ID_AA64MMFR0_EL1 |
| 211 | /// 9 -> ID_AA64MMFR1_EL1 |
| 212 | /// 10 -> ID_AA64MMFR2_EL1 |
| 213 | fn detectNativeCpuFeatures(cpu: *Target.Cpu, registers: *const [11]u64) void { |
| 214 | // ID_AA64PFR0_EL1 |
| 215 | setFeature(cpu, .dit, bitField(registers[0], 48) >= 1); |
| 216 | setFeature(cpu, .am, bitField(registers[0], 44) >= 1); |
| 217 | setFeature(cpu, .amvs, bitField(registers[0], 44) >= 2); |
| 218 | setFeature(cpu, .mpam, bitField(registers[0], 40) >= 1); // MPAM v1.0 |
| 219 | setFeature(cpu, .sel2, bitField(registers[0], 36) >= 1); |
| 220 | setFeature(cpu, .sve, bitField(registers[0], 32) >= 1); |
| 221 | setFeature(cpu, .el3, bitField(registers[0], 12) >= 1); |
| 222 | setFeature(cpu, .ras, bitField(registers[0], 28) >= 1); |
| 223 | |
| 224 | if (bitField(registers[0], 20) < 0xF) blk: { |
| 225 | if (bitField(registers[0], 16) != bitField(registers[0], 20)) break :blk; // This should never occur |
| 226 | |
| 227 | setFeature(cpu, .neon, true); |
| 228 | setFeature(cpu, .fp_armv8, true); |
| 229 | setFeature(cpu, .fullfp16, bitField(registers[0], 20) > 0); |
| 230 | } |
| 231 | |
| 232 | // ID_AA64PFR1_EL1 |
| 233 | setFeature(cpu, .mpam, bitField(registers[1], 16) > 0 and bitField(registers[0], 40) == 0); // MPAM v0.1 |
| 234 | setFeature(cpu, .mte, bitField(registers[1], 8) >= 1); |
| 235 | setFeature(cpu, .ssbs, bitField(registers[1], 4) >= 1); |
| 236 | setFeature(cpu, .bti, bitField(registers[1], 0) >= 1); |
| 237 | |
| 238 | // ID_AA64DFR0_EL1 |
| 239 | setFeature(cpu, .tracev8_4, bitField(registers[2], 40) >= 1); |
| 240 | setFeature(cpu, .spe, bitField(registers[2], 32) >= 1); |
| 241 | setFeature(cpu, .perfmon, bitField(registers[2], 8) >= 1 and bitField(registers[2], 8) < 0xF); |
| 242 | |
| 243 | // ID_AA64DFR1_EL1 reserved |
| 244 | // ID_AA64AFR0_EL1 reserved / implementation defined |
| 245 | // ID_AA64AFR1_EL1 reserved |
| 246 | |
| 247 | // ID_AA64ISAR0_EL1 |
| 248 | setFeature(cpu, .rand, bitField(registers[6], 60) >= 1); |
| 249 | setFeature(cpu, .tlb_rmi, bitField(registers[6], 56) >= 1); |
| 250 | setFeature(cpu, .flagm, bitField(registers[6], 52) >= 1); |
| 251 | setFeature(cpu, .fp16fml, bitField(registers[6], 48) >= 1); |
| 252 | setFeature(cpu, .dotprod, bitField(registers[6], 44) >= 1); |
| 253 | setFeature(cpu, .sm4, bitField(registers[6], 40) >= 1 and bitField(registers[6], 36) >= 1); |
| 254 | setFeature(cpu, .sha3, bitField(registers[6], 32) >= 1 and bitField(registers[6], 12) >= 2); |
| 255 | setFeature(cpu, .rdm, bitField(registers[6], 28) >= 1); |
| 256 | setFeature(cpu, .lse, bitField(registers[6], 20) >= 1); |
| 257 | setFeature(cpu, .crc, bitField(registers[6], 16) >= 1); |
| 258 | setFeature(cpu, .sha2, bitField(registers[6], 12) >= 1 and bitField(registers[6], 8) >= 1); |
| 259 | setFeature(cpu, .aes, bitField(registers[6], 4) >= 1); |
| 260 | |
| 261 | // ID_AA64ISAR1_EL1 |
| 262 | setFeature(cpu, .i8mm, bitField(registers[7], 52) >= 1); |
| 263 | setFeature(cpu, .bf16, bitField(registers[7], 44) >= 1); |
| 264 | setFeature(cpu, .predres, bitField(registers[7], 40) >= 1); |
| 265 | setFeature(cpu, .sb, bitField(registers[7], 36) >= 1); |
| 266 | setFeature(cpu, .fptoint, bitField(registers[7], 32) >= 1); |
| 267 | setFeature(cpu, .rcpc, bitField(registers[7], 20) >= 1); |
| 268 | setFeature(cpu, .rcpc_immo, bitField(registers[7], 20) >= 2); |
| 269 | setFeature(cpu, .complxnum, bitField(registers[7], 16) >= 1); |
| 270 | setFeature(cpu, .jsconv, bitField(registers[7], 12) >= 1); |
| 271 | setFeature(cpu, .pauth, bitField(registers[7], 8) >= 1 or bitField(registers[7], 4) >= 1); |
| 272 | setFeature(cpu, .ccpp, bitField(registers[7], 0) >= 1); |
| 273 | setFeature(cpu, .ccdp, bitField(registers[7], 0) >= 2); |
| 274 | |
| 275 | // ID_AA64MMFR0_EL1 |
| 276 | setFeature(cpu, .ecv, bitField(registers[8], 60) >= 1); |
| 277 | setFeature(cpu, .fgt, bitField(registers[8], 56) >= 1); |
| 278 | |
| 279 | // ID_AA64MMFR1_EL1 |
| 280 | setFeature(cpu, .pan, bitField(registers[9], 20) >= 1); |
| 281 | setFeature(cpu, .pan_rwv, bitField(registers[9], 20) >= 2); |
| 282 | setFeature(cpu, .lor, bitField(registers[9], 16) >= 1); |
| 283 | setFeature(cpu, .vh, bitField(registers[9], 8) >= 1); |
| 284 | setFeature(cpu, .contextidr_el2, bitField(registers[9], 8) >= 1); |
| 285 | |
| 286 | // ID_AA64MMFR2_EL1 |
| 287 | setFeature(cpu, .nv, bitField(registers[10], 24) >= 1); |
| 288 | setFeature(cpu, .ccidx, bitField(registers[10], 20) >= 1); |
| 289 | setFeature(cpu, .uaops, bitField(registers[10], 4) >= 1); |
| 290 | } |
| 291 | |
| 292 | fn addInstructionFusions(cpu: *Target.Cpu, info: CoreInfo) void { |
| 293 | switch (info.implementer) { |
| 294 | 0x41 => switch (info.part) { |
| 295 | 0xd4b, 0xd4c => { |
| 296 | // According to A78C/X1C Core Software Optimization Guide, CPU fuses certain instructions. |
| 297 | setFeature(cpu, .cmp_bcc_fusion, true); |
| 298 | setFeature(cpu, .fuse_aes, true); |
| 299 | }, |
| 300 | else => {}, |
| 301 | }, |
| 302 | else => {}, |
| 303 | } |
| 304 | } |
| 305 | }; |