| ... | @@ -6,27 +6,49 @@ | ... | @@ -6,27 +6,49 @@ |
| 6 | const std = @import("std"); | 6 | const std = @import("std"); |
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | | 8 | |
| 9 | pub fn suggestVectorSizeForCpu(comptime T: type, cpu: std.Target.Cpu) ?usize { | 9 | pub fn suggestVectorSizeForCpu(comptime T: type, comptime cpu: std.Target.Cpu) ?usize { |
| 10 | switch (cpu.arch) { | 10 | // This is guesswork, if you have better suggestions can add it or edit the current here |
| 11 | .x86_64 => { | 11 | // This can run in comptime only, but stage 1 fails at it, stage 2 can understand it |
| 12 | // Note: This is mostly just guesswork. It'd be great if someone more qualified were to take a | 12 | const element_bit_size = @maximum(8, std.math.ceilPowerOfTwo(T, @bitSizeOf(T)) catch unreachable); |
| 13 | // proper look at this. | 13 | const vector_bit_size: u16 = blk: { |
| 14 | | 14 | if (cpu.arch.isX86()) { |
| 15 | if (T == bool and std.Target.x86.featureSetHas(.prefer_mask_registers)) return 64; | 15 | if (T == bool and std.Target.x86.featureSetHas(.prefer_mask_registers)) return 64; |
| | 16 | if (std.Target.x86.featureSetHas(cpu.features, .avx512f) and !std.Target.x86.featureSetHasAny(cpu.features, .{ .prefer_256_bit, .prefer_128_bit })) break :blk 512; |
| | 17 | if (std.Target.x86.featureSetHasAny(cpu.features, .{ .prefer_256_bit, .avx2 }) and !std.Target.x86.featureSetHas(cpu.features, .prefer_128_bit)) break :blk 256; |
| | 18 | if (std.Target.x86.featureSetHas(cpu.features, .sse)) break :blk 128; |
| | 19 | if (std.Target.x86.featureSetHasAny(cpu.features, .{ .mmx, .@"3dnow" })) break :blk 64; |
| | 20 | } else if (cpu.arch.isARM()) { |
| | 21 | if (std.Target.arm.featureSetHas(cpu.features, .neon)) break :blk 128; |
| | 22 | } else if (cpu.arch.isAARCH64()) { |
| | 23 | // SVE allows up to 2048 bits in the specification, as of 2022 the most powerful machine has implemented 512-bit |
| | 24 | // I think is safer to just be on 128 until is more common |
| | 25 | // TODO: Check on this return when bigger values are more common |
| | 26 | if (std.Target.aarch64.featureSetHas(cpu.features, .sve)) break :blk 128; |
| | 27 | if (std.Target.aarch64.featureSetHas(cpu.features, .neon)) break :blk 128; |
| | 28 | } else if (cpu.arch.isPPC() or cpu.arch.isPPC64()) { |
| | 29 | if (std.Target.powerpc.featureSetHas(cpu.features, .altivec)) break :blk 128; |
| | 30 | } else if (cpu.arch.isMIPS()) { |
| | 31 | if (std.Target.mips.featureSetHas(cpu.features, .msa)) break :blk 128; |
| | 32 | // TODO: Test MIPS capability to handle bigger vectors |
| | 33 | // In theory MDMX and by extension mips3d have 32 registers of 64 bits which can use in parallel |
| | 34 | // for multiple processing, but I don't know what's optimal here, if using |
| | 35 | // the 2048 bits or using just 64 per vector or something in between |
| | 36 | if (std.Target.mips.featureSetHas(cpu.features, std.Target.mips.Feature.mips3d)) break :blk 64; |
| | 37 | } else if (cpu.arch.isRISCV()) { |
| | 38 | // in risc-v the Vector Extension allows configurable vector sizes, but a standard size of 128 is a safe estimate |
| | 39 | if (std.Target.riscv.featureSetHas(cpu.features, .v)) break :blk 128; |
| | 40 | } else if (cpu.arch.isSPARC()) { |
| | 41 | // TODO: Test Sparc capability to handle bigger vectors |
| | 42 | // In theory Sparc have 32 registers of 64 bits which can use in parallel |
| | 43 | // for multiple processing, but I don't know what's optimal here, if using |
| | 44 | // the 2048 bits or using just 64 per vector or something in between |
| | 45 | if (std.Target.sparc.featureSetHasAny(cpu.features, .{ .vis, .vis2, .vis3 })) break :blk 64; |
| | 46 | } |
| | 47 | return null; |
| | 48 | }; |
| | 49 | if (vector_bit_size <= element_bit_size) return null; |
| 16 | | 50 | |
| 17 | const vector_bit_size = blk: { | 51 | return @divExact(vector_bit_size, element_bit_size); |
| 18 | if (std.Target.x86.featureSetHas(.avx512f)) break :blk 512; | | |
| 19 | if (std.Target.x86.featureSetHas(.prefer_256_bit)) break :blk 256; | | |
| 20 | if (std.Target.x86.featureSetHas(.prefer_128_bit)) break :blk 128; | | |
| 21 | return null; | | |
| 22 | }; | | |
| 23 | const element_bit_size = std.math.max(8, std.math.ceilPowerOfTwo(T, @bitSizeOf(T))); | | |
| 24 | return @divExact(vector_bit_size, element_bit_size); | | |
| 25 | }, | | |
| 26 | else => { | | |
| 27 | return null; | | |
| 28 | }, | | |
| 29 | } | | |
| 30 | } | 52 | } |
| 31 | | 53 | |
| 32 | /// Suggests a target-dependant vector size for a given type, or null if scalars are recommended. | 54 | /// Suggests a target-dependant vector size for a given type, or null if scalars are recommended. |