const std = @import("std"); const builtin = @import("builtin"); const fs = std.fs; const mem = std.mem; const json = std.json; const assert = std.debug.assert; // All references to other features are based on "zig name" as the key. const FeatureOverride = struct { llvm_name: []const u8, /// If true, completely omit the feature; as if it does not exist. omit: bool = false, /// If true, omit the feature, but all the dependencies of the feature /// are added in its place. flatten: bool = false, zig_name: ?[]const u8 = null, desc: ?[]const u8 = null, omit_deps: []const []const u8 = &.{}, extra_deps: []const []const u8 = &.{}, }; const Cpu = struct { llvm_name: ?[]const u8, zig_name: []const u8, features: []const []const u8, }; const Feature = struct { llvm_name: ?[]const u8 = null, zig_name: []const u8, desc: []const u8, deps: []const []const u8, flatten: bool = false, }; const LlvmTarget = struct { zig_name: []const u8, llvm_name: []const u8, td_name: []const u8, feature_overrides: []const FeatureOverride = &.{}, extra_cpus: []const Cpu = &.{}, extra_features: []const Feature = &.{}, omit_cpus: []const []const u8 = &.{}, branch_quota: ?usize = null, }; const llvm_targets = [_]LlvmTarget{ .{ .zig_name = "aarch64", .llvm_name = "AArch64", .td_name = "AArch64.td", .branch_quota = 2000, .feature_overrides = &.{ .{ .llvm_name = "all", .omit = true, }, .{ .llvm_name = "CONTEXTIDREL2", .zig_name = "contextidr_el2", .desc = "Enable RW operand Context ID Register (EL2)", }, .{ .llvm_name = "v8a", .extra_deps = &.{"neon"}, }, .{ .llvm_name = "neoversee1", .flatten = true, }, .{ .llvm_name = "neoversen1", .flatten = true, }, .{ .llvm_name = "neoversen2", .flatten = true, }, .{ .llvm_name = "neoversen3", .flatten = true, }, .{ .llvm_name = "neoversev1", .flatten = true, }, .{ .llvm_name = "neoversev2", .flatten = true, }, .{ .llvm_name = "neoversev3", .flatten = true, }, .{ .llvm_name = "neoversev3AE", .flatten = true, }, .{ .llvm_name = "neoverse512tvb", .flatten = true, }, .{ .llvm_name = "oryon-1", .flatten = true, }, .{ .llvm_name = "exynosm3", .flatten = true, }, .{ .llvm_name = "exynosm4", .flatten = true, }, .{ .llvm_name = "a35", .flatten = true, }, .{ .llvm_name = "a53", .flatten = true, }, .{ .llvm_name = "a55", .flatten = true, }, .{ .llvm_name = "a57", .flatten = true, }, .{ .llvm_name = "a510", .flatten = true, }, .{ .llvm_name = "a520", .flatten = true, }, .{ .llvm_name = "a520ae", .flatten = true, }, .{ .llvm_name = "a64fx", .flatten = true, }, .{ .llvm_name = "a65", .flatten = true, }, .{ .llvm_name = "a72", .flatten = true, }, .{ .llvm_name = "a73", .flatten = true, }, .{ .llvm_name = "a75", .flatten = true, }, .{ .llvm_name = "a76", .flatten = true, }, .{ .llvm_name = "a77", .flatten = true, }, .{ .llvm_name = "a78", .flatten = true, }, .{ .llvm_name = "a78ae", .flatten = true, }, .{ .llvm_name = "a78c", .flatten = true, }, .{ .llvm_name = "a710", .flatten = true, }, .{ .llvm_name = "a715", .flatten = true, }, .{ .llvm_name = "a720", .flatten = true, }, .{ .llvm_name = "a720ae", .flatten = true, }, .{ .llvm_name = "ampere1a", .flatten = true, }, .{ .llvm_name = "apple-a7", .flatten = true, }, .{ .llvm_name = "apple-a10", .flatten = true, }, .{ .llvm_name = "apple-a11", .flatten = true, }, .{ .llvm_name = "apple-a12", .flatten = true, }, .{ .llvm_name = "apple-a13", .flatten = true, }, .{ .llvm_name = "apple-a14", .flatten = true, }, .{ .llvm_name = "apple-a15", .flatten = true, }, .{ .llvm_name = "apple-a16", .flatten = true, }, .{ .llvm_name = "apple-a17", .flatten = true, }, .{ .llvm_name = "apple-a7-sysreg", .flatten = true, }, .{ .llvm_name = "apple-m4", .flatten = true, }, .{ .llvm_name = "carmel", .flatten = true, }, .{ .llvm_name = "cortex-a725", .flatten = true, }, .{ .llvm_name = "cortex-a78", .flatten = true, }, .{ .llvm_name = "cortex-r82", .flatten = true, }, .{ .llvm_name = "cortex-r82ae", .flatten = true, }, .{ .llvm_name = "cortex-x1", .flatten = true, }, .{ .llvm_name = "cortex-x2", .flatten = true, }, .{ .llvm_name = "cortex-x3", .flatten = true, }, .{ .llvm_name = "cortex-x4", .flatten = true, }, .{ .llvm_name = "cortex-x925", .flatten = true, }, .{ .llvm_name = "falkor", .flatten = true, }, .{ .llvm_name = "kryo", .flatten = true, }, .{ .llvm_name = "saphira", .flatten = true, }, .{ .llvm_name = "thunderx", .flatten = true, }, .{ .llvm_name = "thunderx2t99", .flatten = true, }, .{ .llvm_name = "thunderx3t110", .flatten = true, }, .{ .llvm_name = "thunderxt81", .flatten = true, }, .{ .llvm_name = "thunderxt83", .flatten = true, }, .{ .llvm_name = "thunderxt88", .flatten = true, }, .{ .llvm_name = "tsv110", .flatten = true, }, .{ .llvm_name = "ampere1", .flatten = true, }, .{ .llvm_name = "ampere1b", .flatten = true, }, }, .extra_cpus = &.{ .{ .llvm_name = null, .zig_name = "exynos_m1", .features = &.{ "crc", "crypto", "exynos_cheap_as_move", "force_32bit_jump_tables", "fuse_aes", "perfmon", "slow_misaligned_128store", "slow_paired_128", "use_postra_scheduler", "use_reciprocal_square_root", "v8a", }, }, .{ .llvm_name = null, .zig_name = "exynos_m2", .features = &.{ "crc", "crypto", "exynos_cheap_as_move", "force_32bit_jump_tables", "fuse_aes", "perfmon", "slow_misaligned_128store", "slow_paired_128", "use_postra_scheduler", "v8a", }, }, .{ .llvm_name = null, .zig_name = "xgene1", .features = &.{ "perfmon", "v8a", }, }, .{ .llvm_name = null, .zig_name = "emag", .features = &.{ "crc", "crypto", "perfmon", "v8a", }, }, }, .omit_cpus = &.{ // Who thought this alias was a good idea? Upgrade your compiler and suddenly your // programs SIGILL because this changed meaning. Brilliant. "apple-latest", }, }, .{ .zig_name = "amdgpu", .llvm_name = "AMDGPU", .td_name = "AMDGPU.td", .feature_overrides = &.{ .{ .llvm_name = "DumpCode", .omit = true, }, .{ .llvm_name = "dumpcode", .omit = true, }, .{ .llvm_name = "enable-ds128", .zig_name = "ds128", }, .{ .llvm_name = "enable-flat-scratch", .zig_name = "flat_scratch", }, .{ .llvm_name = "enable-prt-strict-null", .zig_name = "prt_strict_null", }, }, }, .{ .zig_name = "arc", .llvm_name = "ARC", .td_name = "ARC.td", }, .{ .zig_name = "arm", .llvm_name = "ARM", .td_name = "ARM.td", .branch_quota = 10000, .extra_cpus = &.{ .{ .llvm_name = "generic", .zig_name = "baseline", .features = &.{"v7a"}, }, .{ .llvm_name = null, .zig_name = "exynos_m1", .features = &.{ "v8a", "exynos" }, }, .{ .llvm_name = null, .zig_name = "exynos_m2", .features = &.{ "v8a", "exynos" }, }, }, .feature_overrides = &.{ .{ .llvm_name = "exynos", .flatten = true, }, .{ .llvm_name = "cortex-a78", .flatten = true, }, .{ .llvm_name = "cortex-a78ae", .flatten = true, }, .{ .llvm_name = "cortex-a710", .flatten = true, }, .{ .llvm_name = "cortex-m4", .omit_deps = &.{"vfp4d16sp"}, }, .{ .llvm_name = "cortex-m7", .omit_deps = &.{"fp_armv8d16"}, }, .{ .llvm_name = "cortex-m33", .omit_deps = &.{ "fp_armv8d16sp", "dsp" }, }, .{ .llvm_name = "cortex-m35p", .omit_deps = &.{ "fp_armv8d16sp", "dsp" }, }, .{ .llvm_name = "cortex-m55", .omit_deps = &.{ "mve_fp", "fp_armv8d16" }, }, .{ .llvm_name = "cortex-m85", .omit_deps = &.{ "mve_fp", "pacbti", "fp_armv8d16" }, .extra_deps = &.{"trustzone"}, }, .{ .llvm_name = "cortex-x1c", .flatten = true, }, .{ .llvm_name = "r4", .flatten = true, }, .{ .llvm_name = "r52plus", .flatten = true, }, .{ .llvm_name = "r5", .flatten = true, }, .{ .llvm_name = "r52", .flatten = true, }, .{ .llvm_name = "r7", .flatten = true, }, .{ .llvm_name = "m3", .flatten = true, }, .{ .llvm_name = "m7", .flatten = true, }, .{ .llvm_name = "krait", .flatten = true, }, .{ .llvm_name = "kryo", .flatten = true, }, .{ .llvm_name = "swift", .flatten = true, }, .{ .llvm_name = "cortex-x1", .flatten = true, }, .{ .llvm_name = "neoverse-v1", .flatten = true, }, .{ .llvm_name = "a5", .flatten = true, }, .{ .llvm_name = "a7", .flatten = true, }, .{ .llvm_name = "a8", .flatten = true, }, .{ .llvm_name = "a9", .flatten = true, }, .{ .llvm_name = "a12", .flatten = true, }, .{ .llvm_name = "a15", .flatten = true, }, .{ .llvm_name = "a17", .flatten = true, }, .{ .llvm_name = "a32", .flatten = true, }, .{ .llvm_name = "a35", .flatten = true, }, .{ .llvm_name = "a53", .flatten = true, }, .{ .llvm_name = "a55", .flatten = true, }, .{ .llvm_name = "a57", .flatten = true, }, .{ .llvm_name = "a72", .flatten = true, }, .{ .llvm_name = "a73", .flatten = true, }, .{ .llvm_name = "a75", .flatten = true, }, .{ .llvm_name = "a76", .flatten = true, }, .{ .llvm_name = "a77", .flatten = true, }, .{ .llvm_name = "a78c", .flatten = true, }, .{ .llvm_name = "armv2", .zig_name = "v2", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv2a", .zig_name = "v2a", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv3", .zig_name = "v3", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv3m", .zig_name = "v3m", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv4", .zig_name = "v4", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv4t", .zig_name = "v4t", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv5t", .zig_name = "v5t", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv5te", .zig_name = "v5te", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv5tej", .zig_name = "v5tej", .extra_deps = &.{"strict_align"}, }, .{ .llvm_name = "armv6", .zig_name = "v6", }, .{ .llvm_name = "armv6-m", .zig_name = "v6m", }, .{ .llvm_name = "armv6j", .zig_name = "v6j", }, .{ .llvm_name = "armv6k", .zig_name = "v6k", }, .{ .llvm_name = "armv6kz", .zig_name = "v6kz", }, .{ .llvm_name = "armv6s-m", .zig_name = "v6sm", }, .{ .llvm_name = "armv6t2", .zig_name = "v6t2", }, .{ .llvm_name = "armv7-a", .zig_name = "v7a", }, .{ .llvm_name = "armv7-m", .zig_name = "v7m", }, .{ .llvm_name = "armv7-r", .zig_name = "v7r", }, .{ .llvm_name = "armv7e-m", .zig_name = "v7em", }, .{ .llvm_name = "armv7k", .zig_name = "v7k", }, .{ .llvm_name = "armv7s", .zig_name = "v7s", }, .{ .llvm_name = "armv7ve", .zig_name = "v7ve", }, .{ .llvm_name = "armv8.1-a", .zig_name = "v8_1a", }, .{ .llvm_name = "armv8.1-m.main", .zig_name = "v8_1m_main", }, .{ .llvm_name = "armv8.2-a", .zig_name = "v8_2a", }, .{ .llvm_name = "armv8.3-a", .zig_name = "v8_3a", }, .{ .llvm_name = "armv8.4-a", .zig_name = "v8_4a", }, .{ .llvm_name = "armv8.5-a", .zig_name = "v8_5a", }, .{ .llvm_name = "armv8.6-a", .zig_name = "v8_6a", }, .{ .llvm_name = "armv8.7-a", .zig_name = "v8_7a", }, .{ .llvm_name = "armv8.8-a", .zig_name = "v8_8a", }, .{ .llvm_name = "armv8.9-a", .zig_name = "v8_9a", }, .{ .llvm_name = "armv8-a", .zig_name = "v8a", }, .{ .llvm_name = "armv8-m.base", .zig_name = "v8m", }, .{ .llvm_name = "armv8-m.main", .zig_name = "v8m_main", }, .{ .llvm_name = "armv8-r", .zig_name = "v8r", }, .{ .llvm_name = "armv9.1-a", .zig_name = "v9_1a", }, .{ .llvm_name = "armv9.2-a", .zig_name = "v9_2a", }, .{ .llvm_name = "armv9.3-a", .zig_name = "v9_3a", }, .{ .llvm_name = "armv9.4-a", .zig_name = "v9_4a", }, .{ .llvm_name = "armv9.5-a", .zig_name = "v9_5a", }, .{ .llvm_name = "armv9-a", .zig_name = "v9a", }, .{ .llvm_name = "v4t", .zig_name = "has_v4t", }, .{ .llvm_name = "v5t", .zig_name = "has_v5t", }, .{ .llvm_name = "v5te", .zig_name = "has_v5te", }, .{ .llvm_name = "v6", .zig_name = "has_v6", }, .{ .llvm_name = "v6k", .zig_name = "has_v6k", }, .{ .llvm_name = "v6m", .zig_name = "has_v6m", }, .{ .llvm_name = "v6t2", .zig_name = "has_v6t2", }, .{ .llvm_name = "v7", .zig_name = "has_v7", }, .{ .llvm_name = "v7clrex", .zig_name = "has_v7clrex", }, .{ .llvm_name = "v8", .zig_name = "has_v8", }, .{ .llvm_name = "v8m", .zig_name = "has_v8m", }, .{ .llvm_name = "v8m.main", .zig_name = "has_v8m_main", }, .{ .llvm_name = "v8.1a", .zig_name = "has_v8_1a", }, .{ .llvm_name = "v8.1m.main", .zig_name = "has_v8_1m_main", }, .{ .llvm_name = "v8.2a", .zig_name = "has_v8_2a", }, .{ .llvm_name = "v8.3a", .zig_name = "has_v8_3a", }, .{ .llvm_name = "v8.4a", .zig_name = "has_v8_4a", }, .{ .llvm_name = "v8.5a", .zig_name = "has_v8_5a", }, .{ .llvm_name = "v8.6a", .zig_name = "has_v8_6a", }, .{ .llvm_name = "v8.7a", .zig_name = "has_v8_7a", }, .{ .llvm_name = "v8.8a", .zig_name = "has_v8_8a", }, .{ .llvm_name = "v8.9a", .zig_name = "has_v8_9a", }, .{ .llvm_name = "v9a", .zig_name = "has_v9a", }, .{ .llvm_name = "v9.1a", .zig_name = "has_v9_1a", }, .{ .llvm_name = "v9.2a", .zig_name = "has_v9_2a", }, .{ .llvm_name = "v9.3a", .zig_name = "has_v9_3a", }, .{ .llvm_name = "v9.4a", .zig_name = "has_v9_4a", }, .{ .llvm_name = "v9.5a", .zig_name = "has_v9_5a", }, }, // LLVM removed support for v2 and v3 but zig wants to support targeting old hardware .extra_features = &.{ .{ .zig_name = "v2", .desc = "ARMv2 architecture", .deps = &.{"strict_align"}, }, .{ .zig_name = "v2a", .desc = "ARMv2a architecture", .deps = &.{"strict_align"}, }, .{ .zig_name = "v3", .desc = "ARMv3 architecture", .deps = &.{"strict_align"}, }, .{ .zig_name = "v3m", .desc = "ARMv3m architecture", .deps = &.{"strict_align"}, }, }, }, .{ .zig_name = "avr", .llvm_name = "AVR", .td_name = "AVR.td", }, .{ .zig_name = "bpf", .llvm_name = "BPF", .td_name = "BPF.td", }, .{ .zig_name = "csky", .llvm_name = "CSKY", .td_name = "CSKY.td", }, .{ .zig_name = "hexagon", .llvm_name = "Hexagon", .td_name = "Hexagon.td", }, .{ .zig_name = "lanai", .llvm_name = "Lanai", .td_name = "Lanai.td", }, .{ .zig_name = "loongarch", .llvm_name = "LoongArch", .td_name = "LoongArch.td", }, .{ .zig_name = "m68k", .llvm_name = "M68k", .td_name = "M68k.td", }, .{ .zig_name = "msp430", .llvm_name = "MSP430", .td_name = "MSP430.td", }, .{ .zig_name = "mips", .llvm_name = "Mips", .td_name = "Mips.td", }, .{ .zig_name = "nvptx", .llvm_name = "NVPTX", .td_name = "NVPTX.td", }, .{ .zig_name = "powerpc", .llvm_name = "PowerPC", .td_name = "PPC.td", .omit_cpus = &.{ "ppc32", }, }, .{ .zig_name = "riscv", .llvm_name = "RISCV", .td_name = "RISCV.td", .branch_quota = 2000, .feature_overrides = &.{ .{ .llvm_name = "sifive7", .flatten = true, }, }, .extra_cpus = &.{ .{ .llvm_name = null, .zig_name = "baseline_rv32", .features = &.{ "32bit", "a", "c", "d", "f", "i", "m" }, }, .{ .llvm_name = null, .zig_name = "baseline_rv64", .features = &.{ "64bit", "a", "c", "d", "f", "i", "m" }, }, }, }, .{ .zig_name = "sparc", .llvm_name = "Sparc", .td_name = "Sparc.td", }, // TODO: merge tools/update_spirv_features.zig into this script //.{ // .zig_name = "spirv", // .llvm_name = "SPIRV", // .td_name = "SPIRV.td", //}, .{ .zig_name = "s390x", .llvm_name = "SystemZ", .td_name = "SystemZ.td", }, .{ .zig_name = "ve", .llvm_name = "VE", .td_name = "VE.td", }, .{ .zig_name = "wasm", .llvm_name = "WebAssembly", .td_name = "WebAssembly.td", }, .{ .zig_name = "x86", .llvm_name = "X86", .td_name = "X86.td", .feature_overrides = &.{ .{ .llvm_name = "64bit-mode", .omit = true, }, .{ .llvm_name = "amdfam10", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "athlon", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "athlon64", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "athlon64-sse3", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "athlon-4", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "athlon-fx", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "athlon-mp", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "athlon-tbird", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "athlon-xp", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "barcelona", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "c3", .extra_deps = &.{"3dnow"}, }, .{ .llvm_name = "geode", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "k6-2", .extra_deps = &.{"3dnow"}, }, .{ .llvm_name = "k6-3", .extra_deps = &.{"3dnow"}, }, .{ .llvm_name = "k8", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "k8-sse3", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "knl", .extra_deps = &.{ "avx512er", "avx512pf", "prefetchwt1", }, }, .{ .llvm_name = "knm", .extra_deps = &.{ "avx512er", "avx512pf", "prefetchwt1", }, }, .{ .llvm_name = "lakemont", .extra_deps = &.{"soft_float"}, }, .{ .llvm_name = "opteron", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "opteron-sse3", .extra_deps = &.{"3dnowa"}, }, .{ .llvm_name = "winchip2", .extra_deps = &.{"3dnow"}, }, }, // Features removed from LLVM .extra_features = &.{ .{ .zig_name = "3dnow", .desc = "Enable 3DNow! instructions", .deps = &.{"mmx"}, }, .{ .zig_name = "3dnowa", .desc = "Enable 3DNow! Athlon instructions", .deps = &.{"3dnow"}, }, .{ .zig_name = "avx512er", .desc = "Enable AVX-512 Exponential and Reciprocal Instructions", .deps = &.{"avx512f"}, }, .{ .zig_name = "avx512pf", .desc = "Enable AVX-512 PreFetch Instructions", .deps = &.{"avx512f"}, }, .{ .zig_name = "prefetchwt1", .desc = "Prefetch with Intent to Write and T1 Hint", .deps = &.{}, }, }, .omit_cpus = &.{ // LLVM defines a bunch of dumb aliases with foreach loops in X86.td. "pentium_mmx", "pentium_pro", "pentium_ii", "pentium_3m", "pentium_iii_no_xmm_regs", "pentium_iii", "pentium_m", "pentium4m", "pentium_4", "pentium_4_sse3", "core_2_duo_ssse3", "core_2_duo_sse4_1", "atom_sse4_2", "goldmont_plus", "core_i7_sse4_2", "core_aes_pclmulqdq", "corei7-avx", "core_2nd_gen_avx", "core-avx-i", "core_3rd_gen_avx", "core-avx2", "core_4th_gen_avx", "core_4th_gen_avx_tsx", "core_5th_gen_avx", "core_5th_gen_avx_tsx", "mic_avx512", "skylake_avx512", "icelake_client", "icelake_server", "graniterapids_d", "arrowlake_s", }, }, .{ .zig_name = "xcore", .llvm_name = "XCore", .td_name = "XCore.td", }, .{ .zig_name = "xtensa", .llvm_name = "Xtensa", .td_name = "Xtensa.td", }, }; pub fn main() anyerror!void { var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena_state.deinit(); const arena = arena_state.allocator(); const args = try std.process.argsAlloc(arena); if (args.len <= 1) { usageAndExit(std.io.getStdErr(), args[0], 1); } if (std.mem.eql(u8, args[1], "--help")) { usageAndExit(std.io.getStdOut(), args[0], 0); } if (args.len < 4) { usageAndExit(std.io.getStdErr(), args[0], 1); } const llvm_tblgen_exe = args[1]; if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) { usageAndExit(std.io.getStdErr(), args[0], 1); } const llvm_src_root = args[2]; if (std.mem.startsWith(u8, llvm_src_root, "-")) { usageAndExit(std.io.getStdErr(), args[0], 1); } const zig_src_root = args[3]; if (std.mem.startsWith(u8, zig_src_root, "-")) { usageAndExit(std.io.getStdErr(), args[0], 1); } var zig_src_dir = try fs.cwd().openDir(zig_src_root, .{}); defer zig_src_dir.close(); const root_progress = std.Progress.start(.{ .estimated_total_items = llvm_targets.len }); defer root_progress.end(); if (builtin.single_threaded) { for (llvm_targets) |llvm_target| { try processOneTarget(Job{ .llvm_tblgen_exe = llvm_tblgen_exe, .llvm_src_root = llvm_src_root, .zig_src_dir = zig_src_dir, .root_progress = root_progress, .llvm_target = llvm_target, }); } } else { var threads = try arena.alloc(std.Thread, llvm_targets.len); for (llvm_targets, 0..) |llvm_target, i| { const job = Job{ .llvm_tblgen_exe = llvm_tblgen_exe, .llvm_src_root = llvm_src_root, .zig_src_dir = zig_src_dir, .root_progress = root_progress, .llvm_target = llvm_target, }; threads[i] = try std.Thread.spawn(.{}, processOneTarget, .{job}); } for (threads) |thread| { thread.join(); } } } const Job = struct { llvm_tblgen_exe: []const u8, llvm_src_root: []const u8, zig_src_dir: std.fs.Dir, root_progress: std.Progress.Node, llvm_target: LlvmTarget, }; fn processOneTarget(job: Job) anyerror!void { const llvm_target = job.llvm_target; var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena_state.deinit(); const arena = arena_state.allocator(); const progress_node = job.root_progress.start(llvm_target.zig_name, 3); defer progress_node.end(); const tblgen_progress = progress_node.start("invoke llvm-tblgen", 0); const child_args = [_][]const u8{ job.llvm_tblgen_exe, "--dump-json", try std.fmt.allocPrint(arena, "{s}/llvm/lib/Target/{s}/{s}", .{ job.llvm_src_root, llvm_target.llvm_name, llvm_target.td_name, }), try std.fmt.allocPrint(arena, "-I={s}/llvm/include", .{job.llvm_src_root}), try std.fmt.allocPrint(arena, "-I={s}/llvm/lib/Target/{s}", .{ job.llvm_src_root, llvm_target.llvm_name, }), }; const child_result = try std.process.Child.run(.{ .allocator = arena, .argv = &child_args, .max_output_bytes = 500 * 1024 * 1024, }); tblgen_progress.end(); if (child_result.stderr.len != 0) { std.debug.print("{s}\n", .{child_result.stderr}); } const json_text = switch (child_result.term) { .Exited => |code| if (code == 0) child_result.stdout else { std.debug.print("llvm-tblgen exited with code {d}\n", .{code}); std.process.exit(1); }, else => { std.debug.print("llvm-tblgen crashed\n", .{}); std.process.exit(1); }, }; const json_parse_progress = progress_node.start("parse JSON", 0); const parsed = try json.parseFromSlice(json.Value, arena, json_text, .{}); defer parsed.deinit(); const root_map = &parsed.value.object; json_parse_progress.end(); const render_progress = progress_node.start("render zig code", 0); // So far LLVM has only had a few aliases for the same CPU. var cpu_aliases = std.StringHashMap(std.SegmentedList(struct { llvm: []const u8, zig: []const u8, }, 4)).init(arena); { var it = root_map.iterator(); while (it.next()) |kv| { if (kv.key_ptr.len == 0) continue; if (kv.key_ptr.*[0] == '!') continue; if (kv.value_ptr.* != .object) continue; if (hasSuperclass(&kv.value_ptr.object, "ProcessorAlias")) { // Note that `Name` is actually the alias, while `Alias` is the name that will have // a full `Processor` object defined. const llvm_alias = kv.value_ptr.object.get("Name").?.string; const llvm_name = kv.value_ptr.object.get("Alias").?.string; const gop = try cpu_aliases.getOrPut(try llvmNameToZigName(arena, llvm_name)); if (!gop.found_existing) gop.value_ptr.* = .{}; try gop.value_ptr.append(arena, .{ .llvm = llvm_alias, .zig = try llvmNameToZigName(arena, llvm_alias), }); } } } var features_table = std.StringHashMap(Feature).init(arena); var all_features = std.ArrayList(Feature).init(arena); var all_cpus = std.ArrayList(Cpu).init(arena); { var it = root_map.iterator(); while (it.next()) |kv| { if (kv.key_ptr.len == 0) continue; if (kv.key_ptr.*[0] == '!') continue; if (kv.value_ptr.* != .object) continue; if (hasSuperclass(&kv.value_ptr.object, "SubtargetFeature")) { const llvm_name = kv.value_ptr.object.get("Name").?.string; if (llvm_name.len == 0) continue; var zig_name = try llvmNameToZigName(arena, llvm_name); var desc = kv.value_ptr.object.get("Desc").?.string; var deps = std.ArrayList([]const u8).init(arena); var omit = false; var flatten = false; var omit_deps: []const []const u8 = &.{}; var extra_deps: []const []const u8 = &.{}; for (llvm_target.feature_overrides) |feature_override| { if (mem.eql(u8, llvm_name, feature_override.llvm_name)) { if (feature_override.omit) { // Still put the feature into the table so that we can // expand dependencies for the feature overrides marked `flatten`. omit = true; } if (feature_override.flatten) { flatten = true; } if (feature_override.zig_name) |override_name| { zig_name = override_name; } if (feature_override.desc) |override_desc| { desc = override_desc; } omit_deps = feature_override.omit_deps; extra_deps = feature_override.extra_deps; break; } } const implies = kv.value_ptr.object.get("Implies").?.array; for (implies.items) |imply| { const other_key = imply.object.get("def").?.string; const other_obj = &root_map.getPtr(other_key).?.object; const other_llvm_name = other_obj.get("Name").?.string; const other_zig_name = (try llvmFeatureNameToZigNameOmit( arena, llvm_target, other_llvm_name, )) orelse continue; for (omit_deps) |omit_dep| { if (mem.eql(u8, other_zig_name, omit_dep)) break; } else { try deps.append(other_zig_name); } } for (extra_deps) |extra_dep| { try deps.append(extra_dep); } const feature: Feature = .{ .llvm_name = llvm_name, .zig_name = zig_name, .desc = desc, .deps = deps.items, .flatten = flatten, }; try features_table.put(zig_name, feature); if (!omit and !flatten) { try all_features.append(feature); } } if (hasSuperclass(&kv.value_ptr.object, "Processor")) { const llvm_name = kv.value_ptr.object.get("Name").?.string; if (llvm_name.len == 0) continue; const omitted = for (llvm_target.omit_cpus) |omit_cpu_name| { if (mem.eql(u8, omit_cpu_name, llvm_name)) break true; } else false; if (omitted) continue; var zig_name = try llvmNameToZigName(arena, llvm_name); var deps = std.ArrayList([]const u8).init(arena); var omit_deps: []const []const u8 = &.{}; var extra_deps: []const []const u8 = &.{}; for (llvm_target.feature_overrides) |feature_override| { if (mem.eql(u8, llvm_name, feature_override.llvm_name)) { if (feature_override.omit) { continue; } if (feature_override.zig_name) |override_name| { zig_name = override_name; } omit_deps = feature_override.omit_deps; extra_deps = feature_override.extra_deps; break; } } const features = kv.value_ptr.object.get("Features").?.array; for (features.items) |feature| { const feature_key = feature.object.get("def").?.string; const feature_obj = &root_map.getPtr(feature_key).?.object; const feature_llvm_name = feature_obj.get("Name").?.string; if (feature_llvm_name.len == 0) continue; const feature_zig_name = (try llvmFeatureNameToZigNameOmit( arena, llvm_target, feature_llvm_name, )) orelse continue; for (omit_deps) |omit_dep| { if (mem.eql(u8, feature_zig_name, omit_dep)) break; } else { try deps.append(feature_zig_name); } } for (extra_deps) |extra_dep| { try deps.append(extra_dep); } const tune_features = kv.value_ptr.object.get("TuneFeatures").?.array; for (tune_features.items) |feature| { const feature_key = feature.object.get("def").?.string; const feature_obj = &root_map.getPtr(feature_key).?.object; const feature_llvm_name = feature_obj.get("Name").?.string; if (feature_llvm_name.len == 0) continue; const feature_zig_name = (try llvmFeatureNameToZigNameOmit( arena, llvm_target, feature_llvm_name, )) orelse continue; try deps.append(feature_zig_name); } for (llvm_target.feature_overrides) |feature_override| { if (mem.eql(u8, llvm_name, feature_override.llvm_name)) { if (feature_override.omit) { continue; } if (feature_override.zig_name) |override_name| { zig_name = override_name; } for (feature_override.extra_deps) |extra_dep| { try deps.append(extra_dep); } break; } } try all_cpus.append(.{ .llvm_name = llvm_name, .zig_name = zig_name, .features = deps.items, }); if (cpu_aliases.get(zig_name)) |aliases| { var alias_it = aliases.constIterator(0); alias_it: while (alias_it.next()) |alias| { for (llvm_target.omit_cpus) |omit_cpu_name| { if (mem.eql(u8, omit_cpu_name, alias.llvm)) continue :alias_it; } try all_cpus.append(.{ .llvm_name = alias.llvm, .zig_name = alias.zig, .features = deps.items, }); } } } } } for (llvm_target.extra_features) |extra_feature| { try features_table.put(extra_feature.zig_name, extra_feature); try all_features.append(extra_feature); } for (llvm_target.extra_cpus) |extra_cpu| { try all_cpus.append(extra_cpu); } mem.sort(Feature, all_features.items, {}, featureLessThan); mem.sort(Cpu, all_cpus.items, {}, cpuLessThan); const target_sub_path = try fs.path.join(arena, &.{ "lib", "std", "Target" }); var target_dir = try job.zig_src_dir.makeOpenPath(target_sub_path, .{}); defer target_dir.close(); const zig_code_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{llvm_target.zig_name}); if (all_features.items.len == 0) { // We represent this with an empty file. try target_dir.deleteTree(zig_code_basename); return; } var zig_code_file = try target_dir.createFile(zig_code_basename, .{}); defer zig_code_file.close(); var bw = std.io.bufferedWriter(zig_code_file.writer()); const w = bw.writer(); try w.writeAll( \\//! This file is auto-generated by tools/update_cpu_features.zig. \\ \\const std = @import("../std.zig"); \\const CpuFeature = std.Target.Cpu.Feature; \\const CpuModel = std.Target.Cpu.Model; \\ \\pub const Feature = enum { \\ ); for (all_features.items) |feature| { try w.print(" {p},\n", .{std.zig.fmtId(feature.zig_name)}); } try w.writeAll( \\}; \\ \\pub const featureSet = CpuFeature.FeatureSetFns(Feature).featureSet; \\pub const featureSetHas = CpuFeature.FeatureSetFns(Feature).featureSetHas; \\pub const featureSetHasAny = CpuFeature.FeatureSetFns(Feature).featureSetHasAny; \\pub const featureSetHasAll = CpuFeature.FeatureSetFns(Feature).featureSetHasAll; \\ \\pub const all_features = blk: { \\ ); if (llvm_target.branch_quota) |branch_quota| { try w.print(" @setEvalBranchQuota({d});\n", .{branch_quota}); } try w.writeAll( \\ const len = @typeInfo(Feature).@"enum".fields.len; \\ std.debug.assert(len <= CpuFeature.Set.needed_bit_count); \\ var result: [len]CpuFeature = undefined; \\ ); for (all_features.items) |feature| { if (feature.llvm_name) |llvm_name| { try w.print( \\ result[@intFromEnum(Feature.{p_})] = .{{ \\ .llvm_name = "{}", \\ .description = "{}", \\ .dependencies = featureSet(&[_]Feature{{ , .{ std.zig.fmtId(feature.zig_name), std.zig.fmtEscapes(llvm_name), std.zig.fmtEscapes(feature.desc), }, ); } else { try w.print( \\ result[@intFromEnum(Feature.{p_})] = .{{ \\ .llvm_name = null, \\ .description = "{}", \\ .dependencies = featureSet(&[_]Feature{{ , .{ std.zig.fmtId(feature.zig_name), std.zig.fmtEscapes(feature.desc), }, ); } var deps_set = std.StringHashMap(void).init(arena); for (feature.deps) |dep| { try putDep(&deps_set, features_table, dep); } try pruneFeatures(arena, features_table, &deps_set); var dependencies = std.ArrayList([]const u8).init(arena); { var it = deps_set.keyIterator(); while (it.next()) |key| { try dependencies.append(key.*); } } mem.sort([]const u8, dependencies.items, {}, asciiLessThan); if (dependencies.items.len == 0) { try w.writeAll( \\}), \\ }; \\ ); } else { try w.writeAll("\n"); for (dependencies.items) |dep| { try w.print(" .{p_},\n", .{std.zig.fmtId(dep)}); } try w.writeAll( \\ }), \\ }; \\ ); } } try w.writeAll( \\ const ti = @typeInfo(Feature); \\ for (&result, 0..) |*elem, i| { \\ elem.index = i; \\ elem.name = ti.@"enum".fields[i].name; \\ } \\ break :blk result; \\}; \\ \\pub const cpu = struct { \\ ); for (all_cpus.items) |cpu| { var deps_set = std.StringHashMap(void).init(arena); for (cpu.features) |feature_zig_name| { try putDep(&deps_set, features_table, feature_zig_name); } try pruneFeatures(arena, features_table, &deps_set); var cpu_features = std.ArrayList([]const u8).init(arena); { var it = deps_set.keyIterator(); while (it.next()) |key| { try cpu_features.append(key.*); } } mem.sort([]const u8, cpu_features.items, {}, asciiLessThan); if (cpu.llvm_name) |llvm_name| { try w.print( \\ pub const {} = CpuModel{{ \\ .name = "{}", \\ .llvm_name = "{}", \\ .features = featureSet(&[_]Feature{{ , .{ std.zig.fmtId(cpu.zig_name), std.zig.fmtEscapes(cpu.zig_name), std.zig.fmtEscapes(llvm_name), }); } else { try w.print( \\ pub const {} = CpuModel{{ \\ .name = "{}", \\ .llvm_name = null, \\ .features = featureSet(&[_]Feature{{ , .{ std.zig.fmtId(cpu.zig_name), std.zig.fmtEscapes(cpu.zig_name), }); } if (cpu_features.items.len == 0) { try w.writeAll( \\}), \\ }; \\ ); } else { try w.writeAll("\n"); for (cpu_features.items) |feature_zig_name| { try w.print(" .{p_},\n", .{std.zig.fmtId(feature_zig_name)}); } try w.writeAll( \\ }), \\ }; \\ ); } } try w.writeAll( \\}; \\ ); try bw.flush(); render_progress.end(); } fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn { file.writer().print( \\Usage: {s} /path/to/llvm-tblgen /path/git/llvm-project /path/git/zig \\ \\Updates lib/std/target/.zig from llvm/lib/Target//.td . \\ \\On a less beefy system, or when debugging, compile with -fsingle-threaded. \\ , .{arg0}) catch std.process.exit(1); std.process.exit(code); } fn featureLessThan(context: void, a: Feature, b: Feature) bool { _ = context; return std.ascii.lessThanIgnoreCase(a.zig_name, b.zig_name); } fn cpuLessThan(context: void, a: Cpu, b: Cpu) bool { _ = context; return std.ascii.lessThanIgnoreCase(a.zig_name, b.zig_name); } fn asciiLessThan(context: void, a: []const u8, b: []const u8) bool { _ = context; return std.ascii.lessThanIgnoreCase(a, b); } fn llvmNameToZigName(arena: mem.Allocator, llvm_name: []const u8) ![]const u8 { const duped = try arena.dupe(u8, llvm_name); for (duped) |*byte| switch (byte.*) { '-', '.' => byte.* = '_', else => continue, }; return duped; } fn llvmFeatureNameToZigNameOmit( arena: mem.Allocator, llvm_target: LlvmTarget, llvm_name: []const u8, ) !?[]const u8 { for (llvm_target.feature_overrides) |feature_override| { if (mem.eql(u8, feature_override.llvm_name, llvm_name)) { if (feature_override.omit) return null; return feature_override.zig_name orelse break; } } return try llvmNameToZigName(arena, llvm_name); } fn hasSuperclass(obj: *json.ObjectMap, class_name: []const u8) bool { const superclasses_json = obj.get("!superclasses") orelse return false; for (superclasses_json.array.items) |superclass_json| { const superclass = superclass_json.string; if (std.mem.eql(u8, superclass, class_name)) { return true; } } return false; } fn pruneFeatures( arena: mem.Allocator, features_table: std.StringHashMap(Feature), deps_set: *std.StringHashMap(void), ) !void { // For each element, recursively iterate over the dependencies and add // everything we find to a "deletion set". // Then, iterate over the deletion set and delete all that stuff from `deps_set`. var deletion_set = std.StringHashMap(void).init(arena); { var it = deps_set.keyIterator(); while (it.next()) |key| { const feature = features_table.get(key.*).?; try walkFeatures(features_table, &deletion_set, feature); } } { var it = deletion_set.keyIterator(); while (it.next()) |key| { _ = deps_set.remove(key.*); } } } fn walkFeatures( features_table: std.StringHashMap(Feature), deletion_set: *std.StringHashMap(void), feature: Feature, ) error{OutOfMemory}!void { for (feature.deps) |dep| { try deletion_set.put(dep, {}); const other_feature = features_table.get(dep).?; try walkFeatures(features_table, deletion_set, other_feature); } } fn putDep( deps_set: *std.StringHashMap(void), features_table: std.StringHashMap(Feature), zig_feature_name: []const u8, ) error{OutOfMemory}!void { const feature = features_table.get(zig_feature_name).?; if (feature.flatten) { for (feature.deps) |dep| { try putDep(deps_set, features_table, dep); } } else { try deps_set.put(zig_feature_name, {}); } }