Commit f54471b54c

Andrew Kelley <andrew@ziglang.org>
2023-12-15 00:41:20
compiler: miscellaneous branch progress
implement builtin.zig file population for all modules rather than assuming there is only one global builtin.zig module. move some fields from link.File to Compilation move some fields from Module to Compilation compute debug_format in global Compilation config resolution wire up C compilation to the concept of owner modules make whole cache mode call link.File.createEmpty() instead of link.File.open()
1 parent 769dea6
src/codegen/llvm.zig
@@ -854,9 +854,8 @@ pub const Object = struct {
     /// want to iterate over it while adding entries to it.
     pub const DITypeMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, AnnotatedDITypePtr);
 
-    pub fn create(arena: Allocator, options: link.File.OpenOptions) !*Object {
+    pub fn create(arena: Allocator, comp: *Compilation) !*Object {
         if (build_options.only_c) unreachable;
-        const comp = options.comp;
         const gpa = comp.gpa;
         const target = comp.root_mod.resolved_target.result;
         const llvm_target_triple = try targetTriple(arena, target);
@@ -878,14 +877,7 @@ pub const Object = struct {
         var target_data: if (build_options.have_llvm) *llvm.TargetData else void = undefined;
         if (builder.useLibLlvm()) {
             debug_info: {
-                const debug_format = options.debug_format orelse b: {
-                    if (strip) break :b .strip;
-                    break :b switch (target.ofmt) {
-                        .coff => .code_view,
-                        else => .{ .dwarf = .@"32" },
-                    };
-                };
-                switch (debug_format) {
+                switch (comp.config.debug_format) {
                     .strip => break :debug_info,
                     .code_view => builder.llvm.module.?.addModuleCodeViewFlag(),
                     .dwarf => |f| builder.llvm.module.?.addModuleDebugInfoFlag(f == .@"64"),
@@ -961,8 +953,8 @@ pub const Object = struct {
                 opt_level,
                 reloc_mode,
                 code_model,
-                options.function_sections orelse false,
-                options.data_sections orelse false,
+                comp.function_sections,
+                comp.data_sections,
                 float_abi,
                 if (target_util.llvmMachineAbi(target)) |s| s.ptr else null,
             );
src/Compilation/Config.zig
@@ -33,9 +33,16 @@ shared_memory: bool,
 is_test: bool,
 test_evented_io: bool,
 entry: ?[]const u8,
+debug_format: DebugFormat,
 
 pub const CFrontend = enum { clang, aro };
 
+pub const DebugFormat = union(enum) {
+    strip,
+    dwarf: std.dwarf.Format,
+    code_view,
+};
+
 pub const Options = struct {
     output_mode: std.builtin.OutputMode,
     resolved_target: Module.ResolvedTarget,
@@ -43,6 +50,7 @@ pub const Options = struct {
     have_zcu: bool,
     emit_bin: bool,
     root_optimize_mode: ?std.builtin.OptimizeMode = null,
+    root_strip: ?bool = null,
     link_mode: ?std.builtin.LinkMode = null,
     ensure_libc_on_non_freestanding: bool = false,
     ensure_libcpp_on_non_freestanding: bool = false,
@@ -51,6 +59,7 @@ pub const Options = struct {
     any_unwind_tables: bool = false,
     any_dyn_libs: bool = false,
     any_c_source_files: bool = false,
+    any_non_stripped: bool = false,
     emit_llvm_ir: bool = false,
     emit_llvm_bc: bool = false,
     link_libc: ?bool = null,
@@ -74,6 +83,7 @@ pub const Options = struct {
     export_memory: ?bool = null,
     shared_memory: ?bool = null,
     test_evented_io: bool = false,
+    debug_format: ?Config.DebugFormat = null,
 };
 
 pub fn resolve(options: Options) !Config {
@@ -365,6 +375,26 @@ pub fn resolve(options: Options) !Config {
         break :b false;
     };
 
+    const root_strip = b: {
+        if (options.root_strip) |x| break :b x;
+        if (root_optimize_mode == .ReleaseSmall) break :b true;
+        if (!target_util.hasDebugInfo(target)) break :b true;
+        break :b false;
+    };
+
+    const debug_format: DebugFormat = b: {
+        if (root_strip and !options.any_non_stripped) break :b .strip;
+        break :b switch (target.ofmt) {
+            .elf, .macho, .wasm => .{ .dwarf = .@"32" },
+            .coff => .code_view,
+            .c => switch (target.os.tag) {
+                .windows, .uefi => .code_view,
+                else => .{ .dwarf = .@"32" },
+            },
+            .spirv, .nvptx, .dxcontainer, .hex, .raw, .plan9 => .strip,
+        };
+    };
+
     return .{
         .output_mode = options.output_mode,
         .have_zcu = options.have_zcu,
@@ -388,6 +418,7 @@ pub fn resolve(options: Options) !Config {
         .use_lld = use_lld,
         .entry = entry,
         .wasi_exec_model = wasi_exec_model,
+        .debug_format = debug_format,
     };
 }
 
src/link/Coff/lld.zig
@@ -171,7 +171,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
 
         try argv.append("-ERRORLIMIT:0");
         try argv.append("-NOLOGO");
-        if (self.base.debug_format != .strip) {
+        if (comp.config.debug_format != .strip) {
             try argv.append("-DEBUG");
 
             const out_ext = std.fs.path.extension(full_out_path);
src/link/Elf/Object.zig
@@ -293,13 +293,14 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem
 }
 
 fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool {
+    const comp = elf_file.base.comp;
     const shdr = self.shdrs.items[index];
     const name = self.getString(shdr.sh_name);
     const ignore = blk: {
         if (mem.startsWith(u8, name, ".note")) break :blk true;
         if (mem.startsWith(u8, name, ".comment")) break :blk true;
         if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true;
-        if (elf_file.base.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
+        if (comp.config.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and
             mem.startsWith(u8, name, ".debug")) break :blk true;
         break :blk false;
     };
src/link/Elf/ZigObject.zig
@@ -76,7 +76,8 @@ pub const symbol_mask: u32 = 0x7fffffff;
 pub const SHN_ATOM: u16 = 0x100;
 
 pub fn init(self: *ZigObject, elf_file: *Elf) !void {
-    const gpa = elf_file.base.comp.gpa;
+    const comp = elf_file.base.comp;
+    const gpa = comp.gpa;
 
     try self.atoms.append(gpa, 0); // null input section
     try self.relocs.append(gpa, .{}); // null relocs section
@@ -96,8 +97,13 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
     esym.st_shndx = elf.SHN_ABS;
     symbol_ptr.esym_index = esym_index;
 
-    if (elf_file.base.debug_format != .strip) {
-        self.dwarf = Dwarf.init(&elf_file.base, .dwarf32);
+    switch (comp.config.debug_format) {
+        .strip => {},
+        .dwarf => |v| {
+            assert(v == .@"32");
+            self.dwarf = Dwarf.init(&elf_file.base, .dwarf32);
+        },
+        .code_view => unreachable,
     }
 }
 
src/link/C.zig
@@ -92,21 +92,24 @@ pub fn addString(this: *C, s: []const u8) Allocator.Error!String {
     };
 }
 
-pub fn open(arena: Allocator, options: link.File.OpenOptions) !*C {
-    const target = options.comp.root_mod.resolved_target.result;
+pub fn open(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*C {
+    const target = comp.root_mod.resolved_target.result;
     assert(target.ofmt == .c);
-    const optimize_mode = options.comp.root_mod.optimize_mode;
-    const use_lld = build_options.have_llvm and options.comp.config.use_lld;
-    const use_llvm = options.comp.config.use_llvm;
-    const output_mode = options.comp.config.output_mode;
-    const link_mode = options.comp.config.link_mode;
+    const optimize_mode = comp.root_mod.optimize_mode;
+    const use_lld = build_options.have_llvm and comp.config.use_lld;
+    const use_llvm = comp.config.use_llvm;
+    const output_mode = comp.config.output_mode;
+    const link_mode = comp.config.link_mode;
 
     // These are caught by `Compilation.Config.resolve`.
     assert(!use_lld);
     assert(!use_llvm);
 
-    const emit = options.emit;
-
     const file = try emit.directory.handle.createFile(emit.sub_path, .{
         // Truncation is done on `flush`.
         .truncate = false,
@@ -119,7 +122,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*C {
     c_file.* = .{
         .base = .{
             .tag = .c,
-            .comp = options.comp,
+            .comp = comp,
             .emit = emit,
             .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
             .stack_size = options.stack_size orelse 16777216,
@@ -129,9 +132,6 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*C {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
-            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
         },
     };
 
src/link/Coff.zig
@@ -234,44 +234,49 @@ const ideal_factor = 3;
 const minimum_text_block_size = 64;
 pub const min_text_capacity = padToIdeal(minimum_text_block_size);
 
-pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Coff {
+pub fn open(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*Coff {
     if (build_options.only_c) unreachable;
-    const target = options.comp.root_mod.resolved_target.result;
+    const target = comp.root_mod.resolved_target.result;
     assert(target.ofmt == .coff);
 
-    const self = try createEmpty(arena, options);
+    const self = try createEmpty(arena, comp, emit, options);
     errdefer self.base.destroy();
 
-    const use_lld = build_options.have_llvm and options.comp.config.use_lld;
-    const use_llvm = options.comp.config.use_llvm;
+    const use_lld = build_options.have_llvm and comp.config.use_lld;
+    const use_llvm = comp.config.use_llvm;
 
     if (use_lld and use_llvm) {
         // LLVM emits the object file; LLD links it into the final product.
         return self;
     }
 
-    const sub_path = if (!use_lld) options.emit.sub_path else p: {
+    const sub_path = if (!use_lld) emit.sub_path else p: {
         // Open a temporary object file, not the final output file because we
         // want to link with LLD.
         const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
-            options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
+            emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
         });
         self.base.intermediary_basename = o_file_path;
         break :p o_file_path;
     };
 
-    self.base.file = try options.emit.directory.handle.createFile(sub_path, .{
+    self.base.file = try emit.directory.handle.createFile(sub_path, .{
         .truncate = false,
         .read = true,
         .mode = link.File.determineMode(
             use_lld,
-            options.comp.config.output_mode,
-            options.comp.config.link_mode,
+            comp.config.output_mode,
+            comp.config.link_mode,
         ),
     });
 
     assert(self.llvm_object == null);
-    const gpa = self.base.comp.gpa;
+    const gpa = comp.gpa;
 
     try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32));
     self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32));
@@ -362,8 +367,12 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Coff {
     return self;
 }
 
-pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff {
-    const comp = options.comp;
+pub fn createEmpty(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*Coff {
     const target = comp.root_mod.resolved_target.result;
     const optimize_mode = comp.root_mod.optimize_mode;
     const output_mode = comp.config.output_mode;
@@ -380,7 +389,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff {
         .base = .{
             .tag = .coff,
             .comp = comp,
-            .emit = options.emit,
+            .emit = emit,
             .stack_size = options.stack_size orelse 16777216,
             .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
             .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
@@ -389,9 +398,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
-            .debug_format = options.debug_format orelse .code_view,
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
         },
         .ptr_width = ptr_width,
         .page_size = page_size,
@@ -423,7 +429,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Coff {
 
     const use_llvm = comp.config.use_llvm;
     if (use_llvm and comp.config.have_zcu) {
-        self.llvm_object = try LlvmObject.create(arena, options);
+        self.llvm_object = try LlvmObject.create(arena, comp);
     }
     return self;
 }
src/link/Elf.zig
@@ -228,18 +228,23 @@ pub const HashStyle = enum { sysv, gnu, both };
 pub const CompressDebugSections = enum { none, zlib, zstd };
 pub const SortSection = enum { name, alignment };
 
-pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Elf {
+pub fn open(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*Elf {
     if (build_options.only_c) unreachable;
-    const target = options.comp.root_mod.resolved_target.result;
+    const target = comp.root_mod.resolved_target.result;
     assert(target.ofmt == .elf);
 
-    const use_lld = build_options.have_llvm and options.comp.config.use_lld;
-    const use_llvm = options.comp.config.use_llvm;
-    const opt_zcu = options.comp.module;
-    const output_mode = options.comp.config.output_mode;
-    const link_mode = options.comp.config.link_mode;
+    const use_lld = build_options.have_llvm and comp.config.use_lld;
+    const use_llvm = comp.config.use_llvm;
+    const opt_zcu = comp.module;
+    const output_mode = comp.config.output_mode;
+    const link_mode = comp.config.link_mode;
 
-    const self = try createEmpty(arena, options);
+    const self = try createEmpty(arena, comp, emit, options);
     errdefer self.base.destroy();
 
     if (use_lld and use_llvm) {
@@ -250,23 +255,23 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Elf {
     const is_obj = output_mode == .Obj;
     const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .Static);
 
-    const sub_path = if (!use_lld) options.emit.sub_path else p: {
+    const sub_path = if (!use_lld) emit.sub_path else p: {
         // Open a temporary object file, not the final output file because we
         // want to link with LLD.
         const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
-            options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
+            emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
         });
         self.base.intermediary_basename = o_file_path;
         break :p o_file_path;
     };
 
-    self.base.file = try options.emit.directory.handle.createFile(sub_path, .{
+    self.base.file = try emit.directory.handle.createFile(sub_path, .{
         .truncate = false,
         .read = true,
         .mode = link.File.determineMode(use_lld, output_mode, link_mode),
     });
 
-    const gpa = options.comp.gpa;
+    const gpa = comp.gpa;
 
     // Index 0 is always a null symbol.
     try self.symbols.append(gpa, .{});
@@ -343,8 +348,12 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Elf {
     return self;
 }
 
-pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf {
-    const comp = options.comp;
+pub fn createEmpty(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*Elf {
     const use_llvm = comp.config.use_llvm;
     const optimize_mode = comp.root_mod.optimize_mode;
     const target = comp.root_mod.resolved_target.result;
@@ -373,7 +382,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf {
         .base = .{
             .tag = .elf,
             .comp = comp,
-            .emit = options.emit,
+            .emit = emit,
             .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
             .stack_size = options.stack_size orelse 16777216,
             .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os,
@@ -382,9 +391,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
-            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
         },
         .ptr_width = ptr_width,
         .page_size = page_size,
@@ -423,7 +429,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Elf {
         .version_script = options.version_script,
     };
     if (use_llvm and comp.config.have_zcu) {
-        self.llvm_object = try LlvmObject.create(arena, options);
+        self.llvm_object = try LlvmObject.create(arena, comp);
     }
 
     return self;
@@ -1753,7 +1759,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
             try argv.append("-pie");
         }
 
-        if (self.base.debug_format == .strip) {
+        if (comp.config.debug_format == .strip) {
             try argv.append("-s");
         }
 
@@ -2640,7 +2646,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
             try argv.append("--export-dynamic");
         }
 
-        if (self.base.debug_format == .strip) {
+        if (comp.config.debug_format == .strip) {
             try argv.append("-s");
         }
 
src/link/MachO.zig
@@ -182,18 +182,21 @@ pub const SdkLayout = enum {
     vendored,
 };
 
-pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO {
+pub fn open(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*MachO {
     if (build_options.only_c) unreachable;
-    const comp = options.comp;
     const target = comp.root_mod.resolved_target.result;
     const use_lld = build_options.have_llvm and comp.config.use_lld;
     const use_llvm = comp.config.use_llvm;
     assert(target.ofmt == .macho);
 
     const gpa = comp.gpa;
-    const emit = options.emit;
     const mode: Mode = mode: {
-        if (use_llvm or comp.module == null or comp.cache_mode == .whole)
+        if (use_llvm or comp.module == null or comp.cache_use == .whole)
             break :mode .zld;
         break :mode .incremental;
     };
@@ -201,7 +204,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO {
         if (comp.module == null) {
             // No point in opening a file, we would not write anything to it.
             // Initialize with empty.
-            return createEmpty(arena, options);
+            return createEmpty(arena, comp, emit, options);
         }
         // Open a temporary object file, not the final output file because we
         // want to link with LLD.
@@ -210,7 +213,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO {
         });
     } else emit.sub_path;
 
-    const self = try createEmpty(arena, options);
+    const self = try createEmpty(arena, comp, emit, options);
     errdefer self.base.destroy();
 
     if (mode == .zld) {
@@ -232,7 +235,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO {
     });
     self.base.file = file;
 
-    if (self.base.debug_format != .strip and comp.module != null) {
+    if (comp.config.debug_format != .strip and comp.module != null) {
         // Create dSYM bundle.
         log.debug("creating {s}.dSYM bundle", .{sub_path});
 
@@ -279,8 +282,12 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*MachO {
     return self;
 }
 
-pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO {
-    const comp = options.comp;
+pub fn createEmpty(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*MachO {
     const optimize_mode = comp.root_mod.optimize_mode;
     const use_llvm = comp.config.use_llvm;
 
@@ -289,7 +296,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO {
         .base = .{
             .tag = .macho,
             .comp = comp,
-            .emit = options.emit,
+            .emit = emit,
             .gc_sections = options.gc_sections orelse (optimize_mode != .Debug),
             .stack_size = options.stack_size orelse 16777216,
             .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
@@ -298,11 +305,8 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
-            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
         },
-        .mode = if (use_llvm or comp.module == null or comp.cache_mode == .whole)
+        .mode = if (use_llvm or comp.module == null or comp.cache_use == .whole)
             .zld
         else
             .incremental,
@@ -317,7 +321,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*MachO {
     };
 
     if (use_llvm and comp.module != null) {
-        self.llvm_object = try LlvmObject.create(arena, options);
+        self.llvm_object = try LlvmObject.create(arena, comp);
     }
 
     log.debug("selected linker mode '{s}'", .{@tagName(self.mode)});
@@ -4313,7 +4317,8 @@ fn addLocalToSymtab(self: *MachO, sym_loc: SymbolWithLoc, locals: *std.ArrayList
 }
 
 fn writeSymtab(self: *MachO) !SymtabCtx {
-    const gpa = self.base.comp.gpa;
+    const comp = self.base.comp;
+    const gpa = comp.gpa;
 
     var locals = std.ArrayList(macho.nlist_64).init(gpa);
     defer locals.deinit();
@@ -4368,7 +4373,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx {
 
     // We generate stabs last in order to ensure that the strtab always has debug info
     // strings trailing
-    if (self.base.debug_format != .strip) {
+    if (comp.config.debug_format != .strip) {
         for (self.objects.items) |object| {
             assert(self.d_sym == null); // TODO
             try self.generateSymbolStabs(object, &locals);
src/link/NvPtx.zig
@@ -25,12 +25,17 @@ const LlvmObject = @import("../codegen/llvm.zig").Object;
 base: link.File,
 llvm_object: *LlvmObject,
 
-pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx {
+pub fn createEmpty(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*NvPtx {
     if (build_options.only_c) unreachable;
 
-    const target = options.comp.root_mod.resolved_target.result;
-    const use_lld = build_options.have_llvm and options.comp.config.use_lld;
-    const use_llvm = options.comp.config.use_llvm;
+    const target = comp.root_mod.resolved_target.result;
+    const use_lld = build_options.have_llvm and comp.config.use_lld;
+    const use_llvm = comp.config.use_llvm;
 
     assert(use_llvm); // Caught by Compilation.Config.resolve.
     assert(!use_lld); // Caught by Compilation.Config.resolve.
@@ -42,13 +47,13 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx {
         else => return error.PtxArchNotSupported,
     }
 
-    const llvm_object = try LlvmObject.create(arena, options);
+    const llvm_object = try LlvmObject.create(arena, comp);
     const nvptx = try arena.create(NvPtx);
     nvptx.* = .{
         .base = .{
             .tag = .nvptx,
-            .comp = options.comp,
-            .emit = options.emit,
+            .comp = comp,
+            .emit = emit,
             .gc_sections = options.gc_sections orelse false,
             .stack_size = options.stack_size orelse 0,
             .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
@@ -57,9 +62,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
-            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
         },
         .llvm_object = llvm_object,
     };
@@ -67,10 +69,15 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*NvPtx {
     return nvptx;
 }
 
-pub fn open(arena: Allocator, options: link.File.OpenOptions) !*NvPtx {
-    const target = options.comp.root_mod.resolved_target.result;
+pub fn open(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*NvPtx {
+    const target = comp.root_mod.resolved_target.result;
     assert(target.ofmt == .nvptx);
-    return createEmpty(arena, options);
+    return createEmpty(arena, comp, emit, options);
 }
 
 pub fn deinit(self: *NvPtx) void {
src/link/Plan9.zig
@@ -294,8 +294,12 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
     };
 }
 
-pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Plan9 {
-    const comp = options.comp;
+pub fn createEmpty(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*Plan9 {
     const target = comp.root_mod.resolved_target.result;
     const gpa = comp.gpa;
     const optimize_mode = comp.root_mod.optimize_mode;
@@ -313,7 +317,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Plan9 {
         .base = .{
             .tag = .plan9,
             .comp = comp,
-            .emit = options.emit,
+            .emit = emit,
             .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
             .stack_size = options.stack_size orelse 16777216,
             .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
@@ -322,9 +326,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Plan9 {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
-            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
         },
         .sixtyfour_bit = sixtyfour_bit,
         .bases = undefined,
@@ -1308,26 +1309,31 @@ pub fn deinit(self: *Plan9) void {
     }
 }
 
-pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Plan9 {
+pub fn open(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*Plan9 {
     if (build_options.only_c) unreachable;
 
-    const target = options.comp.root_mod.resolved_target.result;
-    const use_lld = build_options.have_llvm and options.comp.config.use_lld;
-    const use_llvm = options.comp.config.use_llvm;
+    const target = comp.root_mod.resolved_target.result;
+    const use_lld = build_options.have_llvm and comp.config.use_lld;
+    const use_llvm = comp.config.use_llvm;
 
     assert(!use_llvm); // Caught by Compilation.Config.resolve.
     assert(!use_lld); // Caught by Compilation.Config.resolve.
     assert(target.ofmt == .plan9);
 
-    const self = try createEmpty(arena, options);
+    const self = try createEmpty(arena, comp, emit, options);
     errdefer self.base.destroy();
 
-    const file = try options.emit.directory.handle.createFile(options.emit.sub_path, .{
+    const file = try emit.directory.handle.createFile(emit.sub_path, .{
         .read = true,
         .mode = link.File.determineMode(
             use_lld,
-            options.comp.config.output_mode,
-            options.comp.config.link_mode,
+            comp.config.output_mode,
+            comp.config.link_mode,
         ),
     });
     errdefer file.close();
@@ -1335,7 +1341,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Plan9 {
 
     self.bases = defaultBaseAddrs(target.cpu.arch);
 
-    const gpa = options.comp.gpa;
+    const gpa = comp.gpa;
 
     try self.syms.appendSlice(gpa, &.{
         // we include the global offset table to make it easier for debugging
src/link/SpirV.zig
@@ -49,16 +49,21 @@ object: codegen.Object,
 
 pub const base_tag: link.File.Tag = .spirv;
 
-pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*SpirV {
-    const gpa = options.comp.gpa;
-    const target = options.comp.root_mod.resolved_target.result;
+pub fn createEmpty(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*SpirV {
+    const gpa = comp.gpa;
+    const target = comp.root_mod.resolved_target.result;
 
     const self = try arena.create(SpirV);
     self.* = .{
         .base = .{
             .tag = .spirv,
-            .comp = options.comp,
-            .emit = options.emit,
+            .comp = comp,
+            .emit = emit,
             .gc_sections = options.gc_sections orelse false,
             .stack_size = options.stack_size orelse 0,
             .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
@@ -67,9 +72,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*SpirV {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
-            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
         },
         .object = codegen.Object.init(gpa),
     };
@@ -90,22 +92,27 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*SpirV {
     return self;
 }
 
-pub fn open(arena: Allocator, options: link.File.OpenOptions) !*SpirV {
+pub fn open(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*SpirV {
     if (build_options.only_c) unreachable;
 
-    const target = options.comp.root_mod.resolved_target.result;
-    const use_lld = build_options.have_llvm and options.comp.config.use_lld;
-    const use_llvm = options.comp.config.use_llvm;
+    const target = comp.root_mod.resolved_target.result;
+    const use_lld = build_options.have_llvm and comp.config.use_lld;
+    const use_llvm = comp.config.use_llvm;
 
     assert(!use_llvm); // Caught by Compilation.Config.resolve.
     assert(!use_lld); // Caught by Compilation.Config.resolve.
     assert(target.ofmt == .spirv); // Caught by Compilation.Config.resolve.
 
-    const spirv = try createEmpty(arena, options);
+    const spirv = try createEmpty(arena, comp, emit, options);
     errdefer spirv.base.destroy();
 
     // TODO: read the file and keep valid parts instead of truncating
-    const file = try options.emit.directory.handle.createFile(options.emit.sub_path, .{
+    const file = try emit.directory.handle.createFile(emit.sub_path, .{
         .truncate = true,
         .read = true,
     });
src/link/Wasm.zig
@@ -373,9 +373,13 @@ pub const StringTable = struct {
     }
 };
 
-pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
+pub fn open(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*Wasm {
     if (build_options.only_c) unreachable;
-    const comp = options.comp;
     const gpa = comp.gpa;
     const target = comp.root_mod.resolved_target.result;
     assert(target.ofmt == .wasm);
@@ -385,7 +389,7 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
     const output_mode = comp.config.output_mode;
     const shared_memory = comp.config.shared_memory;
 
-    const wasm = try createEmpty(arena, options);
+    const wasm = try createEmpty(arena, comp, emit, options);
     errdefer wasm.base.destroy();
 
     if (use_lld and use_llvm) {
@@ -393,18 +397,18 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
         return wasm;
     }
 
-    const sub_path = if (!use_lld) options.emit.sub_path else p: {
+    const sub_path = if (!use_lld) emit.sub_path else p: {
         // Open a temporary object file, not the final output file because we
         // want to link with LLD.
         const o_file_path = try std.fmt.allocPrint(arena, "{s}{s}", .{
-            options.emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
+            emit.sub_path, target.ofmt.fileExt(target.cpu.arch),
         });
         wasm.base.intermediary_basename = o_file_path;
         break :p o_file_path;
     };
 
     // TODO: read the file and keep valid parts instead of truncating
-    const file = try options.emit.directory.handle.createFile(sub_path, .{
+    const file = try emit.directory.handle.createFile(sub_path, .{
         .truncate = true,
         .read = true,
         .mode = if (fs.has_executable_bit)
@@ -530,8 +534,12 @@ pub fn open(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
     return wasm;
 }
 
-pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
-    const comp = options.comp;
+pub fn createEmpty(
+    arena: Allocator,
+    comp: *Compilation,
+    emit: Compilation.Emit,
+    options: link.File.OpenOptions,
+) !*Wasm {
     const use_llvm = comp.config.use_llvm;
     const output_mode = comp.config.output_mode;
 
@@ -540,7 +548,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
         .base = .{
             .tag = .wasm,
             .comp = comp,
-            .emit = options.emit,
+            .emit = emit,
             .gc_sections = options.gc_sections orelse (output_mode != .Obj),
             .stack_size = options.stack_size orelse std.wasm.page_size * 16, // 1MB
             .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
@@ -549,9 +557,6 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
             .build_id = options.build_id,
             .rpath_list = options.rpath_list,
             .force_undefined_symbols = options.force_undefined_symbols,
-            .debug_format = options.debug_format orelse .{ .dwarf = .@"32" },
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
         },
         .name = undefined,
         .import_table = options.import_table,
@@ -566,7 +571,7 @@ pub fn createEmpty(arena: Allocator, options: link.File.OpenOptions) !*Wasm {
     };
 
     if (use_llvm) {
-        wasm.llvm_object = try LlvmObject.create(arena, options);
+        wasm.llvm_object = try LlvmObject.create(arena, comp);
     }
     return wasm;
 }
@@ -4205,11 +4210,11 @@ fn writeToFile(
         if (data_section_index) |data_index| {
             try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table);
         }
-    } else if (wasm.base.debug_format != .strip) {
+    } else if (comp.config.debug_format != .strip) {
         try wasm.emitNameSection(&binary_bytes, arena);
     }
 
-    if (wasm.base.debug_format != .strip) {
+    if (comp.config.debug_format != .strip) {
         // The build id must be computed on the main sections only,
         // so we have to do it now, before the debug sections.
         switch (wasm.base.build_id) {
@@ -4748,7 +4753,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !
             try argv.append("--no-gc-sections");
         }
 
-        if (wasm.base.debug_format == .strip) {
+        if (comp.config.debug_format == .strip) {
             try argv.append("-s");
         }
 
@@ -5276,7 +5281,9 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s
 fn markReferences(wasm: *Wasm) !void {
     const tracy = trace(@src());
     defer tracy.end();
+
     const do_garbage_collect = wasm.base.gc_sections;
+    const comp = wasm.base.comp;
 
     for (wasm.resolved_symbols.keys()) |sym_loc| {
         const sym = sym_loc.getSymbol(wasm);
@@ -5287,7 +5294,7 @@ fn markReferences(wasm: *Wasm) !void {
 
         // Debug sections may require to be parsed and marked when it contains
         // relocations to alive symbols.
-        if (sym.tag == .section and wasm.base.debug_format != .strip) {
+        if (sym.tag == .section and comp.config.debug_format != .strip) {
             const file = sym_loc.file orelse continue; // Incremental debug info is done independently
             const object = &wasm.objects.items[file];
             const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm);
src/Package/Module.zig
@@ -33,11 +33,16 @@ cc_argv: []const []const u8,
 /// (SPIR-V) whether to generate a structured control flow graph or not
 structured_cfg: bool,
 
-/// The contents of `@import("builtin")` for this module.
-generated_builtin_source: []const u8,
+/// If the module is an `@import("builtin")` module, this is the `File` that
+/// is preallocated for it. Otherwise this field is null.
+builtin_file: ?*File,
 
 pub const Deps = std.StringArrayHashMapUnmanaged(*Module);
 
+pub fn isBuiltin(m: Module) bool {
+    return m.file != null;
+}
+
 pub const Tree = struct {
     /// Each `Package` exposes a `Module` with build.zig as its root source file.
     build_module_table: std.AutoArrayHashMapUnmanaged(MultiHashHexDigest, *Module),
@@ -329,6 +334,8 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
             .wasi_exec_model = options.global.wasi_exec_model,
         }, arena);
 
+        const new_file = try arena.create(File);
+
         const digest = Cache.HashHelper.oneShot(generated_builtin_source);
         const builtin_sub_path = try arena.dupe(u8, "b" ++ std.fs.path.sep_str ++ digest);
         const new = try arena.create(Module);
@@ -359,12 +366,25 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
             .stack_protector = stack_protector,
             .code_model = code_model,
             .red_zone = red_zone,
-            .generated_builtin_source = generated_builtin_source,
             .sanitize_c = sanitize_c,
             .sanitize_thread = sanitize_thread,
             .unwind_tables = unwind_tables,
             .cc_argv = &.{},
             .structured_cfg = structured_cfg,
+            .builtin_file = new_file,
+        };
+        new_file.* = .{
+            .sub_file_path = "builtin.zig",
+            .source = generated_builtin_source,
+            .source_loaded = true,
+            .tree_loaded = false,
+            .zir_loaded = false,
+            .stat = undefined,
+            .tree = undefined,
+            .zir = undefined,
+            .status = .never_loaded,
+            .mod = new,
+            .root_decl = .none,
         };
         break :b new;
     };
@@ -391,12 +411,12 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
         .stack_protector = stack_protector,
         .code_model = code_model,
         .red_zone = red_zone,
-        .generated_builtin_source = builtin_mod.generated_builtin_source,
         .sanitize_c = sanitize_c,
         .sanitize_thread = sanitize_thread,
         .unwind_tables = unwind_tables,
         .cc_argv = options.cc_argv,
         .structured_cfg = structured_cfg,
+        .builtin_file = null,
     };
 
     try mod.deps.ensureUnusedCapacity(arena, 1);
@@ -437,8 +457,8 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P
         .sanitize_thread = undefined,
         .unwind_tables = undefined,
         .cc_argv = undefined,
-        .generated_builtin_source = undefined,
         .structured_cfg = undefined,
+        .builtin_file = null,
     };
     return mod;
 }
@@ -457,3 +477,4 @@ const Cache = std.Build.Cache;
 const Builtin = @import("../Builtin.zig");
 const assert = std.debug.assert;
 const Compilation = @import("../Compilation.zig");
+const File = @import("../Module.zig").File;
src/Builtin.zig
@@ -20,8 +20,11 @@ wasi_exec_model: std.builtin.WasiExecModel,
 
 pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 {
     var buffer = std.ArrayList(u8).init(allocator);
-    defer buffer.deinit();
+    try append(opts, &buffer);
+    return buffer.toOwnedSliceSentinel(0);
+}
 
+pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void {
     const target = opts.target;
     const generic_arch_name = target.cpu.arch.genericName();
     const zig_backend = opts.zig_backend;
@@ -231,10 +234,65 @@ pub fn generate(opts: @This(), allocator: Allocator) Allocator.Error![:0]u8 {
             );
         }
     }
+}
 
-    return buffer.toOwnedSliceSentinel(0);
+pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void {
+    assert(file.source_loaded == true);
+
+    if (mod.root.statFile(mod.root_src_path)) |stat| {
+        if (stat.size != file.source.len) {
+            std.log.warn(
+                "the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++
+                    "Overwriting with correct file contents now",
+                .{ mod.root, mod.root_src_path, file.source.len, stat.size },
+            );
+
+            try writeFile(file, mod);
+        } else {
+            file.stat = .{
+                .size = stat.size,
+                .inode = stat.inode,
+                .mtime = stat.mtime,
+            };
+        }
+    } else |err| switch (err) {
+        error.BadPathName => unreachable, // it's always "builtin.zig"
+        error.NameTooLong => unreachable, // it's always "builtin.zig"
+        error.PipeBusy => unreachable, // it's not a pipe
+        error.WouldBlock => unreachable, // not asking for non-blocking I/O
+
+        error.FileNotFound => try writeFile(file, mod),
+
+        else => |e| return e,
+    }
+
+    file.tree = try std.zig.Ast.parse(comp.gpa, file.source, .zig);
+    file.tree_loaded = true;
+    assert(file.tree.errors.len == 0); // builtin.zig must parse
+
+    file.zir = try AstGen.generate(comp.gpa, file.tree);
+    file.zir_loaded = true;
+    file.status = .success_zir;
+}
+
+fn writeFile(file: *File, mod: *Module) !void {
+    var af = try mod.root.atomicFile(mod.root_src_path, .{});
+    defer af.deinit();
+    try af.file.writeAll(file.source);
+    try af.finish();
+
+    file.stat = .{
+        .size = file.source.len,
+        .inode = 0, // dummy value
+        .mtime = 0, // dummy value
+    };
 }
 
 const std = @import("std");
 const Allocator = std.mem.Allocator;
 const build_options = @import("build_options");
+const Module = @import("Package/Module.zig");
+const assert = std.debug.assert;
+const AstGen = @import("AstGen.zig");
+const File = @import("Module.zig").File;
+const Compilation = @import("Compilation.zig");
src/Compilation.zig
@@ -37,6 +37,7 @@ const Zir = @import("Zir.zig");
 const Autodoc = @import("Autodoc.zig");
 const Color = @import("main.zig").Color;
 const resinator = @import("resinator.zig");
+const Builtin = @import("Builtin.zig");
 
 pub const Config = @import("Compilation/Config.zig");
 
@@ -59,7 +60,10 @@ root_mod: *Package.Module,
 /// User-specified settings that have all the defaults resolved into concrete values.
 config: Config,
 
-/// This is `null` when `-fno-emit-bin` is used.
+/// The main output file.
+/// In whole cache mode, this is null except for during the body of the update
+/// function. In incremental cache mode, this is a long-lived object.
+/// In both cases, this is `null` when `-fno-emit-bin` is used.
 bin_file: ?*link.File,
 
 /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
@@ -80,6 +84,8 @@ version: ?std.SemanticVersion,
 libc_installation: ?*const LibCInstallation,
 skip_linker_dependencies: bool,
 no_builtin: bool,
+function_sections: bool,
+data_sections: bool,
 
 c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
 win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) =
@@ -120,7 +126,6 @@ failed_win32_resources: if (build_options.only_core_functionality) void else std
 /// Miscellaneous things that can fail.
 misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{},
 
-keep_source_files_loaded: bool,
 /// When this is `true` it means invoking clang as a sub-process is expected to inherit
 /// stdin, stdout, stderr, and if it returns non success, to forward the exit code.
 /// Otherwise we attempt to parse the error messages and expose them via the Compilation API.
@@ -144,6 +149,7 @@ debug_compiler_runtime_libs: bool,
 debug_compile_errors: bool,
 job_queued_compiler_rt_lib: bool = false,
 job_queued_compiler_rt_obj: bool = false,
+job_queued_update_builtin_zig: bool,
 alloc_failure_occurred: bool = false,
 formatted_panics: bool = false,
 last_update_was_cache_hit: bool = false,
@@ -814,13 +820,13 @@ pub const cache_helpers = struct {
         addEmitLoc(hh, optional_emit_loc orelse return);
     }
 
-    pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?link.File.DebugFormat) void {
+    pub fn addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void {
         hh.add(x != null);
         addDebugFormat(hh, x orelse return);
     }
 
-    pub fn addDebugFormat(hh: *Cache.HashHelper, x: link.File.DebugFormat) void {
-        const tag: @typeInfo(link.File.DebugFormat).Union.tag_type.? = x;
+    pub fn addDebugFormat(hh: *Cache.HashHelper, x: Config.DebugFormat) void {
+        const tag: @typeInfo(Config.DebugFormat).Union.tag_type.? = x;
         hh.add(tag);
         switch (x) {
             .strip, .code_view => {},
@@ -860,11 +866,11 @@ pub const SystemLib = link.SystemLib;
 
 pub const CacheMode = enum { incremental, whole };
 
-pub const CacheUse = union(CacheMode) {
+const CacheUse = union(CacheMode) {
     incremental: *Incremental,
     whole: *Whole,
 
-    pub const Whole = struct {
+    const Whole = struct {
         /// This is a pointer to a local variable inside `update()`.
         cache_manifest: ?*Cache.Manifest = null,
         cache_manifest_mutex: std.Thread.Mutex = .{},
@@ -873,12 +879,14 @@ pub const CacheUse = union(CacheMode) {
         /// of exactly the correct size for "o/[digest]/[basename]".
         /// The basename is of the outputted binary file in case we don't know the directory yet.
         bin_sub_path: ?[]u8,
-        /// Same as `whole_bin_sub_path` but for implibs.
+        /// Same as `bin_sub_path` but for implibs.
         implib_sub_path: ?[]u8,
         docs_sub_path: ?[]u8,
+        lf_open_opts: link.File.OpenOptions,
+        tmp_artifact_directory: ?Cache.Directory,
     };
 
-    pub const Incremental = struct {
+    const Incremental = struct {
         /// Where build artifacts and incremental compilation metadata serialization go.
         artifact_directory: Compilation.Directory,
     };
@@ -937,7 +945,6 @@ pub const InitOptions = struct {
     /// this flag would be set to disable this machinery to avoid false positives.
     disable_lld_caching: bool = false,
     cache_mode: CacheMode = .incremental,
-    keep_source_files_loaded: bool = false,
     lib_dirs: []const []const u8 = &[0][]const u8{},
     rpath_list: []const []const u8 = &[0][]const u8{},
     symbol_wrap_set: std.StringArrayHashMapUnmanaged(void) = .{},
@@ -1040,7 +1047,6 @@ pub const InitOptions = struct {
     test_name_prefix: ?[]const u8 = null,
     test_runner_path: ?[]const u8 = null,
     subsystem: ?std.Target.SubSystem = null,
-    debug_format: ?link.File.DebugFormat = null,
     /// (Zig compiler development) Enable dumping linker's state as JSON.
     enable_link_snapshots: bool = false,
     /// (Darwin) Install name of the dylib
@@ -1327,7 +1333,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
         cache.hash.add(options.config.link_libcpp);
         cache.hash.add(options.config.link_libunwind);
         cache.hash.add(output_mode);
-        cache_helpers.addOptionalDebugFormat(&cache.hash, options.debug_format);
+        cache_helpers.addDebugFormat(&cache.hash, comp.config.debug_format);
         cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin);
         cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib);
         cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_docs);
@@ -1380,7 +1386,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
         };
         errdefer if (opt_zcu) |zcu| zcu.deinit();
 
-        const system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init(
+        var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init(
             gpa,
             options.system_lib_names,
             options.system_lib_infos,
@@ -1409,7 +1415,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
             .win32_resource_work_queue = if (build_options.only_core_functionality) {} else std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa),
             .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa),
             .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa),
-            .keep_source_files_loaded = options.keep_source_files_loaded,
             .c_source_files = options.c_source_files,
             .rc_source_files = options.rc_source_files,
             .cache_parent = cache,
@@ -1451,10 +1456,12 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
             .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit,
             .skip_linker_dependencies = options.skip_linker_dependencies,
             .no_builtin = options.no_builtin,
+            .job_queued_update_builtin_zig = have_zcu,
+            .function_sections = options.function_sections,
+            .data_sections = options.data_sections,
         };
 
         const lf_open_opts: link.File.OpenOptions = .{
-            .comp = comp,
             .linker_script = options.linker_script,
             .z_nodelete = options.linker_z_nodelete,
             .z_notext = options.linker_z_notext,
@@ -1471,8 +1478,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
             .lib_dirs = options.lib_dirs,
             .rpath_list = options.rpath_list,
             .symbol_wrap_set = options.symbol_wrap_set,
-            .function_sections = options.function_sections,
-            .data_sections = options.data_sections,
             .allow_shlib_undefined = options.linker_allow_shlib_undefined,
             .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false,
             .compress_debug_sections = options.linker_compress_debug_sections orelse .none,
@@ -1507,7 +1512,6 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
             .build_id = build_id,
             .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole,
             .subsystem = options.subsystem,
-            .debug_format = options.debug_format,
             .hash_style = options.hash_style,
             .enable_link_snapshots = options.enable_link_snapshots,
             .install_name = options.install_name,
@@ -1572,17 +1576,17 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
                         .directory = emit_bin.directory orelse artifact_directory,
                         .sub_path = emit_bin.basename,
                     };
-                    comp.bin_file = try link.File.open(arena, emit, lf_open_opts);
+                    comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts);
                 }
 
-                if (options.implib_emit) |emit_implib| {
+                if (options.emit_implib) |emit_implib| {
                     comp.implib_emit = .{
                         .directory = emit_implib.directory orelse artifact_directory,
                         .sub_path = emit_implib.basename,
                     };
                 }
 
-                if (options.docs_emit) |emit_docs| {
+                if (options.emit_docs) |emit_docs| {
                     comp.docs_emit = .{
                         .directory = emit_docs.directory orelse artifact_directory,
                         .sub_path = emit_docs.basename,
@@ -1610,6 +1614,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
                     .bin_sub_path = try prepareWholeEmitSubPath(arena, options.emit_bin),
                     .implib_sub_path = try prepareWholeEmitSubPath(arena, options.emit_implib),
                     .docs_sub_path = try prepareWholeEmitSubPath(arena, options.emit_docs),
+                    .tmp_artifact_directory = null,
                 };
                 comp.cache_use = .{ .whole = whole };
             },
@@ -1662,7 +1667,10 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
         }
     }
 
-    const have_bin_emit = comp.bin_file != null or comp.whole_bin_sub_path != null;
+    const have_bin_emit = switch (comp.cache_use) {
+        .whole => |whole| whole.bin_sub_path != null,
+        .incremental => comp.bin_file != null,
+    };
 
     if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) {
         if (target.isDarwin()) {
@@ -1814,8 +1822,13 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
 pub fn destroy(self: *Compilation) void {
     if (self.bin_file) |lf| lf.destroy();
     if (self.module) |zcu| zcu.deinit();
+    switch (self.cache_use) {
+        .incremental => |incremental| {
+            incremental.artifact_directory.handle.close();
+        },
+        .whole => {},
+    }
 
-    const gpa = self.gpa;
     self.work_queue.deinit();
     self.anon_work_queue.deinit();
     self.c_object_work_queue.deinit();
@@ -1825,6 +1838,9 @@ pub fn destroy(self: *Compilation) void {
     self.astgen_work_queue.deinit();
     self.embed_file_work_queue.deinit();
 
+    const gpa = self.gpa;
+    self.system_libs.deinit(gpa);
+
     {
         var it = self.crt_files.iterator();
         while (it.next()) |entry| {
@@ -1914,7 +1930,7 @@ pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.C
 }
 
 fn cleanupAfterUpdate(comp: *Compilation) void {
-    switch (comp) {
+    switch (comp.cache_use) {
         .incremental => return,
         .whole => |whole| {
             if (whole.cache_manifest) |man| {
@@ -1971,7 +1987,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
                 log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name});
                 const digest = man.final();
 
-                comp.wholeCacheModeSetBinFilePath(&digest);
+                comp.wholeCacheModeSetBinFilePath(whole, &digest);
 
                 assert(comp.bin_file.lock == null);
                 comp.bin_file.lock = man.toOwnedLock();
@@ -2001,21 +2017,21 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
             // Now that the directory is known, it is time to create the Emit
             // objects and call link.File.open.
 
-            if (comp.whole_implib_sub_path) |sub_path| {
+            if (whole.implib_sub_path) |sub_path| {
                 comp.implib_emit = .{
                     .directory = tmp_artifact_directory,
                     .sub_path = std.fs.path.basename(sub_path),
                 };
             }
 
-            if (comp.whole_docs_sub_path) |sub_path| {
+            if (whole.docs_sub_path) |sub_path| {
                 comp.docs_emit = .{
                     .directory = tmp_artifact_directory,
                     .sub_path = std.fs.path.basename(sub_path),
                 };
             }
 
-            if (comp.whole_bin_sub_path) |sub_path| {
+            if (whole.bin_sub_path) |sub_path| {
                 const emit: Emit = .{
                     .directory = tmp_artifact_directory,
                     .sub_path = std.fs.path.basename(sub_path),
@@ -2024,7 +2040,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
                 // but in practice it won't leak much and usually whole cache mode
                 // will be combined with exactly one call to update().
                 const arena = comp.arena.allocator();
-                comp.bin_file = try link.File.open(arena, emit, whole.lf_open_opts);
+                comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts);
             }
         },
         .incremental => {},
@@ -2158,7 +2174,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
             const o_sub_path = "o" ++ s ++ digest;
 
             try renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path);
-            comp.wholeCacheModeSetBinFilePath(&digest);
+            comp.wholeCacheModeSetBinFilePath(whole, &digest);
 
             // Failure here only means an unnecessary cache miss.
             man.writeManifest() catch |err| {
@@ -2170,19 +2186,6 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
         },
         .incremental => {},
     }
-
-    // Unload all source files to save memory.
-    // The ZIR needs to stay loaded in memory because (1) Decl objects contain references
-    // to it, and (2) generic instantiations, comptime calls, inline calls will need
-    // to reference the ZIR.
-    if (!comp.keep_source_files_loaded) {
-        if (comp.module) |module| {
-            for (module.import_table.values()) |file| {
-                file.unloadTree(comp.gpa);
-                file.unloadSource(comp.gpa);
-            }
-        }
-    }
 }
 
 /// This function is called by the frontend before flush(). It communicates that
@@ -2274,10 +2277,14 @@ fn flush(comp: *Compilation, prog_node: *std.Progress.Node) !void {
 }
 
 /// Communicate the output binary location to parent Compilations.
-fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_digest_len]u8) void {
+fn wholeCacheModeSetBinFilePath(
+    comp: *Compilation,
+    whole: *CacheUse.Whole,
+    digest: *const [Cache.hex_digest_len]u8,
+) void {
     const digest_start = 2; // "o/[digest]/[basename]"
 
-    if (comp.whole_bin_sub_path) |sub_path| {
+    if (whole.bin_sub_path) |sub_path| {
         @memcpy(sub_path[digest_start..][0..digest.len], digest);
 
         comp.bin_file.?.emit = .{
@@ -2286,7 +2293,7 @@ fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_di
         };
     }
 
-    if (comp.whole_implib_sub_path) |sub_path| {
+    if (whole.implib_sub_path) |sub_path| {
         @memcpy(sub_path[digest_start..][0..digest.len], digest);
 
         comp.implib_emit = .{
@@ -2295,7 +2302,7 @@ fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_di
         };
     }
 
-    if (comp.whole_docs_sub_path) |sub_path| {
+    if (whole.docs_sub_path) |sub_path| {
         @memcpy(sub_path[digest_start..][0..digest.len], digest);
 
         comp.docs_emit = .{
@@ -3232,13 +3239,25 @@ pub fn performAllTheWork(
         // 1. to avoid race condition of zig processes truncating each other's builtin.zig files
         // 2. optimization; in the hot path it only incurs a stat() syscall, which happens
         //    in the `astgen_wait_group`.
-        if (comp.module) |mod| {
-            if (mod.job_queued_update_builtin_zig) {
-                mod.job_queued_update_builtin_zig = false;
+        if (comp.job_queued_update_builtin_zig) b: {
+            comp.job_queued_update_builtin_zig = false;
+            const zcu = comp.module orelse break :b;
+            _ = zcu;
+            // TODO put all the modules in a flat array to make them easy to iterate.
+            var seen: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{};
+            defer seen.deinit(comp.gpa);
+            try seen.put(comp.gpa, comp.root_mod);
+            var i: usize = 0;
+            while (i < seen.count()) : (i += 1) {
+                const mod = seen.keys()[i];
+                for (mod.deps.values()) |dep|
+                    try seen.put(comp.gpa, dep);
+
+                const file = mod.builtin_file orelse continue;
 
                 comp.astgen_wait_group.start();
                 try comp.thread_pool.spawn(workerUpdateBuiltinZigFile, .{
-                    comp, mod, &comp.astgen_wait_group,
+                    comp, mod, file, &comp.astgen_wait_group,
                 });
             }
         }
@@ -3702,19 +3721,17 @@ fn workerAstGenFile(
 
 fn workerUpdateBuiltinZigFile(
     comp: *Compilation,
-    mod: *Module,
+    mod: *Package.Module,
+    file: *Module.File,
     wg: *WaitGroup,
 ) void {
     defer wg.finish();
-
-    mod.populateBuiltinFile() catch |err| {
-        const dir_path: []const u8 = mod.zig_cache_artifact_directory.path orelse ".";
-
+    Builtin.populateFile(comp, mod, file) catch |err| {
         comp.mutex.lock();
         defer comp.mutex.unlock();
 
-        comp.setMiscFailure(.write_builtin_zig, "unable to write builtin.zig to {s}: {s}", .{
-            dir_path, @errorName(err),
+        comp.setMiscFailure(.write_builtin_zig, "unable to write '{}{s}': {s}", .{
+            mod.root, mod.root_src_path, @errorName(err),
         });
     };
 }
@@ -3755,14 +3772,17 @@ fn detectEmbedFileUpdate(comp: *Compilation, embed_file: *Module.EmbedFile) !voi
     @panic("TODO: handle embed file incremental update");
 }
 
-pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest {
+pub fn obtainCObjectCacheManifest(
+    comp: *const Compilation,
+    owner_mod: *Package.Module,
+) Cache.Manifest {
     var man = comp.cache_parent.obtain();
 
     // Only things that need to be added on top of the base hash, and only things
     // that apply both to @cImport and compiling C objects. No linking stuff here!
     // Also nothing that applies only to compiling .zig code.
-    man.hash.add(comp.sanitize_c);
-    man.hash.addListOfBytes(comp.clang_argv);
+    man.hash.add(owner_mod.sanitize_c);
+    man.hash.addListOfBytes(owner_mod.clang_argv);
     man.hash.add(comp.config.link_libcpp);
 
     // When libc_installation is null it means that Zig generated this dir list
@@ -3797,19 +3817,19 @@ pub const CImportResult = struct {
 /// Caller owns returned memory.
 /// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked
 /// a bit when we want to start using it from self-hosted.
-pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
+pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult {
     if (build_options.only_core_functionality) @panic("@cImport is not available in a zig2.c build");
     const tracy_trace = trace(@src());
     defer tracy_trace.end();
 
     const cimport_zig_basename = "cimport.zig";
 
-    var man = comp.obtainCObjectCacheManifest();
+    var man = comp.obtainCObjectCacheManifest(owner_mod);
     defer man.deinit();
 
     man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
     man.hash.addBytes(c_src);
-    man.hash.add(comp.c_frontend);
+    man.hash.add(comp.config.c_frontend);
 
     // If the previous invocation resulted in clang errors, we will see a hit
     // here with 0 files in the manifest, in which case it is actually a miss.
@@ -3846,15 +3866,15 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
         var argv = std.ArrayList([]const u8).init(comp.gpa);
         defer argv.deinit();
 
-        try argv.append(@tagName(comp.c_frontend)); // argv[0] is program name, actual args start at [1]
-        try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path);
+        try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1]
+        try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path, owner_mod);
 
         try argv.append(out_h_path);
 
         if (comp.verbose_cc) {
             dump_argv(argv.items);
         }
-        var tree = switch (comp.c_frontend) {
+        var tree = switch (comp.config.c_frontend) {
             .aro => tree: {
                 const translate_c = @import("aro_translate_c.zig");
                 _ = translate_c;
@@ -4119,7 +4139,7 @@ fn reportRetryableEmbedFileError(
 }
 
 fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.Progress.Node) !void {
-    if (comp.c_frontend == .aro) {
+    if (comp.config.c_frontend == .aro) {
         return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{});
     }
     if (!build_options.have_llvm) {
@@ -4142,7 +4162,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
         _ = comp.failed_c_objects.swapRemove(c_object);
     }
 
-    var man = comp.obtainCObjectCacheManifest();
+    var man = comp.obtainCObjectCacheManifest(c_object.src.owner);
     defer man.deinit();
 
     man.hash.add(comp.clang_preprocessor_mode);
@@ -4219,7 +4239,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
         if (std.process.can_execv and direct_o and
             comp.disable_c_depfile and comp.clang_passthrough_mode)
         {
-            try comp.addCCArgs(arena, &argv, ext, null);
+            try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner);
             try argv.appendSlice(c_object.src.extra_flags);
             try argv.appendSlice(c_object.src.cache_exempt_flags);
 
@@ -4262,7 +4282,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
             null
         else
             try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path});
-        try comp.addCCArgs(arena, &argv, ext, out_dep_path);
+        try comp.addCCArgs(arena, &argv, ext, out_dep_path, c_object.src.owner);
         try argv.appendSlice(c_object.src.extra_flags);
         try argv.appendSlice(c_object.src.cache_exempt_flags);
 
@@ -4610,7 +4630,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32
         // mode. While these defines are not normally present when calling rc.exe directly,
         // them being defined matches the behavior of how MSVC calls rc.exe which is the more
         // relevant behavior in this case.
-        try comp.addCCArgs(arena, &argv, .rc, out_dep_path);
+        try comp.addCCArgs(arena, &argv, .rc, out_dep_path, rc_src.owner);
 
         if (comp.verbose_cc) {
             dump_argv(argv.items);
@@ -4788,11 +4808,12 @@ pub fn addTranslateCCArgs(
     argv: *std.ArrayList([]const u8),
     ext: FileExt,
     out_dep_path: ?[]const u8,
+    owner_mod: *Package.Module,
 ) !void {
-    try argv.appendSlice(&[_][]const u8{ "-x", "c" });
-    try comp.addCCArgs(arena, argv, ext, out_dep_path);
+    try argv.appendSlice(&.{ "-x", "c" });
+    try comp.addCCArgs(arena, argv, ext, out_dep_path, owner_mod);
     // This gives us access to preprocessing entities, presumably at the cost of performance.
-    try argv.appendSlice(&[_][]const u8{ "-Xclang", "-detailed-preprocessing-record" });
+    try argv.appendSlice(&.{ "-Xclang", "-detailed-preprocessing-record" });
 }
 
 /// Add common C compiler args between translate-c and C object compilation.
@@ -4825,11 +4846,11 @@ pub fn addCCArgs(
         try argv.append("-fno-caret-diagnostics");
     }
 
-    if (comp.bin_file.function_sections) {
+    if (comp.function_sections) {
         try argv.append("-ffunction-sections");
     }
 
-    if (comp.bin_file.data_sections) {
+    if (comp.data_sections) {
         try argv.append("-fdata-sections");
     }
 
@@ -5088,7 +5109,7 @@ pub fn addCCArgs(
                 try argv.append("-fPIC");
             }
 
-            if (comp.unwind_tables) {
+            if (mod.unwind_tables) {
                 try argv.append("-funwind-tables");
             } else {
                 try argv.append("-fno-unwind-tables");
@@ -5174,7 +5195,7 @@ pub fn addCCArgs(
     }
 
     try argv.ensureUnusedCapacity(2);
-    switch (comp.bin_file.debug_format) {
+    switch (comp.config.debug_format) {
         .strip => {},
         .code_view => {
             // -g is required here because -gcodeview doesn't trigger debug info
@@ -5210,7 +5231,7 @@ pub fn addCCArgs(
         try argv.append("-ffreestanding");
     }
 
-    try argv.appendSlice(comp.clang_argv);
+    try argv.appendSlice(mod.cc_argv);
 }
 
 fn failCObj(
@@ -6094,6 +6115,7 @@ fn buildOutputFromZig(
         .have_zcu = true,
         .emit_bin = true,
         .root_optimize_mode = comp.compilerRtOptMode(),
+        .root_strip = comp.compilerRtStrip(),
         .link_libc = comp.config.link_libc,
         .any_unwind_tables = unwind_tables,
     });
@@ -6198,6 +6220,7 @@ pub fn build_crt_file(
         .have_zcu = false,
         .emit_bin = true,
         .root_optimize_mode = comp.compilerRtOptMode(),
+        .root_strip = comp.compilerRtStrip(),
         .link_libc = false,
         .lto = switch (output_mode) {
             .Lib => comp.config.lto,
src/libunwind.zig
@@ -28,6 +28,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void {
         .have_zcu = false,
         .emit_bin = true,
         .root_optimize_mode = comp.compilerRtOptMode(),
+        .root_strip = comp.compilerRtStrip(),
         .link_libc = true,
         // Disable LTO to avoid https://github.com/llvm/llvm-project/issues/56825
         .lto = false,
@@ -131,7 +132,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void {
         .libc_installation = comp.libc_installation,
         .emit_bin = emit_bin,
         .link_mode = link_mode,
-        .function_sections = comp.bin_file.function_sections,
+        .function_sections = comp.function_sections,
         .c_source_files = &c_source_files,
         .verbose_cc = comp.verbose_cc,
         .verbose_link = comp.verbose_link,
src/link.zig
@@ -68,9 +68,6 @@ pub const File = struct {
     force_undefined_symbols: std.StringArrayHashMapUnmanaged(void),
     allow_shlib_undefined: bool,
     stack_size: u64,
-    debug_format: DebugFormat,
-    function_sections: bool,
-    data_sections: bool,
 
     /// Prevents other processes from clobbering files in the output directory
     /// of this linking operation.
@@ -78,16 +75,7 @@ pub const File = struct {
 
     child_pid: ?std.ChildProcess.Id = null,
 
-    pub const DebugFormat = union(enum) {
-        strip,
-        dwarf: std.dwarf.Format,
-        code_view,
-    };
-
     pub const OpenOptions = struct {
-        comp: *Compilation,
-        emit: Compilation.Emit,
-
         symbol_count_hint: u64 = 32,
         program_code_size_hint: u64 = 256 * 1024,
 
@@ -95,8 +83,6 @@ pub const File = struct {
         entry_addr: ?u64,
         stack_size: ?u64,
         image_base: ?u64,
-        function_sections: bool,
-        data_sections: bool,
         eh_frame_hdr: bool,
         emit_relocs: bool,
         rdynamic: bool,
@@ -150,8 +136,6 @@ pub const File = struct {
 
         compatibility_version: ?std.SemanticVersion,
 
-        debug_format: ?DebugFormat,
-
         // TODO: remove this. libraries are resolved by the frontend.
         lib_dirs: []const []const u8,
         rpath_list: []const []const u8,
@@ -190,10 +174,29 @@ pub const File = struct {
     /// rewriting it. A malicious file is detected as incremental link failure
     /// and does not cause Illegal Behavior. This operation is not atomic.
     /// `arena` is used for allocations with the same lifetime as the created File.
-    pub fn open(arena: Allocator, options: OpenOptions) !*File {
-        switch (Tag.fromObjectFormat(options.comp.root_mod.resolved_target.result.ofmt)) {
+    pub fn open(
+        arena: Allocator,
+        comp: *Compilation,
+        emit: Compilation.Emit,
+        options: OpenOptions,
+    ) !*File {
+        switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
+            inline else => |tag| {
+                const ptr = try tag.Type().open(arena, comp, emit, options);
+                return &ptr.base;
+            },
+        }
+    }
+
+    pub fn createEmpty(
+        arena: Allocator,
+        comp: *Compilation,
+        emit: Compilation.Emit,
+        options: OpenOptions,
+    ) !*File {
+        switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
             inline else => |tag| {
-                const ptr = try tag.Type().open(arena, options);
+                const ptr = try tag.Type().createEmpty(arena, comp, emit, options);
                 return &ptr.base;
             },
         }
src/main.zig
@@ -892,7 +892,6 @@ fn buildOutputType(
     var contains_res_file: bool = false;
     var reference_trace: ?u32 = null;
     var pdb_out_path: ?[]const u8 = null;
-    var debug_format: ?link.File.DebugFormat = null;
     var error_limit: ?Module.ErrorInt = null;
     // These are before resolving sysroot.
     var lib_dir_args: std.ArrayListUnmanaged([]const u8) = .{};
@@ -1054,6 +1053,8 @@ fn buildOutputType(
                             create_module.opts.any_sanitize_thread = true;
                         if (mod_opts.unwind_tables == true)
                             create_module.opts.any_unwind_tables = true;
+                        if (mod_opts.strip == false)
+                            create_module.opts.any_non_stripped = true;
 
                         const root_src = try introspect.resolvePath(arena, root_src_orig);
                         try create_module.modules.put(arena, mod_name, .{
@@ -1480,9 +1481,9 @@ fn buildOutputType(
                     } else if (mem.eql(u8, arg, "-fno-strip")) {
                         mod_opts.strip = false;
                     } else if (mem.eql(u8, arg, "-gdwarf32")) {
-                        debug_format = .{ .dwarf = .@"32" };
+                        create_module.opts.debug_format = .{ .dwarf = .@"32" };
                     } else if (mem.eql(u8, arg, "-gdwarf64")) {
-                        debug_format = .{ .dwarf = .@"64" };
+                        create_module.opts.debug_format = .{ .dwarf = .@"64" };
                     } else if (mem.eql(u8, arg, "-fformatted-panics")) {
                         formatted_panics = true;
                     } else if (mem.eql(u8, arg, "-fno-formatted-panics")) {
@@ -1989,11 +1990,11 @@ fn buildOutputType(
                     },
                     .gdwarf32 => {
                         mod_opts.strip = false;
-                        debug_format = .{ .dwarf = .@"32" };
+                        create_module.opts.debug_format = .{ .dwarf = .@"32" };
                     },
                     .gdwarf64 => {
                         mod_opts.strip = false;
-                        debug_format = .{ .dwarf = .@"64" };
+                        create_module.opts.debug_format = .{ .dwarf = .@"64" };
                     },
                     .sanitize => {
                         if (mem.eql(u8, it.only_arg, "undefined")) {
@@ -2532,6 +2533,8 @@ fn buildOutputType(
             create_module.opts.any_sanitize_thread = true;
         if (mod_opts.unwind_tables == true)
             create_module.opts.any_unwind_tables = true;
+        if (mod_opts.strip == false)
+            create_module.opts.any_non_stripped = true;
 
         const src_path = try introspect.resolvePath(arena, unresolved_src_path);
         try create_module.modules.put(arena, "main", .{
@@ -3359,7 +3362,6 @@ fn buildOutputType(
         .emit_docs = emit_docs_resolved.data,
         .emit_implib = emit_implib_resolved.data,
         .dll_export_fns = dll_export_fns,
-        .keep_source_files_loaded = false,
         .lib_dirs = lib_dirs.items,
         .rpath_list = rpath_list.items,
         .symbol_wrap_set = symbol_wrap_set,
@@ -3443,7 +3445,6 @@ fn buildOutputType(
         .test_runner_path = test_runner_path,
         .disable_lld_caching = !output_to_cache,
         .subsystem = subsystem,
-        .debug_format = debug_format,
         .debug_compile_errors = debug_compile_errors,
         .enable_link_snapshots = enable_link_snapshots,
         .install_name = install_name,
@@ -3484,7 +3485,9 @@ fn buildOutputType(
     defer if (!comp_destroyed) comp.destroy();
 
     if (show_builtin) {
-        return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
+        const builtin_mod = comp.root_mod.deps.get("builtin").?;
+        const source = builtin_mod.builtin_file.?.source;
+        return std.io.getStdOut().writeAll(source);
     }
     switch (listen) {
         .none => {},
@@ -3737,6 +3740,7 @@ fn createModule(
         const resolved_target = cli_mod.inherited.resolved_target.?;
         create_module.opts.resolved_target = resolved_target;
         create_module.opts.root_optimize_mode = cli_mod.inherited.optimize_mode;
+        create_module.opts.root_strip = cli_mod.inherited.strip;
         const target = resolved_target.result;
 
         // First, remove libc, libc++, and compiler_rt libraries from the system libraries list.
@@ -4366,12 +4370,12 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
 
     const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.root_name});
 
-    var man: Cache.Manifest = comp.obtainCObjectCacheManifest();
+    var man: Cache.Manifest = comp.obtainCObjectCacheManifest(comp.root_mod);
     man.want_shared_lock = false;
     defer man.deinit();
 
     man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
-    man.hash.add(comp.c_frontend);
+    man.hash.add(comp.config.c_frontend);
     Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| {
         fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) });
     };
@@ -4380,14 +4384,14 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
     const digest = if (try man.hit()) man.final() else digest: {
         if (fancy_output) |p| p.cache_hit = false;
         var argv = std.ArrayList([]const u8).init(arena);
-        try argv.append(@tagName(comp.c_frontend)); // argv[0] is program name, actual args start at [1]
+        try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1]
 
         var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
         defer zig_cache_tmp_dir.close();
 
         const ext = Compilation.classifyFileExt(c_source_file.src_path);
         const out_dep_path: ?[]const u8 = blk: {
-            if (comp.c_frontend == .aro or comp.disable_c_depfile or !ext.clangSupportsDepFile())
+            if (comp.config.c_frontend == .aro or comp.disable_c_depfile or !ext.clangSupportsDepFile())
                 break :blk null;
 
             const c_src_basename = fs.path.basename(c_source_file.src_path);
@@ -4397,14 +4401,15 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
         };
 
         // TODO
-        if (comp.c_frontend != .aro) try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path);
+        if (comp.config.c_frontend != .aro)
+            try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path, comp.root_mod);
         try argv.append(c_source_file.src_path);
 
         if (comp.verbose_cc) {
             Compilation.dump_argv(argv.items);
         }
 
-        var tree = switch (comp.c_frontend) {
+        var tree = switch (comp.config.c_frontend) {
             .aro => tree: {
                 const aro = @import("aro");
                 const translate_c = @import("aro_translate_c.zig");
src/Module.zig
@@ -152,8 +152,6 @@ stage1_flags: packed struct {
     reserved: u2 = 0,
 } = .{},
 
-job_queued_update_builtin_zig: bool = true,
-
 compile_log_text: ArrayListUnmanaged(u8) = .{},
 
 emit_h: ?*GlobalEmitH,
@@ -2490,7 +2488,6 @@ pub fn deinit(mod: *Module) void {
 
     mod.compile_log_text.deinit(gpa);
 
-    mod.zig_cache_artifact_directory.handle.close();
     mod.local_zir_cache.handle.close();
     mod.global_zir_cache.handle.close();
 
@@ -3075,72 +3072,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
     }
 }
 
-pub fn populateBuiltinFile(mod: *Module) !void {
-    const tracy = trace(@src());
-    defer tracy.end();
-
-    const comp = mod.comp;
-    const builtin_mod, const file = blk: {
-        comp.mutex.lock();
-        defer comp.mutex.unlock();
-
-        const builtin_mod = mod.main_mod.deps.get("builtin").?;
-        const result = try mod.importPkg(builtin_mod);
-        break :blk .{ builtin_mod, result.file };
-    };
-    const gpa = mod.gpa;
-    file.source = try comp.generateBuiltinZigSource(gpa);
-    file.source_loaded = true;
-
-    if (builtin_mod.root.statFile(builtin_mod.root_src_path)) |stat| {
-        if (stat.size != file.source.len) {
-            log.warn(
-                "the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++
-                    "Overwriting with correct file contents now",
-                .{ builtin_mod.root, builtin_mod.root_src_path, file.source.len, stat.size },
-            );
-
-            try writeBuiltinFile(file, builtin_mod);
-        } else {
-            file.stat = .{
-                .size = stat.size,
-                .inode = stat.inode,
-                .mtime = stat.mtime,
-            };
-        }
-    } else |err| switch (err) {
-        error.BadPathName => unreachable, // it's always "builtin.zig"
-        error.NameTooLong => unreachable, // it's always "builtin.zig"
-        error.PipeBusy => unreachable, // it's not a pipe
-        error.WouldBlock => unreachable, // not asking for non-blocking I/O
-
-        error.FileNotFound => try writeBuiltinFile(file, builtin_mod),
-
-        else => |e| return e,
-    }
-
-    file.tree = try Ast.parse(gpa, file.source, .zig);
-    file.tree_loaded = true;
-    assert(file.tree.errors.len == 0); // builtin.zig must parse
-
-    file.zir = try AstGen.generate(gpa, file.tree);
-    file.zir_loaded = true;
-    file.status = .success_zir;
-}
-
-fn writeBuiltinFile(file: *File, builtin_mod: *Package.Module) !void {
-    var af = try builtin_mod.root.atomicFile(builtin_mod.root_src_path, .{});
-    defer af.deinit();
-    try af.file.writeAll(file.source);
-    try af.finish();
-
-    file.stat = .{
-        .size = file.source.len,
-        .inode = 0, // dummy value
-        .mtime = 0, // dummy value
-    };
-}
-
 pub fn mapOldZirToNew(
     gpa: Allocator,
     old_zir: Zir,
src/Sema.zig
@@ -784,6 +784,11 @@ pub const Block = struct {
         }
     }
 
+    pub fn ownerModule(block: Block) *Package.Module {
+        const zcu = block.sema.mod;
+        return zcu.namespacePtr(block.namespace).file_scope.mod;
+    }
+
     pub fn startAnonDecl(block: *Block) !WipAnonDecl {
         return WipAnonDecl{
             .block = block,
@@ -5733,7 +5738,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
     // Ignore the result, all the relevant operations have written to c_import_buf already.
     _ = try sema.analyzeBodyBreak(&child_block, body);
 
-    var c_import_res = comp.cImport(c_import_buf.items) catch |err|
+    var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err|
         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
     defer c_import_res.deinit(gpa);