diff --git a/src/Compilation.zig b/src/Compilation.zig index 6acbe0ac39..71dff4a442 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3495,7 +3495,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v .module = module, .error_msg = null, .decl_index = decl_index.toOptional(), - .decl = decl, + .is_naked_fn = false, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = .{}, }; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a442d4bcbe..97f897b344 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -522,8 +522,8 @@ pub const Object = struct { pub const DeclGen = struct { gpa: mem.Allocator, module: *Module, - decl: ?*Decl, decl_index: Decl.OptionalIndex, + is_naked_fn: bool, /// This is a borrowed reference from `link.C`. fwd_decl: std.ArrayList(u8), error_msg: ?*Module.ErrorMsg, @@ -532,8 +532,10 @@ pub const DeclGen = struct { fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { @setCold(true); const mod = dg.module; + const decl_index = dg.decl_index.unwrap().?; + const decl = mod.declPtr(decl_index); const src = LazySrcLoc.nodeOffset(0); - const src_loc = src.toSrcLoc(dg.decl.?, mod); + const src_loc = src.toSrcLoc(decl, mod); dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args); return error.AnalysisFail; } @@ -2493,8 +2495,8 @@ fn genExports(o: *Object) !void { const mod = o.dg.module; const ip = &mod.intern_pool; - const decl = o.dg.decl.?; const decl_index = o.dg.decl_index.unwrap().?; + const decl = mod.declPtr(decl_index); const tv: TypedValue = .{ .ty = decl.ty, .val = (try decl.internValue(mod)).toValue() }; const fwd = o.dg.fwd_decl.writer(); @@ -2634,9 +2636,10 @@ pub fn genFunc(f: *Function) !void { const mod = o.dg.module; const gpa = o.dg.gpa; const decl_index = o.dg.decl_index.unwrap().?; + const decl = mod.declPtr(decl_index); const tv: TypedValue = .{ - .ty = o.dg.decl.?.ty, - .val = o.dg.decl.?.val, + .ty = decl.ty, + .val = decl.val, }; o.code_header = std.ArrayList(u8).init(gpa); @@ -2719,19 +2722,20 @@ pub fn genDecl(o: *Object) !void { defer tracy.end(); const mod = o.dg.module; - const decl = o.dg.decl.?; - const decl_c_value = .{ .decl = o.dg.decl_index.unwrap().? }; + const decl_index = o.dg.decl_index.unwrap().?; + const decl_c_value = .{ .decl = decl_index }; + const decl = mod.declPtr(decl_index); const tv: TypedValue = .{ .ty = decl.ty, .val = (try decl.internValue(mod)).toValue() }; if (!tv.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) return; if (tv.val.getExternFunc(mod)) |_| { const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll("zig_extern "); - try o.dg.renderFunctionSignature(fwd_decl_writer, decl_c_value.decl, .forward, .{ .export_index = 0 }); + try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 }); try fwd_decl_writer.writeAll(";\n"); try genExports(o); } else if (tv.val.getVariable(mod)) |variable| { - try o.dg.renderFwdDecl(decl_c_value.decl, variable); + try o.dg.renderFwdDecl(decl_index, variable); try genExports(o); if (variable.is_extern) return; @@ -2750,7 +2754,7 @@ pub fn genDecl(o: *Object) !void { try w.writeByte(';'); try o.indent_writer.insertNewline(); } else { - const is_global = o.dg.module.decl_exports.contains(decl_c_value.decl); + const is_global = o.dg.module.decl_exports.contains(decl_index); const fwd_decl_writer = o.dg.fwd_decl.writer(); try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); @@ -2773,12 +2777,14 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { const tracy = trace(@src()); defer tracy.end(); + const mod = dg.module; + const decl_index = dg.decl_index.unwrap().?; + const decl = mod.declPtr(decl_index); const tv: TypedValue = .{ - .ty = dg.decl.?.ty, - .val = dg.decl.?.val, + .ty = decl.ty, + .val = decl.val, }; const writer = dg.fwd_decl.writer(); - const mod = dg.module; switch (tv.ty.zigTypeTag(mod)) { .Fn => { @@ -3504,8 +3510,7 @@ fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { } else { try reap(f, inst, &.{un_op}); // Not even allowed to return void in a naked function. - if (if (f.object.dg.decl) |decl| decl.ty.fnCallingConvention(mod) != .Naked else true) - try writer.writeAll("return;\n"); + if (!f.object.dg.is_naked_fn) try writer.writeAll("return;\n"); } return .none; } @@ -4144,7 +4149,7 @@ fn airCall( ) !CValue { const mod = f.object.dg.module; // Not even allowed to call panic in a naked function. - if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention(mod) == .Naked) return .none; + if (f.object.dg.is_naked_fn) return .none; const gpa = f.object.dg.gpa; const writer = f.object.writer(); @@ -4637,9 +4642,8 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !Loca } fn airTrap(f: *Function, writer: anytype) !CValue { - const mod = f.object.dg.module; // Not even allowed to call trap in a naked function. - if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention(mod) == .Naked) return .none; + if (f.object.dg.is_naked_fn) return .none; try writer.writeAll("zig_trap();\n"); return .none; @@ -4682,9 +4686,8 @@ fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { } fn airUnreach(f: *Function) !CValue { - const mod = f.object.dg.module; // Not even allowed to call unreachable in a naked function. - if (f.object.dg.decl) |decl| if (decl.ty.fnCallingConvention(mod) == .Naked) return .none; + if (f.object.dg.is_naked_fn) return .none; try f.object.writer().writeAll("zig_unreachable();\n"); return .none; @@ -7194,8 +7197,11 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue { } fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue { + const mod = f.object.dg.module; const inst_ty = f.typeOfIndex(inst); - const fn_cty = try f.typeToCType(f.object.dg.decl.?.ty, .complete); + const decl_index = f.object.dg.decl_index.unwrap().?; + const decl = mod.declPtr(decl_index); + const fn_cty = try f.typeToCType(decl.ty, .complete); const param_len = fn_cty.castTag(.varargs_function).?.data.param_types.len; const writer = f.object.writer(); diff --git a/src/link/C.zig b/src/link/C.zig index 8ffe33bb09..edb114d07d 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -139,6 +139,7 @@ pub fn updateFunc(self: *C, module: *Module, func_index: InternPool.Index, air: const func = module.funcInfo(func_index); const decl_index = func.owner_decl; + const decl = module.declPtr(decl_index); const gop = try self.decl_table.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; @@ -163,7 +164,7 @@ pub fn updateFunc(self: *C, module: *Module, func_index: InternPool.Index, air: .module = module, .error_msg = null, .decl_index = decl_index.toOptional(), - .decl = module.declPtr(decl_index), + .is_naked_fn = decl.ty.fnCallingConvention(module) == .Naked, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, }, @@ -216,15 +217,13 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi fwd_decl.clearRetainingCapacity(); code.clearRetainingCapacity(); - const decl = module.declPtr(decl_index); - var object: codegen.Object = .{ .dg = .{ .gpa = gpa, .module = module, .error_msg = null, .decl_index = decl_index.toOptional(), - .decl = decl, + .is_naked_fn = false, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, }, @@ -322,29 +321,19 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo self.lazy_code_buf.clearRetainingCapacity(); try self.flushErrDecls(&f.lazy_ctypes); + // Unlike other backends, the .c code we are emitting has order-dependent decls. // `CType`s, forward decls, and non-functions first. - // Unlike other backends, the .c code we are emitting is order-dependent. Therefore - // we must traverse the set of Decls that we are emitting according to their dependencies. - // Our strategy is to populate a set of remaining decls, pop Decls one by one, - // recursively chasing their dependencies. - try f.remaining_decls.ensureUnusedCapacity(gpa, self.decl_table.count()); - - const decl_keys = self.decl_table.keys(); - const decl_values = self.decl_table.values(); - for (decl_keys) |decl_index| { - assert(module.declPtr(decl_index).has_tv); - f.remaining_decls.putAssumeCapacityNoClobber(decl_index, {}); - } { var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; defer export_names.deinit(gpa); - try export_names.ensureTotalCapacity(gpa, @as(u32, @intCast(module.decl_exports.entries.len))); + try export_names.ensureTotalCapacity(gpa, @intCast(module.decl_exports.entries.len)); for (module.decl_exports.values()) |exports| for (exports.items) |@"export"| try export_names.put(gpa, @"export".opts.name, {}); - while (f.remaining_decls.popOrNull()) |kv| { - const decl_index = kv.key; + const decl_keys = self.decl_table.keys(); + for (decl_keys) |decl_index| { + assert(module.declPtr(decl_index).has_tv); try self.flushDecl(&f, decl_index, export_names); } } @@ -355,9 +344,9 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo assert(f.ctypes.count() == 0); try self.flushCTypes(&f, .none, f.lazy_ctypes); - var it = self.decl_table.iterator(); - while (it.next()) |entry| - try self.flushCTypes(&f, entry.key_ptr.toOptional(), entry.value_ptr.ctypes); + for (self.decl_table.keys(), self.decl_table.values()) |decl_index, db| { + try self.flushCTypes(&f, decl_index.toOptional(), db.ctypes); + } } f.all_buffers.items[ctypes_index] = .{ @@ -374,6 +363,7 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo f.file_size += lazy_fwd_decl_len; // Now the code. + const decl_values = self.decl_table.values(); try f.all_buffers.ensureUnusedCapacity(gpa, 1 + decl_values.len); f.appendBufAssumeCapacity(self.lazy_code_buf.items); for (decl_values) |decl| f.appendBufAssumeCapacity(self.getString(decl.code)); @@ -384,8 +374,6 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo } const Flush = struct { - remaining_decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, void) = .{}, - ctypes: codegen.CType.Store = .{}, ctypes_map: std.ArrayListUnmanaged(codegen.CType.Index) = .{}, ctypes_buf: std.ArrayListUnmanaged(u8) = .{}, @@ -416,7 +404,6 @@ const Flush = struct { f.ctypes_buf.deinit(gpa); f.ctypes_map.deinit(gpa); f.ctypes.deinit(gpa); - f.remaining_decls.deinit(gpa); } }; @@ -522,7 +509,7 @@ fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void { .module = self.base.options.module.?, .error_msg = null, .decl_index = .none, - .decl = null, + .is_naked_fn = false, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, }, @@ -556,7 +543,7 @@ fn flushLazyFn(self: *C, ctypes: *codegen.CType.Store, lazy_fn: codegen.LazyFnMa .module = self.base.options.module.?, .error_msg = null, .decl_index = .none, - .decl = null, + .is_naked_fn = false, .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, }, @@ -591,7 +578,6 @@ fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError } } -/// Assumes `decl` was in the `remaining_decls` set, and has already been removed. fn flushDecl( self: *C, f: *Flush, @@ -601,14 +587,6 @@ fn flushDecl( const gpa = self.base.allocator; const mod = self.base.options.module.?; const decl = mod.declPtr(decl_index); - // Before flushing any particular Decl we must ensure its - // dependencies are already flushed, so that the order in the .c - // file comes out correctly. - for (decl.dependencies.keys()) |dep| { - if (f.remaining_decls.swapRemove(dep)) { - try flushDecl(self, f, dep, export_names); - } - } const decl_block = self.decl_table.getPtr(decl_index).?;