diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2020-02-21 14:19:20 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-21 14:19:20 -0500 |
| commit | 10e0b071354d8a1b4ab70041dfdd06a008bd6d3d (patch) | |
| tree | a705cb0f0e25fceb5263e10fc027011e0ee26f76 /lib/std/target.zig | |
| parent | e381a42de9c0f0c5439a926b0ac99026a0373f49 (diff) | |
| parent | 71573584cdfb1ddb176681fcb7d1544cac7a72ca (diff) | |
| download | zig-10e0b071354d8a1b4ab70041dfdd06a008bd6d3d.tar.gz zig-10e0b071354d8a1b4ab70041dfdd06a008bd6d3d.zip | |
Merge pull request #4509 from ziglang/sub-architecture-annihilation
sub-architecture annihilation
Diffstat (limited to 'lib/std/target.zig')
| -rw-r--r-- | lib/std/target.zig | 1222 |
1 files changed, 579 insertions, 643 deletions
diff --git a/lib/std/target.zig b/lib/std/target.zig index bc78b2dce5..83d3dafbc2 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -47,6 +47,16 @@ pub const Target = union(enum) { emscripten, uefi, other, + + pub fn parse(text: []const u8) !Os { + const info = @typeInfo(Os); + inline for (info.Enum.fields) |field| { + if (mem.eql(u8, text, field.name)) { + return @field(Os, field.name); + } + } + return error.UnknownOperatingSystem; + } }; pub const aarch64 = @import("target/aarch64.zig"); @@ -65,457 +75,6 @@ pub const Target = union(enum) { pub const wasm = @import("target/wasm.zig"); pub const x86 = @import("target/x86.zig"); - pub const Arch = union(enum) { - arm: Arm32, - armeb: Arm32, - aarch64: Arm64, - aarch64_be: Arm64, - aarch64_32: Arm64, - arc, - avr, - bpfel, - bpfeb, - hexagon, - mips, - mipsel, - mips64, - mips64el, - msp430, - powerpc, - powerpc64, - powerpc64le, - r600, - amdgcn, - riscv32, - riscv64, - sparc, - sparcv9, - sparcel, - s390x, - tce, - tcele, - thumb: Arm32, - thumbeb: Arm32, - i386, - x86_64, - xcore, - nvptx, - nvptx64, - le32, - le64, - amdil, - amdil64, - hsail, - hsail64, - spir, - spir64, - kalimba: Kalimba, - shave, - lanai, - wasm32, - wasm64, - renderscript32, - renderscript64, - - pub const Arm32 = enum { - v8_5a, - v8_4a, - v8_3a, - v8_2a, - v8_1a, - v8a, - v8r, - v8m_baseline, - v8m_mainline, - v8_1m_mainline, - v7a, - v7em, - v7m, - v7s, - v7k, - v7ve, - v6, - v6m, - v6k, - v6t2, - v5, - v5te, - v4t, - - pub fn version(version: Arm32) comptime_int { - return switch (version) { - .v8_5a, .v8_4a, .v8_3a, .v8_2a, .v8_1a, .v8a, .v8r, .v8m_baseline, .v8m_mainline, .v8_1m_mainline => 8, - .v7a, .v7em, .v7m, .v7s, .v7k, .v7ve => 7, - .v6, .v6m, .v6k, .v6t2 => 6, - .v5, .v5te => 5, - .v4t => 4, - }; - } - }; - pub const Arm64 = enum { - v8_5a, - v8_4a, - v8_3a, - v8_2a, - v8_1a, - v8a, - }; - pub const Kalimba = enum { - v5, - v4, - v3, - }; - pub const Mips = enum { - r6, - }; - - pub fn subArchName(arch: Arch) ?[]const u8 { - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => |arm32| @tagName(arm32), - .aarch64, .aarch64_be, .aarch64_32 => |arm64| @tagName(arm64), - .kalimba => |kalimba| @tagName(kalimba), - else => return null, - }; - } - - pub fn subArchFeature(arch: Arch) ?Cpu.Feature.Set.Index { - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => |arm32| switch (arm32) { - .v8_5a => @enumToInt(arm.Feature.armv8_5_a), - .v8_4a => @enumToInt(arm.Feature.armv8_4_a), - .v8_3a => @enumToInt(arm.Feature.armv8_3_a), - .v8_2a => @enumToInt(arm.Feature.armv8_2_a), - .v8_1a => @enumToInt(arm.Feature.armv8_1_a), - .v8a => @enumToInt(arm.Feature.armv8_a), - .v8r => @enumToInt(arm.Feature.armv8_r), - .v8m_baseline => @enumToInt(arm.Feature.armv8_m_base), - .v8m_mainline => @enumToInt(arm.Feature.armv8_m_main), - .v8_1m_mainline => @enumToInt(arm.Feature.armv8_1_m_main), - .v7a => @enumToInt(arm.Feature.armv7_a), - .v7em => @enumToInt(arm.Feature.armv7e_m), - .v7m => @enumToInt(arm.Feature.armv7_m), - .v7s => @enumToInt(arm.Feature.armv7s), - .v7k => @enumToInt(arm.Feature.armv7k), - .v7ve => @enumToInt(arm.Feature.armv7ve), - .v6 => @enumToInt(arm.Feature.armv6), - .v6m => @enumToInt(arm.Feature.armv6_m), - .v6k => @enumToInt(arm.Feature.armv6k), - .v6t2 => @enumToInt(arm.Feature.armv6t2), - .v5 => @enumToInt(arm.Feature.armv5t), - .v5te => @enumToInt(arm.Feature.armv5te), - .v4t => @enumToInt(arm.Feature.armv4t), - }, - .aarch64, .aarch64_be, .aarch64_32 => |arm64| switch (arm64) { - .v8_5a => @enumToInt(aarch64.Feature.v8_5a), - .v8_4a => @enumToInt(aarch64.Feature.v8_4a), - .v8_3a => @enumToInt(aarch64.Feature.v8_3a), - .v8_2a => @enumToInt(aarch64.Feature.v8_2a), - .v8_1a => @enumToInt(aarch64.Feature.v8_1a), - .v8a => @enumToInt(aarch64.Feature.v8a), - }, - else => return null, - }; - } - - pub fn isARM(arch: Arch) bool { - return switch (arch) { - .arm, .armeb => true, - else => false, - }; - } - - pub fn isThumb(arch: Arch) bool { - return switch (arch) { - .thumb, .thumbeb => true, - else => false, - }; - } - - pub fn isWasm(arch: Arch) bool { - return switch (arch) { - .wasm32, .wasm64 => true, - else => false, - }; - } - - pub fn isRISCV(arch: Arch) bool { - return switch (arch) { - .riscv32, .riscv64 => true, - else => false, - }; - } - - pub fn isMIPS(arch: Arch) bool { - return switch (arch) { - .mips, .mipsel, .mips64, .mips64el => true, - else => false, - }; - } - - pub fn parseCpu(arch: Arch, cpu_name: []const u8) !*const Cpu { - for (arch.allCpus()) |cpu| { - if (mem.eql(u8, cpu_name, cpu.name)) { - return cpu; - } - } - return error.UnknownCpu; - } - - /// Comma-separated list of features, with + or - in front of each feature. This - /// form represents a deviation from baseline CPU, which is provided as a parameter. - /// Extra commas are ignored. - pub fn parseCpuFeatureSet(arch: Arch, cpu: *const Cpu, features_text: []const u8) !Cpu.Feature.Set { - const all_features = arch.allFeaturesList(); - var set = cpu.features; - var it = mem.tokenize(features_text, ","); - while (it.next()) |item_text| { - var feature_name: []const u8 = undefined; - var op: enum { - add, - sub, - } = undefined; - if (mem.startsWith(u8, item_text, "+")) { - op = .add; - feature_name = item_text[1..]; - } else if (mem.startsWith(u8, item_text, "-")) { - op = .sub; - feature_name = item_text[1..]; - } else { - return error.InvalidCpuFeatures; - } - for (all_features) |feature, index_usize| { - const index = @intCast(Cpu.Feature.Set.Index, index_usize); - if (mem.eql(u8, feature_name, feature.name)) { - switch (op) { - .add => set.addFeature(index), - .sub => set.removeFeature(index), - } - break; - } - } else { - return error.UnknownCpuFeature; - } - } - return set; - } - - pub fn toElfMachine(arch: Arch) std.elf.EM { - return switch (arch) { - .avr => ._AVR, - .msp430 => ._MSP430, - .arc => ._ARC, - .arm => ._ARM, - .armeb => ._ARM, - .hexagon => ._HEXAGON, - .le32 => ._NONE, - .mips => ._MIPS, - .mipsel => ._MIPS_RS3_LE, - .powerpc => ._PPC, - .r600 => ._NONE, - .riscv32 => ._RISCV, - .sparc => ._SPARC, - .sparcel => ._SPARC, - .tce => ._NONE, - .tcele => ._NONE, - .thumb => ._ARM, - .thumbeb => ._ARM, - .i386 => ._386, - .xcore => ._XCORE, - .nvptx => ._NONE, - .amdil => ._NONE, - .hsail => ._NONE, - .spir => ._NONE, - .kalimba => ._CSR_KALIMBA, - .shave => ._NONE, - .lanai => ._LANAI, - .wasm32 => ._NONE, - .renderscript32 => ._NONE, - .aarch64_32 => ._AARCH64, - .aarch64 => ._AARCH64, - .aarch64_be => ._AARCH64, - .mips64 => ._MIPS, - .mips64el => ._MIPS_RS3_LE, - .powerpc64 => ._PPC64, - .powerpc64le => ._PPC64, - .riscv64 => ._RISCV, - .x86_64 => ._X86_64, - .nvptx64 => ._NONE, - .le64 => ._NONE, - .amdil64 => ._NONE, - .hsail64 => ._NONE, - .spir64 => ._NONE, - .wasm64 => ._NONE, - .renderscript64 => ._NONE, - .amdgcn => ._NONE, - .bpfel => ._BPF, - .bpfeb => ._BPF, - .sparcv9 => ._SPARCV9, - .s390x => ._S390, - }; - } - - pub fn endian(arch: Arch) builtin.Endian { - return switch (arch) { - .avr, - .arm, - .aarch64_32, - .aarch64, - .amdgcn, - .amdil, - .amdil64, - .bpfel, - .hexagon, - .hsail, - .hsail64, - .kalimba, - .le32, - .le64, - .mipsel, - .mips64el, - .msp430, - .nvptx, - .nvptx64, - .sparcel, - .tcele, - .powerpc64le, - .r600, - .riscv32, - .riscv64, - .i386, - .x86_64, - .wasm32, - .wasm64, - .xcore, - .thumb, - .spir, - .spir64, - .renderscript32, - .renderscript64, - .shave, - => .Little, - - .arc, - .armeb, - .aarch64_be, - .bpfeb, - .mips, - .mips64, - .powerpc, - .powerpc64, - .thumbeb, - .sparc, - .sparcv9, - .tce, - .lanai, - .s390x, - => .Big, - }; - } - - /// Returns a name that matches the lib/std/target/* directory name. - pub fn genericName(arch: Arch) []const u8 { - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => "arm", - .aarch64, .aarch64_be, .aarch64_32 => "aarch64", - .avr => "avr", - .bpfel, .bpfeb => "bpf", - .hexagon => "hexagon", - .mips, .mipsel, .mips64, .mips64el => "mips", - .msp430 => "msp430", - .powerpc, .powerpc64, .powerpc64le => "powerpc", - .amdgcn => "amdgpu", - .riscv32, .riscv64 => "riscv", - .sparc, .sparcv9, .sparcel => "sparc", - .s390x => "systemz", - .i386, .x86_64 => "x86", - .nvptx, .nvptx64 => "nvptx", - .wasm32, .wasm64 => "wasm", - else => @tagName(arch), - }; - } - - /// All CPU features Zig is aware of, sorted lexicographically by name. - pub fn allFeaturesList(arch: Arch) []const Cpu.Feature { - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => &arm.all_features, - .aarch64, .aarch64_be, .aarch64_32 => &aarch64.all_features, - .avr => &avr.all_features, - .bpfel, .bpfeb => &bpf.all_features, - .hexagon => &hexagon.all_features, - .mips, .mipsel, .mips64, .mips64el => &mips.all_features, - .msp430 => &msp430.all_features, - .powerpc, .powerpc64, .powerpc64le => &powerpc.all_features, - .amdgcn => &amdgpu.all_features, - .riscv32, .riscv64 => &riscv.all_features, - .sparc, .sparcv9, .sparcel => &sparc.all_features, - .s390x => &systemz.all_features, - .i386, .x86_64 => &x86.all_features, - .nvptx, .nvptx64 => &nvptx.all_features, - .wasm32, .wasm64 => &wasm.all_features, - - else => &[0]Cpu.Feature{}, - }; - } - - /// The "default" set of CPU features for cross-compiling. A conservative set - /// of features that is expected to be supported on most available hardware. - pub fn getBaselineCpuFeatures(arch: Arch) CpuFeatures { - const S = struct { - const generic_cpu = Cpu{ - .name = "generic", - .llvm_name = null, - .features = Cpu.Feature.Set.empty, - }; - }; - const cpu = switch (arch) { - .arm, .armeb, .thumb, .thumbeb => &arm.cpu.generic, - .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, - .avr => &avr.cpu.avr1, - .bpfel, .bpfeb => &bpf.cpu.generic, - .hexagon => &hexagon.cpu.generic, - .mips, .mipsel => &mips.cpu.mips32, - .mips64, .mips64el => &mips.cpu.mips64, - .msp430 => &msp430.cpu.generic, - .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic, - .amdgcn => &amdgpu.cpu.generic, - .riscv32 => &riscv.cpu.baseline_rv32, - .riscv64 => &riscv.cpu.baseline_rv64, - .sparc, .sparcv9, .sparcel => &sparc.cpu.generic, - .s390x => &systemz.cpu.generic, - .i386 => &x86.cpu.pentium4, - .x86_64 => &x86.cpu.x86_64, - .nvptx, .nvptx64 => &nvptx.cpu.sm_20, - .wasm32, .wasm64 => &wasm.cpu.generic, - - else => &S.generic_cpu, - }; - return CpuFeatures.initFromCpu(arch, cpu); - } - - /// All CPUs Zig is aware of, sorted lexicographically by name. - pub fn allCpus(arch: Arch) []const *const Cpu { - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => arm.all_cpus, - .aarch64, .aarch64_be, .aarch64_32 => aarch64.all_cpus, - .avr => avr.all_cpus, - .bpfel, .bpfeb => bpf.all_cpus, - .hexagon => hexagon.all_cpus, - .mips, .mipsel, .mips64, .mips64el => mips.all_cpus, - .msp430 => msp430.all_cpus, - .powerpc, .powerpc64, .powerpc64le => powerpc.all_cpus, - .amdgcn => amdgpu.all_cpus, - .riscv32, .riscv64 => riscv.all_cpus, - .sparc, .sparcv9, .sparcel => sparc.all_cpus, - .s390x => systemz.all_cpus, - .i386, .x86_64 => x86.all_cpus, - .nvptx, .nvptx64 => nvptx.all_cpus, - .wasm32, .wasm64 => wasm.all_cpus, - - else => &[0]*const Cpu{}, - }; - } - }; - pub const Abi = enum { none, gnu, @@ -539,11 +98,102 @@ pub const Target = union(enum) { coreclr, simulator, macabi, + + pub fn default(arch: Cpu.Arch, target_os: Os) Abi { + switch (arch) { + .wasm32, .wasm64 => return .musl, + else => {}, + } + switch (target_os) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .lv2, + .solaris, + .haiku, + .minix, + .rtems, + .nacl, + .cnk, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .other, + => return .eabi, + .openbsd, + .macosx, + .freebsd, + .ios, + .tvos, + .watchos, + .fuchsia, + .kfreebsd, + .netbsd, + .hurd, + => return .gnu, + .windows, + .uefi, + => return .msvc, + .linux, + .wasi, + .emscripten, + => return .musl, + } + } + + pub fn parse(text: []const u8) !Abi { + const info = @typeInfo(Abi); + inline for (info.Enum.fields) |field| { + if (mem.eql(u8, text, field.name)) { + return @field(Abi, field.name); + } + } + return error.UnknownApplicationBinaryInterface; + } + }; + + pub const ObjectFormat = enum { + unknown, + coff, + elf, + macho, + wasm, + }; + + pub const SubSystem = enum { + Console, + Windows, + Posix, + Native, + EfiApplication, + EfiBootServiceDriver, + EfiRom, + EfiRuntimeDriver, + }; + + pub const Cross = struct { + cpu: Cpu, + os: Os, + abi: Abi, }; pub const Cpu = struct { - name: []const u8, - llvm_name: ?[:0]const u8, + /// Architecture + arch: Arch, + + /// The CPU model to target. It has a set of features + /// which are overridden with the `features` field. + model: *const Model, + + /// An explicit list of the entire CPU feature set. It may differ from the specific CPU model's features. features: Feature.Set, pub const Feature = struct { @@ -569,7 +219,7 @@ pub const Target = union(enum) { pub const Set = struct { ints: [usize_count]usize, - pub const needed_bit_count = 174; + pub const needed_bit_count = 154; pub const byte_count = (needed_bit_count + 7) / 8; pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize); pub const Index = std.math.Log2Int(@IntType(false, usize_count * @bitSizeOf(usize))); @@ -593,6 +243,12 @@ pub const Target = union(enum) { set.ints[usize_index] |= @as(usize, 1) << bit_index; } + /// Adds the specified feature set but not its dependencies. + pub fn addFeatureSet(set: *Set, other_set: Set) void { + set.ints = @as(@Vector(usize_count, usize), set.ints) | + @as(@Vector(usize_count, usize), other_set.ints); + } + /// Removes the specified feature but not its dependents. pub fn removeFeature(set: *Set, arch_feature_index: Index) void { const usize_index = arch_feature_index / @bitSizeOf(usize); @@ -608,8 +264,7 @@ pub const Target = union(enum) { for (all_features_list) |feature, index_usize| { const index = @intCast(Index, index_usize); if (set.isEnabled(index)) { - set.ints = @as(@Vector(usize_count, usize), set.ints) | - @as(@Vector(usize_count, usize), feature.dependencies.ints); + set.addFeatureSet(feature.dependencies); } } const nothing_changed = mem.eql(usize, &old, &set.ints); @@ -644,77 +299,360 @@ pub const Target = union(enum) { }; } }; - }; - pub const ObjectFormat = enum { - unknown, - coff, - elf, - macho, - wasm, - }; + pub const Arch = enum { + arm, + armeb, + aarch64, + aarch64_be, + aarch64_32, + arc, + avr, + bpfel, + bpfeb, + hexagon, + mips, + mipsel, + mips64, + mips64el, + msp430, + powerpc, + powerpc64, + powerpc64le, + r600, + amdgcn, + riscv32, + riscv64, + sparc, + sparcv9, + sparcel, + s390x, + tce, + tcele, + thumb, + thumbeb, + i386, + x86_64, + xcore, + nvptx, + nvptx64, + le32, + le64, + amdil, + amdil64, + hsail, + hsail64, + spir, + spir64, + kalimba, + shave, + lanai, + wasm32, + wasm64, + renderscript32, + renderscript64, + + pub fn isARM(arch: Arch) bool { + return switch (arch) { + .arm, .armeb => true, + else => false, + }; + } - pub const SubSystem = enum { - Console, - Windows, - Posix, - Native, - EfiApplication, - EfiBootServiceDriver, - EfiRom, - EfiRuntimeDriver, - }; + pub fn isThumb(arch: Arch) bool { + return switch (arch) { + .thumb, .thumbeb => true, + else => false, + }; + } - pub const Cross = struct { - arch: Arch, - os: Os, - abi: Abi, - cpu_features: CpuFeatures, - }; + pub fn isWasm(arch: Arch) bool { + return switch (arch) { + .wasm32, .wasm64 => true, + else => false, + }; + } - pub const CpuFeatures = struct { - /// The CPU to target. It has a set of features - /// which are overridden with the `features` field. - cpu: *const Cpu, + pub fn isRISCV(arch: Arch) bool { + return switch (arch) { + .riscv32, .riscv64 => true, + else => false, + }; + } - /// Explicitly provide the entire CPU feature set. - features: Cpu.Feature.Set, + pub fn isMIPS(arch: Arch) bool { + return switch (arch) { + .mips, .mipsel, .mips64, .mips64el => true, + else => false, + }; + } - pub fn initFromCpu(arch: Arch, cpu: *const Cpu) CpuFeatures { - var features = cpu.features; - if (arch.subArchFeature()) |sub_arch_index| { - features.addFeature(sub_arch_index); + pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model { + for (arch.allCpuModels()) |cpu| { + if (mem.eql(u8, cpu_name, cpu.name)) { + return cpu; + } + } + return error.UnknownCpu; + } + + pub fn toElfMachine(arch: Arch) std.elf.EM { + return switch (arch) { + .avr => ._AVR, + .msp430 => ._MSP430, + .arc => ._ARC, + .arm => ._ARM, + .armeb => ._ARM, + .hexagon => ._HEXAGON, + .le32 => ._NONE, + .mips => ._MIPS, + .mipsel => ._MIPS_RS3_LE, + .powerpc => ._PPC, + .r600 => ._NONE, + .riscv32 => ._RISCV, + .sparc => ._SPARC, + .sparcel => ._SPARC, + .tce => ._NONE, + .tcele => ._NONE, + .thumb => ._ARM, + .thumbeb => ._ARM, + .i386 => ._386, + .xcore => ._XCORE, + .nvptx => ._NONE, + .amdil => ._NONE, + .hsail => ._NONE, + .spir => ._NONE, + .kalimba => ._CSR_KALIMBA, + .shave => ._NONE, + .lanai => ._LANAI, + .wasm32 => ._NONE, + .renderscript32 => ._NONE, + .aarch64_32 => ._AARCH64, + .aarch64 => ._AARCH64, + .aarch64_be => ._AARCH64, + .mips64 => ._MIPS, + .mips64el => ._MIPS_RS3_LE, + .powerpc64 => ._PPC64, + .powerpc64le => ._PPC64, + .riscv64 => ._RISCV, + .x86_64 => ._X86_64, + .nvptx64 => ._NONE, + .le64 => ._NONE, + .amdil64 => ._NONE, + .hsail64 => ._NONE, + .spir64 => ._NONE, + .wasm64 => ._NONE, + .renderscript64 => ._NONE, + .amdgcn => ._NONE, + .bpfel => ._BPF, + .bpfeb => ._BPF, + .sparcv9 => ._SPARCV9, + .s390x => ._S390, + }; } - features.populateDependencies(arch.allFeaturesList()); - return CpuFeatures{ - .cpu = cpu, - .features = features, + + pub fn endian(arch: Arch) builtin.Endian { + return switch (arch) { + .avr, + .arm, + .aarch64_32, + .aarch64, + .amdgcn, + .amdil, + .amdil64, + .bpfel, + .hexagon, + .hsail, + .hsail64, + .kalimba, + .le32, + .le64, + .mipsel, + .mips64el, + .msp430, + .nvptx, + .nvptx64, + .sparcel, + .tcele, + .powerpc64le, + .r600, + .riscv32, + .riscv64, + .i386, + .x86_64, + .wasm32, + .wasm64, + .xcore, + .thumb, + .spir, + .spir64, + .renderscript32, + .renderscript64, + .shave, + => .Little, + + .arc, + .armeb, + .aarch64_be, + .bpfeb, + .mips, + .mips64, + .powerpc, + .powerpc64, + .thumbeb, + .sparc, + .sparcv9, + .tce, + .lanai, + .s390x, + => .Big, + }; + } + + /// Returns a name that matches the lib/std/target/* directory name. + pub fn genericName(arch: Arch) []const u8 { + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => "arm", + .aarch64, .aarch64_be, .aarch64_32 => "aarch64", + .avr => "avr", + .bpfel, .bpfeb => "bpf", + .hexagon => "hexagon", + .mips, .mipsel, .mips64, .mips64el => "mips", + .msp430 => "msp430", + .powerpc, .powerpc64, .powerpc64le => "powerpc", + .amdgcn => "amdgpu", + .riscv32, .riscv64 => "riscv", + .sparc, .sparcv9, .sparcel => "sparc", + .s390x => "systemz", + .i386, .x86_64 => "x86", + .nvptx, .nvptx64 => "nvptx", + .wasm32, .wasm64 => "wasm", + else => @tagName(arch), + }; + } + + /// All CPU features Zig is aware of, sorted lexicographically by name. + pub fn allFeaturesList(arch: Arch) []const Cpu.Feature { + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => &arm.all_features, + .aarch64, .aarch64_be, .aarch64_32 => &aarch64.all_features, + .avr => &avr.all_features, + .bpfel, .bpfeb => &bpf.all_features, + .hexagon => &hexagon.all_features, + .mips, .mipsel, .mips64, .mips64el => &mips.all_features, + .msp430 => &msp430.all_features, + .powerpc, .powerpc64, .powerpc64le => &powerpc.all_features, + .amdgcn => &amdgpu.all_features, + .riscv32, .riscv64 => &riscv.all_features, + .sparc, .sparcv9, .sparcel => &sparc.all_features, + .s390x => &systemz.all_features, + .i386, .x86_64 => &x86.all_features, + .nvptx, .nvptx64 => &nvptx.all_features, + .wasm32, .wasm64 => &wasm.all_features, + + else => &[0]Cpu.Feature{}, + }; + } + + /// All processors Zig is aware of, sorted lexicographically by name. + pub fn allCpuModels(arch: Arch) []const *const Cpu.Model { + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => arm.all_cpus, + .aarch64, .aarch64_be, .aarch64_32 => aarch64.all_cpus, + .avr => avr.all_cpus, + .bpfel, .bpfeb => bpf.all_cpus, + .hexagon => hexagon.all_cpus, + .mips, .mipsel, .mips64, .mips64el => mips.all_cpus, + .msp430 => msp430.all_cpus, + .powerpc, .powerpc64, .powerpc64le => powerpc.all_cpus, + .amdgcn => amdgpu.all_cpus, + .riscv32, .riscv64 => riscv.all_cpus, + .sparc, .sparcv9, .sparcel => sparc.all_cpus, + .s390x => systemz.all_cpus, + .i386, .x86_64 => x86.all_cpus, + .nvptx, .nvptx64 => nvptx.all_cpus, + .wasm32, .wasm64 => wasm.all_cpus, + + else => &[0]*const Model{}, + }; + } + + pub fn parse(text: []const u8) !Arch { + const info = @typeInfo(Arch); + inline for (info.Enum.fields) |field| { + if (mem.eql(u8, text, field.name)) { + return @as(Arch, @field(Arch, field.name)); + } + } + return error.UnknownArchitecture; + } + }; + + pub const Model = struct { + name: []const u8, + llvm_name: ?[:0]const u8, + features: Feature.Set, + + pub fn toCpu(model: *const Model, arch: Arch) Cpu { + var features = model.features; + features.populateDependencies(arch.allFeaturesList()); + return .{ + .arch = arch, + .model = model, + .features = features, + }; + } + }; + + /// The "default" set of CPU features for cross-compiling. A conservative set + /// of features that is expected to be supported on most available hardware. + pub fn baseline(arch: Arch) Cpu { + const S = struct { + const generic_model = Model{ + .name = "generic", + .llvm_name = null, + .features = Cpu.Feature.Set.empty, + }; + }; + const model = switch (arch) { + .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline, + .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, + .avr => &avr.cpu.avr1, + .bpfel, .bpfeb => &bpf.cpu.generic, + .hexagon => &hexagon.cpu.generic, + .mips, .mipsel => &mips.cpu.mips32, + .mips64, .mips64el => &mips.cpu.mips64, + .msp430 => &msp430.cpu.generic, + .powerpc, .powerpc64, .powerpc64le => &powerpc.cpu.generic, + .amdgcn => &amdgpu.cpu.generic, + .riscv32 => &riscv.cpu.baseline_rv32, + .riscv64 => &riscv.cpu.baseline_rv64, + .sparc, .sparcv9, .sparcel => &sparc.cpu.generic, + .s390x => &systemz.cpu.generic, + .i386 => &x86.cpu.pentium4, + .x86_64 => &x86.cpu.x86_64, + .nvptx, .nvptx64 => &nvptx.cpu.sm_20, + .wasm32, .wasm64 => &wasm.cpu.generic, + + else => &S.generic_model, }; + return model.toCpu(arch); } }; pub const current = Target{ .Cross = Cross{ - .arch = builtin.arch, + .cpu = builtin.cpu, .os = builtin.os, .abi = builtin.abi, - .cpu_features = builtin.cpu_features, }, }; pub const stack_align = 16; - pub fn getCpuFeatures(self: Target) CpuFeatures { - return switch (self) { - .Native => builtin.cpu_features, - .Cross => |cross| cross.cpu_features, - }; - } - pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { - return std.fmt.allocPrint(allocator, "{}{}-{}-{}", .{ + return std.fmt.allocPrint(allocator, "{}-{}-{}", .{ @tagName(self.getArch()), - Target.archSubArchName(self.getArch()), @tagName(self.getOs()), @tagName(self.getAbi()), }); @@ -776,139 +714,115 @@ pub const Target = union(enum) { }); } - /// TODO: Support CPU features here? - /// https://github.com/ziglang/zig/issues/4261 - pub fn parse(text: []const u8) !Target { - var it = mem.separate(text, "-"); - const arch_name = it.next() orelse return error.MissingArchitecture; - const os_name = it.next() orelse return error.MissingOperatingSystem; - const abi_name = it.next(); - const arch = try parseArchSub(arch_name); + pub const ParseOptions = struct { + /// This is sometimes called a "triple". It looks roughly like this: + /// riscv64-linux-gnu + /// The fields are, respectively: + /// * CPU Architecture + /// * Operating System + /// * C ABI (optional) + arch_os_abi: []const u8, - var cross = Cross{ - .arch = arch, - .cpu_features = arch.getBaselineCpuFeatures(), - .os = try parseOs(os_name), - .abi = undefined, - }; - cross.abi = if (abi_name) |n| try parseAbi(n) else defaultAbi(cross.arch, cross.os); - return Target{ .Cross = cross }; - } + /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e" + /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features + /// to remove from the set. + cpu_features: []const u8 = "baseline", - pub fn defaultAbi(arch: Arch, target_os: Os) Abi { - switch (arch) { - .wasm32, .wasm64 => return .musl, - else => {}, - } - switch (target_os) { - .freestanding, - .ananas, - .cloudabi, - .dragonfly, - .lv2, - .solaris, - .haiku, - .minix, - .rtems, - .nacl, - .cnk, - .aix, - .cuda, - .nvcl, - .amdhsa, - .ps4, - .elfiamcu, - .mesa3d, - .contiki, - .amdpal, - .hermit, - .other, - => return .eabi, - .openbsd, - .macosx, - .freebsd, - .ios, - .tvos, - .watchos, - .fuchsia, - .kfreebsd, - .netbsd, - .hurd, - => return .gnu, - .windows, - .uefi, - => return .msvc, - .linux, - .wasi, - .emscripten, - => return .musl, - } - } + /// If this is provided, the function will populate some information about parsing failures, + /// so that user-friendly error messages can be delivered. + diagnostics: ?*Diagnostics = null, - pub const ParseArchSubError = error{ - UnknownArchitecture, - UnknownSubArchitecture, - }; + pub const Diagnostics = struct { + /// If the architecture was determined, this will be populated. + arch: ?Cpu.Arch = null, - pub fn parseArchSub(text: []const u8) ParseArchSubError!Arch { - const info = @typeInfo(Arch); - inline for (info.Union.fields) |field| { - if (mem.startsWith(u8, text, field.name)) { - if (field.field_type == void) { - return @as(Arch, @field(Arch, field.name)); - } else { - const sub_info = @typeInfo(field.field_type); - inline for (sub_info.Enum.fields) |sub_field| { - const combined = field.name ++ sub_field.name; - if (mem.eql(u8, text, combined)) { - return @unionInit(Arch, field.name, @field(field.field_type, sub_field.name)); - } - } - return error.UnknownSubArchitecture; - } - } - } - return error.UnknownArchitecture; - } + /// If the OS was determined, this will be populated. + os: ?Os = null, - pub fn parseOs(text: []const u8) !Os { - const info = @typeInfo(Os); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @field(Os, field.name); - } - } - return error.UnknownOperatingSystem; - } + /// If the ABI was determined, this will be populated. + abi: ?Abi = null, - pub fn parseAbi(text: []const u8) !Abi { - const info = @typeInfo(Abi); - inline for (info.Enum.fields) |field| { - if (mem.eql(u8, text, field.name)) { - return @field(Abi, field.name); - } - } - return error.UnknownApplicationBinaryInterface; - } + /// If the CPU name was determined, this will be populated. + cpu_name: ?[]const u8 = null, - fn archSubArchName(arch: Arch) []const u8 { - return switch (arch) { - .arm => |sub| @tagName(sub), - .armeb => |sub| @tagName(sub), - .thumb => |sub| @tagName(sub), - .thumbeb => |sub| @tagName(sub), - .aarch64 => |sub| @tagName(sub), - .aarch64_be => |sub| @tagName(sub), - .kalimba => |sub| @tagName(sub), - else => "", + /// If error.UnknownCpuFeature is returned, this will be populated. + unknown_feature_name: ?[]const u8 = null, }; - } + }; - pub fn subArchName(self: Target) []const u8 { - switch (self) { - .Native => return archSubArchName(builtin.arch), - .Cross => |cross| return archSubArchName(cross.arch), + pub fn parse(args: ParseOptions) !Target { + var dummy_diags: ParseOptions.Diagnostics = undefined; + var diags = args.diagnostics orelse &dummy_diags; + + var it = mem.separate(args.arch_os_abi, "-"); + const arch_name = it.next() orelse return error.MissingArchitecture; + const arch = try Cpu.Arch.parse(arch_name); + diags.arch = arch; + + const os_name = it.next() orelse return error.MissingOperatingSystem; + const os = try Os.parse(os_name); + diags.os = os; + + const abi_name = it.next(); + const abi = if (abi_name) |n| try Abi.parse(n) else Abi.default(arch, os); + diags.abi = abi; + + if (it.next() != null) return error.UnexpectedExtraField; + + const all_features = arch.allFeaturesList(); + var index: usize = 0; + while (index < args.cpu_features.len and + args.cpu_features[index] != '+' and + args.cpu_features[index] != '-') + { + index += 1; } + const cpu_name = args.cpu_features[0..index]; + diags.cpu_name = cpu_name; + + const cpu: Cpu = if (mem.eql(u8, cpu_name, "baseline")) Cpu.baseline(arch) else blk: { + const cpu_model = try arch.parseCpuModel(cpu_name); + + var set = cpu_model.features; + while (index < args.cpu_features.len) { + const op = args.cpu_features[index]; + index += 1; + const start = index; + while (index < args.cpu_features.len and + args.cpu_features[index] != '+' and + args.cpu_features[index] != '-') + { + index += 1; + } + const feature_name = args.cpu_features[start..index]; + for (all_features) |feature, feat_index_usize| { + const feat_index = @intCast(Cpu.Feature.Set.Index, feat_index_usize); + if (mem.eql(u8, feature_name, feature.name)) { + switch (op) { + '+' => set.addFeature(feat_index), + '-' => set.removeFeature(feat_index), + else => unreachable, + } + break; + } + } else { + diags.unknown_feature_name = feature_name; + return error.UnknownCpuFeature; + } + } + set.populateDependencies(all_features); + break :blk .{ + .arch = arch, + .model = cpu_model, + .features = set, + }; + }; + var cross = Cross{ + .cpu = cpu, + .os = os, + .abi = abi, + }; + return Target{ .Cross = cross }; } pub fn oFileExt(self: Target) []const u8 { @@ -967,11 +881,15 @@ pub const Target = union(enum) { }; } - pub fn getArch(self: Target) Arch { - switch (self) { - .Native => return builtin.arch, - .Cross => |t| return t.arch, - } + pub fn getCpu(self: Target) Cpu { + return switch (self) { + .Native => builtin.cpu, + .Cross => |cross| cross.cpu, + }; + } + + pub fn getArch(self: Target) Cpu.Arch { + return self.getCpu().arch; } pub fn getAbi(self: Target) Abi { @@ -1372,14 +1290,32 @@ pub const Target = union(enum) { } }; -test "parseCpuFeatureSet" { - const arch: Target.Arch = .x86_64; - const baseline = arch.getBaselineCpuFeatures(); - const set = try arch.parseCpuFeatureSet(baseline.cpu, "-sse,-avx,-cx8"); - std.testing.expect(!Target.x86.featureSetHas(set, .sse)); - std.testing.expect(!Target.x86.featureSetHas(set, .avx)); - std.testing.expect(!Target.x86.featureSetHas(set, .cx8)); - // These are expected because they are part of the baseline - std.testing.expect(Target.x86.featureSetHas(set, .cmov)); - std.testing.expect(Target.x86.featureSetHas(set, .fxsr)); +test "Target.parse" { + { + const target = (try Target.parse(.{ + .arch_os_abi = "x86_64-linux-gnu", + .cpu_features = "x86_64-sse-sse2-avx-cx8", + })).Cross; + + std.testing.expect(target.os == .linux); + std.testing.expect(target.abi == .gnu); + std.testing.expect(target.cpu.arch == .x86_64); + std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse)); + std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx)); + std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8)); + std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov)); + std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr)); + } + { + const target = (try Target.parse(.{ + .arch_os_abi = "arm-linux-musleabihf", + .cpu_features = "generic+v8a", + })).Cross; + + std.testing.expect(target.os == .linux); + std.testing.expect(target.abi == .musleabihf); + std.testing.expect(target.cpu.arch == .arm); + std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); + std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); + } } |
