diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2025-06-12 20:46:36 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-12 20:46:36 -0400 |
| commit | dcdb4422b801f2d184107fdd7b9493f7840a0244 (patch) | |
| tree | ca7a37c544382c10e45fbad68ea7701a05d0543c /src/codegen/c.zig | |
| parent | 5e3c0b7af7cd866f5464c244b9775e488b93ae48 (diff) | |
| parent | 43d01ff69f6c6c46bef81dd4de2c78fb0a942b65 (diff) | |
| download | zig-dcdb4422b801f2d184107fdd7b9493f7840a0244.tar.gz zig-dcdb4422b801f2d184107fdd7b9493f7840a0244.zip | |
Merge pull request #24124 from mlugg/better-backend-pipeline-2
compiler: threaded codegen (and more goodies)
Diffstat (limited to 'src/codegen/c.zig')
| -rw-r--r-- | src/codegen/c.zig | 146 |
1 files changed, 123 insertions, 23 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 3b8ab52982..f4952d4a58 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const assert = std.debug.assert; const mem = std.mem; const log = std.log.scoped(.c); +const Allocator = mem.Allocator; const dev = @import("../dev.zig"); const link = @import("../link.zig"); @@ -30,6 +31,35 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features { }) else null; // we don't currently ask zig1 to use safe optimization modes } +/// For most backends, MIR is basically a sequence of machine code instructions, perhaps with some +/// "pseudo instructions" thrown in. For the C backend, it is instead the generated C code for a +/// single function. We also need to track some information to get merged into the global `link.C` +/// state, including: +/// * The UAVs used, so declarations can be emitted in `flush` +/// * The types used, so declarations can be emitted in `flush` +/// * The lazy functions used, so definitions can be emitted in `flush` +pub const Mir = struct { + /// This map contains all the UAVs we saw generating this function. + /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields. + /// Key is the value of the UAV; value is the UAV's alignment, or + /// `.none` for natural alignment. The specified alignment is never + /// less than the natural alignment. + uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), + // These remaining fields are essentially just an owned version of `link.C.AvBlock`. + code: []u8, + fwd_decl: []u8, + ctype_pool: CType.Pool, + lazy_fns: LazyFnMap, + + pub fn deinit(mir: *Mir, gpa: Allocator) void { + mir.uavs.deinit(gpa); + gpa.free(mir.code); + gpa.free(mir.fwd_decl); + mir.ctype_pool.deinit(gpa); + mir.lazy_fns.deinit(gpa); + } +}; + pub const CType = @import("c/Type.zig"); pub const CValue = union(enum) { @@ -671,7 +701,7 @@ pub const Object = struct { /// This data is available both when outputting .c code and when outputting an .h file. pub const DeclGen = struct { - gpa: mem.Allocator, + gpa: Allocator, pt: Zcu.PerThread, mod: *Module, pass: Pass, @@ -682,10 +712,12 @@ pub const DeclGen = struct { error_msg: ?*Zcu.ErrorMsg, ctype_pool: CType.Pool, scratch: std.ArrayListUnmanaged(u32), - /// Keeps track of anonymous decls that need to be rendered before this - /// (named) Decl in the output C code. - uav_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, C.AvBlock), - aligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), + /// This map contains all the UAVs we saw generating this function. + /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields. + /// Key is the value of the UAV; value is the UAV's alignment, or + /// `.none` for natural alignment. The specified alignment is never + /// less than the natural alignment. + uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), pub const Pass = union(enum) { nav: InternPool.Nav.Index, @@ -753,21 +785,17 @@ pub const DeclGen = struct { // Indicate that the anon decl should be rendered to the output so that // our reference above is not undefined. const ptr_type = ip.indexToKey(uav.orig_ty).ptr_type; - const gop = try dg.uav_deps.getOrPut(dg.gpa, uav.val); - if (!gop.found_existing) gop.value_ptr.* = .{}; - - // Only insert an alignment entry if the alignment is greater than ABI - // alignment. If there is already an entry, keep the greater alignment. - const explicit_alignment = ptr_type.flags.alignment; - if (explicit_alignment != .none) { - const abi_alignment = Type.fromInterned(ptr_type.child).abiAlignment(zcu); - if (explicit_alignment.order(abi_alignment).compare(.gt)) { - const aligned_gop = try dg.aligned_uavs.getOrPut(dg.gpa, uav.val); - aligned_gop.value_ptr.* = if (aligned_gop.found_existing) - aligned_gop.value_ptr.maxStrict(explicit_alignment) - else - explicit_alignment; - } + const gop = try dg.uavs.getOrPut(dg.gpa, uav.val); + if (!gop.found_existing) gop.value_ptr.* = .none; + // If there is an explicit alignment, greater than the current one, use it. + // Note that we intentionally start at `.none`, so `gop.value_ptr.*` is never + // underaligned, so we don't need to worry about the `.none` case here. + if (ptr_type.flags.alignment != .none) { + // Resolve the current alignment so we can choose the bigger one. + const cur_alignment: Alignment = if (gop.value_ptr.* == .none) abi: { + break :abi Type.fromInterned(ptr_type.child).abiAlignment(zcu); + } else gop.value_ptr.*; + gop.value_ptr.* = cur_alignment.maxStrict(ptr_type.flags.alignment); } } @@ -2895,7 +2923,79 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn } } -pub fn genFunc(f: *Function) !void { +pub fn generate( + lf: *link.File, + pt: Zcu.PerThread, + src_loc: Zcu.LazySrcLoc, + func_index: InternPool.Index, + air: *const Air, + liveness: *const Air.Liveness, +) @import("../codegen.zig").CodeGenError!Mir { + const zcu = pt.zcu; + const gpa = zcu.gpa; + + _ = src_loc; + assert(lf.tag == .c); + + const func = zcu.funcInfo(func_index); + + var function: Function = .{ + .value_map = .init(gpa), + .air = air.*, + .liveness = liveness.*, + .func_index = func_index, + .object = .{ + .dg = .{ + .gpa = gpa, + .pt = pt, + .mod = zcu.navFileScope(func.owner_nav).mod.?, + .error_msg = null, + .pass = .{ .nav = func.owner_nav }, + .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked, + .expected_block = null, + .fwd_decl = .init(gpa), + .ctype_pool = .empty, + .scratch = .empty, + .uavs = .empty, + }, + .code = .init(gpa), + .indent_writer = undefined, // set later so we can get a pointer to object.code + }, + .lazy_fns = .empty, + }; + defer { + function.object.code.deinit(); + function.object.dg.fwd_decl.deinit(); + function.object.dg.ctype_pool.deinit(gpa); + function.object.dg.scratch.deinit(gpa); + function.object.dg.uavs.deinit(gpa); + function.deinit(); + } + try function.object.dg.ctype_pool.init(gpa); + function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; + + genFunc(&function) catch |err| switch (err) { + error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?), + error.OutOfMemory => |e| return e, + }; + + var mir: Mir = .{ + .uavs = .empty, + .code = &.{}, + .fwd_decl = &.{}, + .ctype_pool = .empty, + .lazy_fns = .empty, + }; + errdefer mir.deinit(gpa); + mir.uavs = function.object.dg.uavs.move(); + mir.code = try function.object.code.toOwnedSlice(); + mir.fwd_decl = try function.object.dg.fwd_decl.toOwnedSlice(); + mir.ctype_pool = function.object.dg.ctype_pool.move(); + mir.lazy_fns = function.lazy_fns.move(); + return mir; +} + +fn genFunc(f: *Function) !void { const tracy = trace(@src()); defer tracy.end(); @@ -8482,7 +8582,7 @@ fn iterateBigTomb(f: *Function, inst: Air.Inst.Index) BigTomb { /// A naive clone of this map would create copies of the ArrayList which is /// stored in the values. This function additionally clones the values. -fn cloneFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) !LocalsMap { +fn cloneFreeLocalsMap(gpa: Allocator, map: *LocalsMap) !LocalsMap { var cloned = try map.clone(gpa); const values = cloned.values(); var i: usize = 0; @@ -8499,7 +8599,7 @@ fn cloneFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) !LocalsMap { return cloned; } -fn deinitFreeLocalsMap(gpa: mem.Allocator, map: *LocalsMap) void { +fn deinitFreeLocalsMap(gpa: Allocator, map: *LocalsMap) void { for (map.values()) |*value| { value.deinit(gpa); } |
