From 26a94e8481385619ae049143dd67e551f333fa3f Mon Sep 17 00:00:00 2001 From: mlugg Date: Tue, 26 Mar 2024 04:06:39 +0000 Subject: [PATCH] Zcu: eliminate `Decl.alive` field Legacy anon decls now have three uses: * Type owner decls * Function owner decls * `@export` and `@extern` Therefore, there are no longer any cases where we wish to explicitly omit legacy anon decls from the binary. This means we can remove the concept of an "alive" vs "dead" `Decl`, which also allows us to remove the separate `anon_work_queue` in `Compilation`. --- src/Compilation.zig | 19 +---------- src/InternPool.zig | 1 - src/Module.zig | 64 +------------------------------------ src/Sema.zig | 2 -- src/arch/wasm/CodeGen.zig | 2 -- src/arch/x86_64/CodeGen.zig | 2 -- src/codegen.zig | 4 --- src/codegen/c.zig | 1 - src/codegen/llvm.zig | 7 ---- src/codegen/spirv.zig | 1 - 10 files changed, 2 insertions(+), 101 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index b95a50cc59..5bbca51ede 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -102,7 +102,6 @@ link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{}, lld_errors: std.ArrayListUnmanaged(LldError) = .{}, work_queue: std.fifo.LinearFifo(Job, .Dynamic), -anon_work_queue: std.fifo.LinearFifo(Job, .Dynamic), /// These jobs are to invoke the Clang compiler to create an object file, which /// gets linked with the Compilation. @@ -1417,7 +1416,6 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .emit_llvm_ir = options.emit_llvm_ir, .emit_llvm_bc = options.emit_llvm_bc, .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), - .anon_work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), .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), @@ -1840,7 +1838,6 @@ pub fn destroy(comp: *Compilation) void { if (comp.module) |zcu| zcu.deinit(); comp.cache_use.deinit(); comp.work_queue.deinit(); - comp.anon_work_queue.deinit(); comp.c_object_work_queue.deinit(); if (!build_options.only_core_functionality) { comp.win32_resource_work_queue.deinit(); @@ -3354,18 +3351,11 @@ pub fn performAllTheWork( mod.sema_prog_node = undefined; }; - // In this main loop we give priority to non-anonymous Decls in the work queue, so - // that they can establish references to anonymous Decls, setting alive=true in the - // backend, preventing anonymous Decls from being prematurely destroyed. while (true) { if (comp.work_queue.readItem()) |work_item| { try processOneJob(comp, work_item, main_progress_node); continue; } - if (comp.anon_work_queue.readItem()) |work_item| { - try processOneJob(comp, work_item, main_progress_node); - continue; - } if (comp.module) |zcu| { // If there's no work queued, check if there's anything outdated // which we need to work on, and queue it if so. @@ -3413,14 +3403,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v assert(decl.has_tv); - if (decl.alive) { - try module.linkerUpdateDecl(decl_index); - return; - } - - // Instead of sending this decl to the linker, we actually will delete it - // because we found out that it in fact was never referenced. - module.deleteUnusedDecl(decl_index); + try module.linkerUpdateDecl(decl_index); return; }, } diff --git a/src/InternPool.zig b/src/InternPool.zig index e4132e577e..63d29d3760 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -6740,7 +6740,6 @@ fn finishFuncInstance( .zir_decl_index = fn_owner_decl.zir_decl_index, .is_pub = fn_owner_decl.is_pub, .is_exported = fn_owner_decl.is_exported, - .alive = true, .kind = .anon, }); errdefer ip.destroyDecl(gpa, decl_index); diff --git a/src/Module.zig b/src/Module.zig index 4391472fa5..b3abefb576 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -394,15 +394,6 @@ pub const Decl = struct { is_pub: bool, /// Whether the corresponding AST decl has a `export` keyword. is_exported: bool, - /// Flag used by garbage collection to mark and sweep. - /// Decls which correspond to an AST node always have this field set to `true`. - /// Anonymous Decls are initialized with this field set to `false` and then it - /// is the responsibility of machine code backends to mark it `true` whenever - /// a `decl_ref` Value is encountered that points to this Decl. - /// When the `codegen_decl` job is encountered in the main work queue, if the - /// Decl is marked alive, then it sends the Decl to the linker. Otherwise it - /// deletes the Decl on the spot. - alive: bool, /// If true `name` is already fully qualified. name_fully_qualified: bool = false, /// What kind of a declaration is this. @@ -3525,7 +3516,6 @@ fn semaFile(mod: *Module, file: *File) SemaError!void { new_decl.is_exported = false; new_decl.alignment = .none; new_decl.@"linksection" = .none; - new_decl.alive = true; // This Decl corresponds to a File and is therefore always alive. new_decl.analysis = .in_progress; if (file.status != .success_zir) { @@ -4375,7 +4365,6 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void const decl = zcu.declPtr(decl_index); const was_exported = decl.is_exported; assert(decl.kind == kind); // ZIR tracking should preserve this - assert(decl.alive); decl.name = decl_name; decl.src_node = decl_node; decl.src_line = line; @@ -4392,7 +4381,6 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void new_decl.is_pub = declaration.flags.is_pub; new_decl.is_exported = declaration.flags.is_export; new_decl.zir_decl_index = tracked_inst.toOptional(); - new_decl.alive = true; // This Decl corresponds to an AST node and is therefore always alive. break :decl_index .{ false, new_decl_index }; }; @@ -4470,12 +4458,8 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void { /// Finalize the creation of an anon decl. pub fn finalizeAnonDecl(mod: *Module, decl_index: Decl.Index) Allocator.Error!void { - // The Decl starts off with alive=false and the codegen backend will set alive=true - // if the Decl is referenced by an instruction or another constant. Otherwise, - // the Decl will be garbage collected by the `codegen_decl` task instead of sent - // to the linker. if (mod.declPtr(decl_index).typeOf(mod).isFnOrHasRuntimeBits(mod)) { - try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = decl_index }); + try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); } } @@ -4815,7 +4799,6 @@ pub fn allocateNewDecl( .zir_decl_index = .none, .is_pub = false, .is_exported = false, - .alive = false, .kind = .anon, }); @@ -5582,51 +5565,6 @@ fn reportRetryableFileError( gop.value_ptr.* = err_msg; } -pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void { - switch (mod.intern_pool.indexToKey(val.toIntern())) { - .variable => |variable| try mod.markDeclIndexAlive(variable.decl), - .extern_func => |extern_func| try mod.markDeclIndexAlive(extern_func.decl), - .func => |func| try mod.markDeclIndexAlive(func.owner_decl), - .error_union => |error_union| switch (error_union.val) { - .err_name => {}, - .payload => |payload| try mod.markReferencedDeclsAlive(Value.fromInterned(payload)), - }, - .slice => |slice| { - try mod.markReferencedDeclsAlive(Value.fromInterned(slice.ptr)); - try mod.markReferencedDeclsAlive(Value.fromInterned(slice.len)); - }, - .ptr => |ptr| switch (ptr.addr) { - .decl => |decl| try mod.markDeclIndexAlive(decl), - .anon_decl => {}, - .int, .comptime_field, .comptime_alloc => {}, - .eu_payload, .opt_payload => |parent| try mod.markReferencedDeclsAlive(Value.fromInterned(parent)), - .elem, .field => |base_index| try mod.markReferencedDeclsAlive(Value.fromInterned(base_index.base)), - }, - .opt => |opt| if (opt.val != .none) try mod.markReferencedDeclsAlive(Value.fromInterned(opt.val)), - .aggregate => |aggregate| for (aggregate.storage.values()) |elem| - try mod.markReferencedDeclsAlive(Value.fromInterned(elem)), - .un => |un| { - if (un.tag != .none) try mod.markReferencedDeclsAlive(Value.fromInterned(un.tag)); - try mod.markReferencedDeclsAlive(Value.fromInterned(un.val)); - }, - else => {}, - } -} - -pub fn markDeclAlive(mod: *Module, decl: *Decl) Allocator.Error!void { - if (decl.alive) return; - decl.alive = true; - - // This is the first time we are marking this Decl alive. We must - // therefore recurse into its value and mark any Decl it references - // as also alive, so that any Decl referenced does not get garbage collected. - try mod.markReferencedDeclsAlive(decl.val); -} - -fn markDeclIndexAlive(mod: *Module, decl_index: Decl.Index) Allocator.Error!void { - return mod.markDeclAlive(mod.declPtr(decl_index)); -} - pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void { const gop = try mod.global_assembly.getOrPut(mod.gpa, decl_index); if (gop.found_existing) { diff --git a/src/Sema.zig b/src/Sema.zig index c0b78c3e1d..8dfd0248f5 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6445,8 +6445,6 @@ pub fn analyzeExport( return sema.fail(block, src, "export target cannot be extern", .{}); } - // This decl is alive no matter what, since it's being exported - try mod.markDeclAlive(exported_decl); try sema.maybeQueueFuncBodyAnalysis(exported_decl_index); try addExport(mod, .{ diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 11c95e318a..be7f4ab66a 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3121,7 +3121,6 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: InternPool.DeclIndex, offset: u32) InnerError!WValue { const mod = func.bin_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - try mod.markDeclAlive(decl); const ptr_ty = try mod.singleMutPtrType(decl.typeOf(mod)); return func.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index, offset); } @@ -3178,7 +3177,6 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: InternPool.Decl return WValue{ .imm32 = 0xaaaaaaaa }; } - try mod.markDeclAlive(decl); const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index); const atom = func.bin_file.getAtom(atom_index); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 8fe19d4e21..c5b815b429 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -12263,7 +12263,6 @@ fn genCall(self: *Self, info: union(enum) { }, }) { .func => |func| { - try mod.markDeclAlive(mod.declPtr(func.owner_decl)); if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); const sym = elf_file.symbol(sym_index); @@ -12323,7 +12322,6 @@ fn genCall(self: *Self, info: union(enum) { }, .extern_func => |extern_func| { const owner_decl = mod.declPtr(extern_func.decl); - try mod.markDeclAlive(owner_decl); const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name); const decl_name = mod.intern_pool.stringToSlice(owner_decl.name); try self.genExternSymbolRef(.call, lib_name, decl_name); diff --git a/src/codegen.zig b/src/codegen.zig index 818284a8f0..48a7f840b2 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -835,8 +835,6 @@ fn lowerDeclRef( return Result.ok; } - try zcu.markDeclAlive(decl); - const vaddr = try lf.getDeclVAddr(decl_index, .{ .parent_atom_index = reloc_info.parent_atom_index, .offset = code.items.len, @@ -958,8 +956,6 @@ fn genDeclRef( } } - try zcu.markDeclAlive(decl); - const decl_namespace = zcu.namespacePtr(decl.src_namespace); const single_threaded = decl_namespace.file_scope.mod.single_threaded; const is_threadlocal = tv.val.isPtrToThreadLocal(zcu) and !single_threaded; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index d1575feaba..ed506cfdfe 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2010,7 +2010,6 @@ pub const DeclGen = struct { fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: InternPool.DeclIndex, export_index: u32) !void { const mod = dg.module; const decl = mod.declPtr(decl_index); - try mod.markDeclAlive(decl); if (mod.decl_exports.get(decl_index)) |exports| { try writer.print("{ }", .{ diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 7bbe4e715a..421c767bd8 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3722,15 +3722,11 @@ pub const Object = struct { => unreachable, // non-runtime values .extern_func => |extern_func| { const fn_decl_index = extern_func.decl; - const fn_decl = mod.declPtr(fn_decl_index); - try mod.markDeclAlive(fn_decl); const function_index = try o.resolveLlvmFunction(fn_decl_index); return function_index.ptrConst(&o.builder).global.toConst(); }, .func => |func| { const fn_decl_index = func.owner_decl; - const fn_decl = mod.declPtr(fn_decl_index); - try mod.markDeclAlive(fn_decl); const function_index = try o.resolveLlvmFunction(fn_decl_index); return function_index.ptrConst(&o.builder).global.toConst(); }, @@ -4262,7 +4258,6 @@ pub const Object = struct { fn lowerParentPtrDecl(o: *Object, decl_index: InternPool.DeclIndex) Allocator.Error!Builder.Constant { const mod = o.module; const decl = mod.declPtr(decl_index); - try mod.markDeclAlive(decl); const ptr_ty = try mod.singleMutPtrType(decl.typeOf(mod)); return o.lowerDeclRefValue(ptr_ty, decl_index); } @@ -4455,8 +4450,6 @@ pub const Object = struct { if ((!is_fn_body and !decl_ty.hasRuntimeBits(mod)) or (is_fn_body and mod.typeToFunc(decl_ty).?.is_generic)) return o.lowerPtrToVoid(ty); - try mod.markDeclAlive(decl); - const llvm_global = if (is_fn_body) (try o.resolveLlvmFunction(decl_index)).ptrConst(&o.builder).global else diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 17b44806e2..3e560b0918 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -255,7 +255,6 @@ pub const Object = struct { pub fn resolveDecl(self: *Object, mod: *Module, decl_index: InternPool.DeclIndex) !SpvModule.Decl.Index { const decl = mod.declPtr(decl_index); assert(decl.has_tv); // TODO: Do we need to handle a situation where this is false? - try mod.markDeclAlive(decl); const entry = try self.decl_link.getOrPut(self.gpa, decl_index); if (!entry.found_existing) {