Commit 5248f0a909

Alex Rønne Petersen <alex@alexrp.com>
2024-12-17 15:23:50
update_cpu_features: Handle targets that don't have LLVM data.
1 parent b541a7a
Changed files (1)
tools/update_cpu_features.zig
@@ -34,10 +34,12 @@ const Feature = struct {
     flatten: bool = false,
 };
 
-const LlvmTarget = struct {
+const ArchTarget = struct {
     zig_name: []const u8,
-    llvm_name: []const u8,
-    td_name: []const u8,
+    llvm: ?struct {
+        name: []const u8,
+        td_name: []const u8,
+    },
     feature_overrides: []const FeatureOverride = &.{},
     extra_cpus: []const Cpu = &.{},
     extra_features: []const Feature = &.{},
@@ -45,11 +47,13 @@ const LlvmTarget = struct {
     branch_quota: ?usize = null,
 };
 
-const llvm_targets = [_]LlvmTarget{
+const targets = [_]ArchTarget{
     .{
         .zig_name = "aarch64",
-        .llvm_name = "AArch64",
-        .td_name = "AArch64.td",
+        .llvm = .{
+            .name = "AArch64",
+            .td_name = "AArch64",
+        },
         .branch_quota = 2000,
         .feature_overrides = &.{
             .{
@@ -391,8 +395,10 @@ const llvm_targets = [_]LlvmTarget{
     },
     .{
         .zig_name = "amdgcn",
-        .llvm_name = "AMDGPU",
-        .td_name = "AMDGPU.td",
+        .llvm = .{
+            .name = "AMDGPU",
+            .td_name = "AMDGPU",
+        },
         .feature_overrides = &.{
             .{
                 .llvm_name = "DumpCode",
@@ -418,13 +424,17 @@ const llvm_targets = [_]LlvmTarget{
     },
     .{
         .zig_name = "arc",
-        .llvm_name = "ARC",
-        .td_name = "ARC.td",
+        .llvm = .{
+            .name = "ARC",
+            .td_name = "ARC",
+        },
     },
     .{
         .zig_name = "arm",
-        .llvm_name = "ARM",
-        .td_name = "ARM.td",
+        .llvm = .{
+            .name = "ARM",
+            .td_name = "ARM",
+        },
         .branch_quota = 10000,
         .feature_overrides = &.{
             .{
@@ -928,66 +938,90 @@ const llvm_targets = [_]LlvmTarget{
     },
     .{
         .zig_name = "avr",
-        .llvm_name = "AVR",
-        .td_name = "AVR.td",
+        .llvm = .{
+            .name = "AVR",
+            .td_name = "AVR",
+        },
     },
     .{
         .zig_name = "bpf",
-        .llvm_name = "BPF",
-        .td_name = "BPF.td",
+        .llvm = .{
+            .name = "BPF",
+            .td_name = "BPF",
+        },
     },
     .{
         .zig_name = "csky",
-        .llvm_name = "CSKY",
-        .td_name = "CSKY.td",
+        .llvm = .{
+            .name = "CSKY",
+            .td_name = "CSKY",
+        },
     },
     .{
         .zig_name = "hexagon",
-        .llvm_name = "Hexagon",
-        .td_name = "Hexagon.td",
+        .llvm = .{
+            .name = "Hexagon",
+            .td_name = "Hexagon",
+        },
     },
     .{
         .zig_name = "lanai",
-        .llvm_name = "Lanai",
-        .td_name = "Lanai.td",
+        .llvm = .{
+            .name = "Lanai",
+            .td_name = "Lanai",
+        },
     },
     .{
         .zig_name = "loongarch",
-        .llvm_name = "LoongArch",
-        .td_name = "LoongArch.td",
+        .llvm = .{
+            .name = "LoongArch",
+            .td_name = "LoongArch",
+        },
     },
     .{
         .zig_name = "m68k",
-        .llvm_name = "M68k",
-        .td_name = "M68k.td",
+        .llvm = .{
+            .name = "M68k",
+            .td_name = "M68k",
+        },
     },
     .{
         .zig_name = "msp430",
-        .llvm_name = "MSP430",
-        .td_name = "MSP430.td",
+        .llvm = .{
+            .name = "MSP430",
+            .td_name = "MSP430",
+        },
     },
     .{
         .zig_name = "mips",
-        .llvm_name = "Mips",
-        .td_name = "Mips.td",
+        .llvm = .{
+            .name = "Mips",
+            .td_name = "Mips",
+        },
     },
     .{
         .zig_name = "nvptx",
-        .llvm_name = "NVPTX",
-        .td_name = "NVPTX.td",
+        .llvm = .{
+            .name = "NVPTX",
+            .td_name = "NVPTX",
+        },
     },
     .{
         .zig_name = "powerpc",
-        .llvm_name = "PowerPC",
-        .td_name = "PPC.td",
+        .llvm = .{
+            .name = "PowerPC",
+            .td_name = "PPC",
+        },
         .omit_cpus = &.{
             "ppc32",
         },
     },
     .{
         .zig_name = "riscv",
-        .llvm_name = "RISCV",
-        .td_name = "RISCV.td",
+        .llvm = .{
+            .name = "RISCV",
+            .td_name = "RISCV",
+        },
         .branch_quota = 2000,
         .feature_overrides = &.{
             .{
@@ -1010,29 +1044,38 @@ const llvm_targets = [_]LlvmTarget{
     },
     .{
         .zig_name = "sparc",
-        .llvm_name = "Sparc",
-        .td_name = "Sparc.td",
+        .llvm = .{
+            .name = "Sparc",
+            .td_name = "Sparc",
+        },
     },
     // TODO: merge tools/update_spirv_features.zig into this script
     //.{
     //    .zig_name = "spirv",
-    //    .llvm_name = "SPIRV",
-    //    .td_name = "SPIRV.td",
+    //    .llvm = .{
+    //        .name = "SPIRV",
+    //    },
     //},
     .{
         .zig_name = "s390x",
-        .llvm_name = "SystemZ",
-        .td_name = "SystemZ.td",
+        .llvm = .{
+            .name = "SystemZ",
+            .td_name = "SystemZ",
+        },
     },
     .{
         .zig_name = "ve",
-        .llvm_name = "VE",
-        .td_name = "VE.td",
+        .llvm = .{
+            .name = "VE",
+            .td_name = "VE",
+        },
     },
     .{
         .zig_name = "wasm",
-        .llvm_name = "WebAssembly",
-        .td_name = "WebAssembly.td",
+        .llvm = .{
+            .name = "WebAssembly",
+            .td_name = "WebAssembly",
+        },
         .extra_features = &.{
             .{
                 .zig_name = "nontrapping_bulk_memory_len0",
@@ -1057,8 +1100,10 @@ const llvm_targets = [_]LlvmTarget{
     },
     .{
         .zig_name = "x86",
-        .llvm_name = "X86",
-        .td_name = "X86.td",
+        .llvm = .{
+            .name = "X86",
+            .td_name = "X86",
+        },
         .feature_overrides = &.{
             .{
                 .llvm_name = "64bit-mode",
@@ -1362,13 +1407,17 @@ const llvm_targets = [_]LlvmTarget{
     },
     .{
         .zig_name = "xcore",
-        .llvm_name = "XCore",
-        .td_name = "XCore.td",
+        .llvm = .{
+            .name = "XCore",
+            .td_name = "XCore",
+        },
     },
     .{
         .zig_name = "xtensa",
-        .llvm_name = "Xtensa",
-        .td_name = "Xtensa.td",
+        .llvm = .{
+            .name = "Xtensa",
+            .td_name = "Xtensa",
+        },
     },
 };
 
@@ -1413,33 +1462,33 @@ pub fn main() anyerror!void {
     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 });
+    const root_progress = std.Progress.start(.{ .estimated_total_items = targets.len });
     defer root_progress.end();
 
     if (builtin.single_threaded) {
-        for (llvm_targets) |llvm_target| {
-            if (filter) |zig_name| if (!std.mem.eql(u8, llvm_target.zig_name, zig_name)) continue;
+        for (targets) |target| {
+            if (filter) |zig_name| if (!std.mem.eql(u8, target.zig_name, zig_name)) continue;
             try processOneTarget(.{
                 .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,
+                .target = target,
             });
         }
     } else {
         var pool: std.Thread.Pool = undefined;
-        try pool.init(.{ .allocator = arena, .n_jobs = llvm_targets.len });
+        try pool.init(.{ .allocator = arena, .n_jobs = targets.len });
         defer pool.deinit();
 
-        for (llvm_targets) |llvm_target| {
-            if (filter) |zig_name| if (!std.mem.eql(u8, llvm_target.zig_name, zig_name)) continue;
+        for (targets) |target| {
+            if (filter) |zig_name| if (!std.mem.eql(u8, target.zig_name, zig_name)) continue;
             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,
+                .target = target,
             };
             try pool.spawn(processOneTarget, .{job});
         }
@@ -1451,162 +1500,147 @@ const Job = struct {
     llvm_src_root: []const u8,
     zig_src_dir: std.fs.Dir,
     root_progress: std.Progress.Node,
-    llvm_target: LlvmTarget,
+    target: ArchTarget,
 };
 
 fn processOneTarget(job: Job) void {
     errdefer |err| std.debug.panic("panic: {s}", .{@errorName(err)});
-    const llvm_target = job.llvm_target;
+    const target = job.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);
+    const progress_node = job.root_progress.start(target.zig_name, 3);
     defer progress_node.end();
 
-    const tblgen_progress = progress_node.start("running 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,
-        }),
-    };
+    var features_table = std.StringHashMap(Feature).init(arena);
+    var all_features = std.ArrayList(Feature).init(arena);
+    var all_cpus = std.ArrayList(Cpu).init(arena);
 
-    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});
-    }
+    if (target.llvm) |llvm| {
+        const tblgen_progress = progress_node.start("running llvm-tblgen", 0);
 
-    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 child_args = [_][]const u8{
+            job.llvm_tblgen_exe,
+            "--dump-json",
+            try std.fmt.allocPrint(arena, "{s}/llvm/lib/Target/{s}/{s}.td", .{
+                job.llvm_src_root,
+                llvm.name,
+                llvm.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.name,
+            }),
+        };
 
-    const json_parse_progress = progress_node.start("parsing JSON", 0);
+        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 parsed = try json.parseFromSlice(json.Value, arena, json_text, .{});
-    defer parsed.deinit();
-    const root_map = &parsed.value.object;
-    json_parse_progress.end();
+        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 render_progress = progress_node.start("rendering Zig code", 0);
+        const json_parse_progress = progress_node.start("parsing JSON", 0);
 
-    // So far, LLVM only has a few aliases for the same CPU.
-    var cpu_aliases = std.StringHashMap(std.SegmentedList(struct {
-        llvm: []const u8,
-        zig: []const u8,
-    }, 4)).init(arena);
+        const parsed = try json.parseFromSlice(json.Value, arena, json_text, .{});
+        defer parsed.deinit();
+        const root_map = &parsed.value.object;
+        json_parse_progress.end();
 
-    {
-        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),
-                });
+        const collate_progress = progress_node.start("collating LLVM data", 0);
+
+        // So far, LLVM only has 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;
+        {
+            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 (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;
                         }
-                        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.get(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);
                     }
-                }
-                // This is used by AArch64.
-                if (kv.value_ptr.object.get("DefaultExts")) |exts_val| {
-                    for (exts_val.array.items) |ext| {
-                        const other_key = ext.object.get("def").?.string;
+                    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.get(other_key).?.object;
                         const other_llvm_name = other_obj.get("Name").?.string;
                         const other_zig_name = (try llvmFeatureNameToZigNameOmit(
                             arena,
-                            llvm_target,
+                            target,
                             other_llvm_name,
                         )) orelse continue;
                         for (omit_deps) |omit_dep| {
@@ -1615,118 +1649,141 @@ fn processOneTarget(job: Job) void {
                             try deps.append(other_zig_name);
                         }
                     }
+                    // This is used by AArch64.
+                    if (kv.value_ptr.object.get("DefaultExts")) |exts_val| {
+                        for (exts_val.array.items) |ext| {
+                            const other_key = ext.object.get("def").?.string;
+                            const other_obj = root_map.get(other_key).?.object;
+                            const other_llvm_name = other_obj.get("Name").?.string;
+                            const other_zig_name = (try llvmFeatureNameToZigNameOmit(
+                                arena,
+                                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);
+                    }
                 }
-                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 (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 (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 (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;
                         }
-                        if (feature_override.zig_name) |override_name| {
-                            zig_name = override_name;
+                    }
+                    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.get(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,
+                            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);
                         }
-                        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.get(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 {
+                    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.get(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,
+                            target,
+                            feature_llvm_name,
+                        )) orelse continue;
                         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.get(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);
-                }
-                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 = 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 (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,
-                        });
+                            try all_cpus.append(.{
+                                .llvm_name = alias.llvm,
+                                .zig_name = alias.zig,
+                                .features = deps.items,
+                            });
+                        }
                     }
                 }
             }
         }
+
+        collate_progress.end();
     }
-    for (llvm_target.extra_features) |extra_feature| {
+
+    for (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| {
+    for (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 render_progress = progress_node.start("rendering Zig code", 0);
+
     var target_dir = try job.zig_src_dir.openDir("lib/std/Target", .{});
     defer target_dir.close();
 
-    const zig_code_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{llvm_target.zig_name});
+    const zig_code_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{target.zig_name});
     var zig_code_file = try target_dir.createFile(zig_code_basename, .{});
     defer zig_code_file.close();
 
@@ -1760,7 +1817,7 @@ fn processOneTarget(job: Job) void {
         \\pub const all_features = blk: {
         \\
     );
-    if (llvm_target.branch_quota) |branch_quota| {
+    if (target.branch_quota) |branch_quota| {
         try w.print("    @setEvalBranchQuota({d});\n", .{branch_quota});
     }
     try w.writeAll(
@@ -1941,10 +1998,10 @@ fn llvmNameToZigName(arena: mem.Allocator, llvm_name: []const u8) ![]const u8 {
 
 fn llvmFeatureNameToZigNameOmit(
     arena: mem.Allocator,
-    llvm_target: LlvmTarget,
+    target: ArchTarget,
     llvm_name: []const u8,
 ) !?[]const u8 {
-    for (llvm_target.feature_overrides) |feature_override| {
+    for (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;