authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-02-19 13:05:25+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:12+00:00
log5cc12da1c0b9e516e356d4d13c48ae671d2e17ff
tree429da7e532ae8d896a6dfcab93f315bd3a43fd9a
parent2b8feabb8f2b3a3c96d2d8e74393e0c8dfa17390
signaturelock-open Commit is signed but in an unrecognized format.

cbe: rework CType and other major refactors

The goal of these changes is to allow the C backend to support the new lazier type resolution system implemented by the frontend. This required a full rewrite of the `CType` abstraction, and major changes to the C backend "linker". The `DebugConstPool` abstraction introduced in a previous commit turns out to be useful for the C backend to codegen types. Because this use case is not debug information but rather general linking (albeit when targeting an unusual object format), I have renamed the abstraction to `ConstPool`. With it, the C linker is told when a type's layout becomes known, and can at that point generate the corresponding C definitions, rather than deferring this work until `flush`. The work done in `flush` is now more-or-less *solely* focused on collecting all of the buffers into a big array for a vectored write. This does unfortunately involve a non-trivial graph traversal to emit type definitions in an appropriate order, but it's still quite fast in practice, and it operates on fairly compact dependency data. We don't generate the actual type *definitions* in `flush`; that happens during compilation using `ConstPool` as discussed above. (We do generate the typedefs for underaligned types in `flush`, but that's a trivial amount of work in most cases.) `CType` is now an ephemeral type: it is created only when we render a type (the logic for which has been pushed into just 2 or 3 functions in `codegen.c`---most of the backend now operates on unmolested Zig `Type`s instead). C types are no longer stored in a "pool", although the type "dependencies" of generated C code (that is, the struct, unions, and typedefs which the generated code references) are tracked (in some simple hash sets) and given to the linker so it can codegen the types.

17 files changed, 5546 insertions(+), 7440 deletions(-)

lib/zig.h+1-1
......@@ -259,7 +259,7 @@
259259#endif
260260
261261#if zig_has_attribute(packed) || defined(zig_tinyc)
262#define zig_packed(definition) __attribute__((packed)) definition
262#define zig_packed(definition) definition __attribute__((packed))
263263#elif defined(zig_msvc)
264264#define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack())
265265#else
src/Compilation.zig-3
......@@ -3382,9 +3382,6 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
33823382 error.OutOfMemory, error.Canceled => |e| return e,
33833383 };
33843384 }
3385 if (comp.zcu) |zcu| {
3386 try link.File.C.flushEmitH(zcu);
3387 }
33883385}
33893386
33903387/// This function is called by the frontend before flush(). It communicates that
src/InternPool.zig+1-1
......@@ -3403,7 +3403,7 @@ pub const LoadedStructType = struct {
34033403 /// Iterates over non-comptime fields in the order they are laid out in memory at runtime.
34043404 /// May or may not include zero-bit fields.
34053405 /// Asserts the struct is not packed.
3406 pub fn iterateRuntimeOrder(s: *const LoadedStructType, ip: *InternPool) RuntimeOrderIterator {
3406 pub fn iterateRuntimeOrder(s: *const LoadedStructType, ip: *const InternPool) RuntimeOrderIterator {
34073407 switch (s.layout) {
34083408 .auto => {
34093409 const ro = std.mem.sliceTo(s.field_runtime_order.get(ip), .omitted);
src/Type.zig+2-2
......@@ -789,7 +789,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
789789/// Determines whether a function type has runtime bits, i.e. whether a
790790/// function with this type can exist at runtime.
791791/// Asserts that `ty` is a function type.
792pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *Zcu) bool {
792pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *const Zcu) bool {
793793 assertHasLayout(fn_ty, zcu);
794794 const fn_info = zcu.typeToFunc(fn_ty).?;
795795 if (fn_info.comptime_bits != 0) return false;
......@@ -830,7 +830,7 @@ pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *Zcu) bool {
830830}
831831
832832/// Like `hasRuntimeBits`, but also returns `true` for runtime functions.
833pub fn isRuntimeFnOrHasRuntimeBits(ty: Type, zcu: *Zcu) bool {
833pub fn isRuntimeFnOrHasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
834834 switch (ty.zigTypeTag(zcu)) {
835835 .@"fn" => return ty.fnHasRuntimeBits(zcu),
836836 else => return ty.hasRuntimeBits(zcu),
src/Value.zig+4-4
......@@ -151,7 +151,7 @@ pub fn intFromEnum(val: Value, zcu: *const Zcu) Value {
151151}
152152
153153/// Asserts that `val` is an integer.
154pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *Zcu) BigIntConst {
154pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *const Zcu) BigIntConst {
155155 if (val.getUnsignedInt(zcu)) |x| {
156156 return BigIntMutable.init(&space.limbs, x).toConst();
157157 }
......@@ -669,7 +669,7 @@ pub fn floatCast(val: Value, dest_ty: Type, pt: Zcu.PerThread) !Value {
669669}
670670
671671/// Asserts the value is comparable. Supports comparisons between heterogeneous types.
672pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu: *Zcu) bool {
672pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu: *const Zcu) bool {
673673 if (lhs.pointerNav(zcu)) |lhs_nav| {
674674 if (rhs.pointerNav(zcu)) |rhs_nav| {
675675 switch (op) {
......@@ -695,7 +695,7 @@ pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu:
695695 return order(lhs, rhs, zcu).compare(op);
696696}
697697
698pub fn order(lhs: Value, rhs: Value, zcu: *Zcu) std.math.Order {
698pub fn order(lhs: Value, rhs: Value, zcu: *const Zcu) std.math.Order {
699699 if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) {
700700 const lhs_f128 = lhs.toFloat(f128, zcu);
701701 const rhs_f128 = rhs.toFloat(f128, zcu);
......@@ -805,7 +805,7 @@ pub fn canMutateComptimeVarState(val: Value, zcu: *Zcu) bool {
805805/// Gets the `Nav` referenced by this pointer. If the pointer does not point
806806/// to a `Nav`, or if it points to some part of one (like a field or element),
807807/// returns null.
808pub fn pointerNav(val: Value, zcu: *Zcu) ?InternPool.Nav.Index {
808pub fn pointerNav(val: Value, zcu: *const Zcu) ?InternPool.Nav.Index {
809809 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
810810 // TODO: these 3 cases are weird; these aren't pointer values!
811811 .variable => |v| v.owner_nav,
src/Zcu.zig+2-2
......@@ -4113,13 +4113,13 @@ pub const ResolvedReference = struct {
41134113/// If an `AnalUnit` is not in the returned map, it is unreferenced.
41144114/// The returned hashmap is owned by the `Zcu`, so should not be freed by the caller.
41154115/// This hashmap is cached, so repeated calls to this function are cheap.
4116pub fn resolveReferences(zcu: *Zcu) !*const std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
4116pub fn resolveReferences(zcu: *Zcu) Allocator.Error!*const std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
41174117 if (zcu.resolved_references == null) {
41184118 zcu.resolved_references = try zcu.resolveReferencesInner();
41194119 }
41204120 return &zcu.resolved_references.?;
41214121}
4122fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
4122fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
41234123 const gpa = zcu.gpa;
41244124 const comp = zcu.comp;
41254125 const ip = &zcu.intern_pool;
src/codegen/c.zig+2272-2995
......@@ -50,32 +50,39 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
5050/// * The types used, so declarations can be emitted in `flush`
5151/// * The lazy functions used, so definitions can be emitted in `flush`
5252pub const Mir = struct {
53 // These remaining fields are essentially just an owned version of `link.C.AvBlock`.
54 fwd_decl: []u8,
55 code_header: []u8,
56 code: []u8,
5357 /// This map contains all the UAVs we saw generating this function.
5458 /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields.
5559 /// Key is the value of the UAV; value is the UAV's alignment, or
5660 /// `.none` for natural alignment. The specified alignment is never
5761 /// less than the natural alignment.
58 uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
59 // These remaining fields are essentially just an owned version of `link.C.AvBlock`.
60 code_header: []u8,
61 code: []u8,
62 fwd_decl: []u8,
63 ctype_pool: CType.Pool,
64 lazy_fns: LazyFnMap,
62 need_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
63 ctype_deps: CType.Dependencies,
64 /// Key is an enum type for which we need a generated `@tagName` function.
65 need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
66 /// Key is a function Nav for which we need a generated `zig_never_tail` wrapper.
67 need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
68 /// Key is a function Nav for which we need a generated `zig_never_inline` wrapper.
69 need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
6570
6671 pub fn deinit(mir: *Mir, gpa: Allocator) void {
67 mir.uavs.deinit(gpa);
72 gpa.free(mir.fwd_decl);
6873 gpa.free(mir.code_header);
6974 gpa.free(mir.code);
70 gpa.free(mir.fwd_decl);
71 mir.ctype_pool.deinit(gpa);
72 mir.lazy_fns.deinit(gpa);
75 mir.need_uavs.deinit(gpa);
76 mir.ctype_deps.deinit(gpa);
77 mir.need_tag_name_funcs.deinit(gpa);
78 mir.need_never_tail_funcs.deinit(gpa);
79 mir.need_never_inline_funcs.deinit(gpa);
7380 }
7481};
7582
76pub const Error = Writer.Error || std.mem.Allocator.Error || error{AnalysisFail};
83pub const Error = Writer.Error || Allocator.Error || error{AnalysisFail};
7784
78pub const CType = @import("c/Type.zig");
85pub const CType = @import("c/type.zig").CType;
7986
8087pub const CValue = union(enum) {
8188 none: void,
......@@ -87,8 +94,6 @@ pub const CValue = union(enum) {
8794 constant: Value,
8895 /// Index into the parameters
8996 arg: usize,
90 /// The array field of a parameter
91 arg_array: usize,
9297 /// Index into a tuple's fields
9398 field: usize,
9499 /// By-value
......@@ -100,8 +105,6 @@ pub const CValue = union(enum) {
100105 identifier: []const u8,
101106 /// Rendered as "payload." followed by as identifier (using fmtIdent)
102107 payload_identifier: []const u8,
103 /// Rendered with fmtCTypePoolString
104 ctype_pool_string: CType.Pool.String,
105108
106109 fn eql(lhs: CValue, rhs: CValue) bool {
107110 return switch (lhs) {
......@@ -122,10 +125,6 @@ pub const CValue = union(enum) {
122125 .arg => |rhs_arg_index| lhs_arg_index == rhs_arg_index,
123126 else => false,
124127 },
125 .arg_array => |lhs_arg_index| switch (rhs) {
126 .arg_array => |rhs_arg_index| lhs_arg_index == rhs_arg_index,
127 else => false,
128 },
129128 .field => |lhs_field_index| switch (rhs) {
130129 .field => |rhs_field_index| lhs_field_index == rhs_field_index,
131130 else => false,
......@@ -150,10 +149,6 @@ pub const CValue = union(enum) {
150149 .payload_identifier => |rhs_id| std.mem.eql(u8, lhs_id, rhs_id),
151150 else => false,
152151 },
153 .ctype_pool_string => |lhs_str| switch (rhs) {
154 .ctype_pool_string => |rhs_str| lhs_str.index == rhs_str.index,
155 else => false,
156 },
157152 };
158153 }
159154};
......@@ -163,53 +158,24 @@ const BlockData = struct {
163158 result: CValue,
164159};
165160
166pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue);
167
168pub const LazyFnKey = union(enum) {
169 tag_name: InternPool.Index,
170 never_tail: InternPool.Nav.Index,
171 never_inline: InternPool.Nav.Index,
172};
173pub const LazyFnValue = struct {
174 fn_name: CType.Pool.String,
175};
176pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue);
177
178const Local = struct {
179 ctype: CType,
180 flags: packed struct(u32) {
181 alignas: CType.AlignAs,
182 _: u20 = undefined,
183 },
184
185 fn getType(local: Local) LocalType {
186 return .{ .ctype = local.ctype, .alignas = local.flags.alignas };
187 }
161const LocalType = struct {
162 type: Type,
163 alignment: Alignment,
188164};
189165
190166const LocalIndex = u16;
191const LocalType = struct { ctype: CType, alignas: CType.AlignAs };
192167const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void);
193168const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList);
194169
195170const ValueRenderLocation = enum {
196 FunctionArgument,
197 Initializer,
198 StaticInitializer,
199 Other,
171 initializer,
172 static_initializer,
173 other,
200174
201175 fn isInitializer(loc: ValueRenderLocation) bool {
202176 return switch (loc) {
203 .Initializer, .StaticInitializer => true,
204 else => false,
205 };
206 }
207
208 fn toCTypeKind(loc: ValueRenderLocation) CType.Kind {
209 return switch (loc) {
210 .FunctionArgument => .parameter,
211 .Initializer, .Other => .complete,
212 .StaticInitializer => .global,
177 .initializer, .static_initializer => true,
178 .other => false,
213179 };
214180 }
215181};
......@@ -334,16 +300,31 @@ const reserved_idents = std.StaticStringMap(void).initComptime(.{
334300});
335301
336302fn isReservedIdent(ident: []const u8) bool {
337 if (ident.len >= 2 and ident[0] == '_') { // C language
303 // C language
304 if (ident.len >= 2 and ident[0] == '_') {
338305 switch (ident[1]) {
339306 'A'...'Z', '_' => return true,
340 else => return false,
307 else => {},
341308 }
342 } else if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or
309 }
310
311 // windows.h
312 if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or
343313 mem.startsWith(u8, ident, "DUMMYUNIONNAME"))
344 { // windows.h
314 {
315 return true;
316 }
317
318 // CType
319 if (mem.startsWith(u8, ident, "enum__") or
320 mem.startsWith(u8, ident, "bitpack__") or
321 mem.startsWith(u8, ident, "aligned__") or
322 mem.startsWith(u8, ident, "fn__"))
323 {
345324 return true;
346 } else return reserved_idents.has(ident);
325 }
326
327 return reserved_idents.has(ident);
347328}
348329
349330fn formatIdentSolo(ident: []const u8, w: *Writer) Writer.Error!void {
......@@ -361,7 +342,7 @@ fn formatIdentOptions(ident: []const u8, w: *Writer, solo: bool) Writer.Error!vo
361342 for (ident, 0..) |c, i| {
362343 switch (c) {
363344 'a'...'z', 'A'...'Z', '_' => try w.writeByte(c),
364 '.' => try w.writeByte('_'),
345 '.', ' ' => try w.writeByte('_'),
365346 '0'...'9' => if (i == 0) {
366347 try w.print("_{x:2}", .{c});
367348 } else {
......@@ -380,29 +361,6 @@ pub fn fmtIdentUnsolo(ident: []const u8) std.fmt.Alt([]const u8, formatIdentUnso
380361 return .{ .data = ident };
381362}
382363
383const CTypePoolStringFormatData = struct {
384 ctype_pool_string: CType.Pool.String,
385 ctype_pool: *const CType.Pool,
386 solo: bool,
387};
388fn formatCTypePoolString(data: CTypePoolStringFormatData, w: *Writer) Writer.Error!void {
389 if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice|
390 try formatIdentOptions(slice, w, data.solo)
391 else
392 try w.print("{f}", .{data.ctype_pool_string.fmt(data.ctype_pool)});
393}
394pub fn fmtCTypePoolString(
395 ctype_pool_string: CType.Pool.String,
396 ctype_pool: *const CType.Pool,
397 solo: bool,
398) std.fmt.Alt(CTypePoolStringFormatData, formatCTypePoolString) {
399 return .{ .data = .{
400 .ctype_pool_string = ctype_pool_string,
401 .ctype_pool = ctype_pool,
402 .solo = solo,
403 } };
404}
405
406364// Returns true if `formatIdent` would make any edits to ident.
407365// This must be kept in sync with `formatIdent`.
408366pub fn isMangledIdent(ident: []const u8, solo: bool) bool {
......@@ -417,21 +375,26 @@ pub fn isMangledIdent(ident: []const u8, solo: bool) bool {
417375 return false;
418376}
419377
420/// This data is available when outputting .c code for a `InternPool.Index`
421/// that corresponds to `func`.
422/// It is not available when generating .h file.
378/// This data is available when rendering C source code for an interned function.
423379pub const Function = struct {
424380 air: Air,
425381 liveness: Air.Liveness,
426 value_map: CValueMap,
382 value_map: std.AutoHashMap(Air.Inst.Ref, CValue),
427383 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,
428384 next_arg_index: u32 = 0,
429385 next_block_index: u32 = 0,
430 object: Object,
431 lazy_fns: LazyFnMap,
386 dg: DeclGen,
387 code: Writer.Allocating,
388 indent_counter: usize,
389 /// Key is an enum type for which we need a generated `@tagName` function.
390 need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
391 /// Key is a function Nav for which we need a generated `zig_never_tail` wrapper.
392 need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
393 /// Key is a function Nav for which we need a generated `zig_never_inline` wrapper.
394 need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
432395 func_index: InternPool.Index,
433396 /// All the locals, to be emitted at the top of the function.
434 locals: std.ArrayList(Local) = .empty,
397 locals: std.ArrayList(LocalType) = .empty,
435398 /// Which locals are available for reuse, based on Type.
436399 free_locals_map: LocalsMap = .{},
437400 /// Locals which will not be freed by Liveness. This is used after a
......@@ -445,37 +408,41 @@ pub const Function = struct {
445408 /// for the switch cond. Dispatches should set this local to the new cond.
446409 loop_switch_conds: std.AutoHashMapUnmanaged(Air.Inst.Index, LocalIndex) = .empty,
447410
411 const indent_width = 1;
412 const indent_char = ' ';
413
414 fn newline(f: *Function) !void {
415 const w = &f.code.writer;
416 try w.writeByte('\n');
417 try w.splatByteAll(indent_char, f.indent_counter);
418 }
419 fn indent(f: *Function) void {
420 f.indent_counter += indent_width;
421 }
422 fn outdent(f: *Function) !void {
423 f.indent_counter -= indent_width;
424 const written = f.code.written();
425 switch (written[written.len - 1]) {
426 indent_char => f.code.shrinkRetainingCapacity(written.len - indent_width),
427 '\n' => try f.code.writer.splatByteAll(indent_char, f.indent_counter),
428 else => {
429 std.debug.print("\"{f}\"\n", .{std.zig.fmtString(written[written.len -| 100..])});
430 unreachable;
431 },
432 }
433 }
434
448435 fn resolveInst(f: *Function, ref: Air.Inst.Ref) !CValue {
449436 const gop = try f.value_map.getOrPut(ref);
450 if (gop.found_existing) return gop.value_ptr.*;
451
452 const pt = f.object.dg.pt;
453 const zcu = pt.zcu;
454 const val = (try f.air.value(ref, pt)).?;
455 const ty = f.typeOf(ref);
456
457 const result: CValue = if (lowersToArray(ty, zcu)) result: {
458 const ch = &f.object.code_header.writer;
459 const decl_c_value = try f.allocLocalValue(.{
460 .ctype = try f.ctypeFromType(ty, .complete),
461 .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(zcu)),
462 });
463 const gpa = f.object.dg.gpa;
464 try f.allocs.put(gpa, decl_c_value.new_local, false);
465 try ch.writeAll("static ");
466 try f.object.dg.renderTypeAndName(ch, ty, decl_c_value, Const, .none, .complete);
467 try ch.writeAll(" = ");
468 try f.object.dg.renderValue(ch, val, .StaticInitializer);
469 try ch.writeAll(";\n ");
470 break :result .{ .local = decl_c_value.new_local };
471 } else .{ .constant = val };
472
473 gop.value_ptr.* = result;
474 return result;
437 if (!gop.found_existing) {
438 const val = try f.air.value(ref, f.dg.pt);
439 gop.value_ptr.* = .{ .constant = val.? };
440 }
441 return gop.value_ptr.*;
475442 }
476443
477444 fn wantSafety(f: *Function) bool {
478 return switch (f.object.dg.pt.zcu.optimizeMode()) {
445 return switch (f.dg.pt.zcu.optimizeMode()) {
479446 .Debug, .ReleaseSafe => true,
480447 .ReleaseFast, .ReleaseSmall => false,
481448 };
......@@ -485,18 +452,16 @@ pub const Function = struct {
485452 /// those which go into `allocs`. This function does not add the resulting local into `allocs`;
486453 /// that responsibility lies with the caller.
487454 fn allocLocalValue(f: *Function, local_type: LocalType) !CValue {
488 try f.locals.ensureUnusedCapacity(f.object.dg.gpa, 1);
489 defer f.locals.appendAssumeCapacity(.{
490 .ctype = local_type.ctype,
491 .flags = .{ .alignas = local_type.alignas },
492 });
493 return .{ .new_local = @intCast(f.locals.items.len) };
455 try f.locals.ensureUnusedCapacity(f.dg.gpa, 1);
456 const index = f.locals.items.len;
457 f.locals.appendAssumeCapacity(local_type);
458 return .{ .new_local = @intCast(index) };
494459 }
495460
496461 fn allocLocal(f: *Function, inst: ?Air.Inst.Index, ty: Type) !CValue {
497462 return f.allocAlignedLocal(inst, .{
498 .ctype = try f.ctypeFromType(ty, .complete),
499 .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(f.object.dg.pt.zcu)),
463 .type = ty,
464 .alignment = .none,
500465 });
501466 }
502467
......@@ -524,11 +489,10 @@ pub const Function = struct {
524489 .none => unreachable,
525490 .new_local, .local => |i| try w.print("t{d}", .{i}),
526491 .local_ref => |i| try w.print("&t{d}", .{i}),
527 .constant => |val| try f.object.dg.renderValue(w, val, location),
492 .constant => |val| try f.dg.renderValue(w, val, location),
528493 .arg => |i| try w.print("a{d}", .{i}),
529 .arg_array => |i| try f.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }),
530 .undef => |ty| try f.object.dg.renderUndefValue(w, ty, location),
531 else => try f.object.dg.writeCValue(w, c_value),
494 .undef => |ty| try f.dg.renderUndefValue(w, ty, location),
495 else => try f.dg.writeCValue(w, c_value),
532496 }
533497 }
534498
......@@ -537,17 +501,12 @@ pub const Function = struct {
537501 .none => unreachable,
538502 .new_local, .local, .constant => {
539503 try w.writeAll("(*");
540 try f.writeCValue(w, c_value, .Other);
504 try f.writeCValue(w, c_value, .other);
541505 try w.writeByte(')');
542506 },
543507 .local_ref => |i| try w.print("t{d}", .{i}),
544508 .arg => |i| try w.print("(*a{d})", .{i}),
545 .arg_array => |i| {
546 try w.writeAll("(*");
547 try f.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" });
548 try w.writeByte(')');
549 },
550 else => try f.object.dg.writeCValueDeref(w, c_value),
509 else => try f.dg.writeCValueDeref(w, c_value),
551510 }
552511 }
553512
......@@ -558,119 +517,77 @@ pub const Function = struct {
558517 member: CValue,
559518 ) Error!void {
560519 switch (c_value) {
561 .new_local, .local, .local_ref, .constant, .arg, .arg_array => {
562 try f.writeCValue(w, c_value, .Other);
520 .new_local, .local, .local_ref, .constant, .arg => {
521 try f.writeCValue(w, c_value, .other);
563522 try w.writeByte('.');
564 try f.writeCValue(w, member, .Other);
523 try f.writeCValue(w, member, .other);
565524 },
566 else => return f.object.dg.writeCValueMember(w, c_value, member),
525 else => return f.dg.writeCValueMember(w, c_value, member),
567526 }
568527 }
569528
570529 fn writeCValueDerefMember(f: *Function, w: *Writer, c_value: CValue, member: CValue) !void {
571530 switch (c_value) {
572 .new_local, .local, .arg, .arg_array => {
573 try f.writeCValue(w, c_value, .Other);
531 .new_local, .local, .arg => {
532 try f.writeCValue(w, c_value, .other);
574533 try w.writeAll("->");
575534 },
576535 .constant => {
577536 try w.writeByte('(');
578 try f.writeCValue(w, c_value, .Other);
537 try f.writeCValue(w, c_value, .other);
579538 try w.writeAll(")->");
580539 },
581540 .local_ref => {
582541 try f.writeCValueDeref(w, c_value);
583542 try w.writeByte('.');
584543 },
585 else => return f.object.dg.writeCValueDerefMember(w, c_value, member),
544 else => return f.dg.writeCValueDerefMember(w, c_value, member),
586545 }
587 try f.writeCValue(w, member, .Other);
546 try f.writeCValue(w, member, .other);
588547 }
589548
590549 fn fail(f: *Function, comptime format: []const u8, args: anytype) Error {
591 return f.object.dg.fail(format, args);
592 }
593
594 fn ctypeFromType(f: *Function, ty: Type, kind: CType.Kind) !CType {
595 return f.object.dg.ctypeFromType(ty, kind);
596 }
597
598 fn byteSize(f: *Function, ctype: CType) u64 {
599 return f.object.dg.byteSize(ctype);
600 }
601
602 fn renderType(f: *Function, w: *Writer, ctype: Type) !void {
603 return f.object.dg.renderType(w, ctype);
550 return f.dg.fail(format, args);
604551 }
605552
606 fn renderCType(f: *Function, w: *Writer, ctype: CType) !void {
607 return f.object.dg.renderCType(w, ctype);
553 fn renderType(f: *Function, w: *Writer, ty: Type) !void {
554 return f.dg.renderType(w, ty);
608555 }
609556
610557 fn renderIntCast(f: *Function, w: *Writer, dest_ty: Type, src: CValue, v: Vectorize, src_ty: Type, location: ValueRenderLocation) !void {
611 return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location);
558 return f.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location);
612559 }
613560
614561 fn fmtIntLiteralDec(f: *Function, val: Value) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) {
615 return f.object.dg.fmtIntLiteralDec(val, .Other);
562 return f.dg.fmtIntLiteralDec(val, .other);
616563 }
617564
618565 fn fmtIntLiteralHex(f: *Function, val: Value) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) {
619 return f.object.dg.fmtIntLiteralHex(val, .Other);
620 }
621
622 fn getLazyFnName(f: *Function, key: LazyFnKey) ![]const u8 {
623 const gpa = f.object.dg.gpa;
624 const pt = f.object.dg.pt;
625 const zcu = pt.zcu;
626 const ip = &zcu.intern_pool;
627 const ctype_pool = &f.object.dg.ctype_pool;
628
629 const gop = try f.lazy_fns.getOrPut(gpa, key);
630 if (!gop.found_existing) {
631 errdefer _ = f.lazy_fns.pop();
632
633 gop.value_ptr.* = .{
634 .fn_name = switch (key) {
635 .tag_name,
636 => |enum_ty| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{
637 @tagName(key),
638 fmtIdentUnsolo(ip.loadEnumType(enum_ty).name.toSlice(ip)),
639 @intFromEnum(enum_ty),
640 }),
641 .never_tail,
642 .never_inline,
643 => |owner_nav| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{
644 @tagName(key),
645 fmtIdentUnsolo(ip.getNav(owner_nav).name.toSlice(ip)),
646 @intFromEnum(owner_nav),
647 }),
648 },
649 };
650 }
651 return gop.value_ptr.fn_name.toSlice(ctype_pool).?;
566 return f.dg.fmtIntLiteralHex(val, .other);
652567 }
653568
654569 pub fn deinit(f: *Function) void {
655 const gpa = f.object.dg.gpa;
570 const gpa = f.dg.gpa;
656571 f.allocs.deinit(gpa);
657572 f.locals.deinit(gpa);
658573 deinitFreeLocalsMap(gpa, &f.free_locals_map);
659574 f.blocks.deinit(gpa);
660575 f.value_map.deinit();
661 f.lazy_fns.deinit(gpa);
576 f.need_tag_name_funcs.deinit(gpa);
577 f.need_never_tail_funcs.deinit(gpa);
578 f.need_never_inline_funcs.deinit(gpa);
662579 f.loop_switch_conds.deinit(gpa);
663580 }
664581
665582 fn typeOf(f: *Function, inst: Air.Inst.Ref) Type {
666 return f.air.typeOf(inst, &f.object.dg.pt.zcu.intern_pool);
583 return f.air.typeOf(inst, &f.dg.pt.zcu.intern_pool);
667584 }
668585
669586 fn typeOfIndex(f: *Function, inst: Air.Inst.Index) Type {
670 return f.air.typeOfIndex(inst, &f.object.dg.pt.zcu.intern_pool);
587 return f.air.typeOfIndex(inst, &f.dg.pt.zcu.intern_pool);
671588 }
672589
673 fn copyCValue(f: *Function, ctype: CType, dst: CValue, src: CValue) !void {
590 fn copyCValue(f: *Function, dst: CValue, src: CValue) !void {
674591 switch (dst) {
675592 .new_local, .local => |dst_local_index| switch (src) {
676593 .new_local, .local => |src_local_index| if (dst_local_index == src_local_index) return,
......@@ -678,12 +595,12 @@ pub const Function = struct {
678595 },
679596 else => {},
680597 }
681 const w = &f.object.code.writer;
682 const a = try Assignment.start(f, w, ctype);
683 try f.writeCValue(w, dst, .Other);
684 try a.assign(f, w);
685 try f.writeCValue(w, src, .Other);
686 try a.end(f, w);
598 const w = &f.code.writer;
599 try f.writeCValue(w, dst, .other);
600 try w.writeAll(" = ");
601 try f.writeCValue(w, src, .other);
602 try w.writeByte(';');
603 try f.newline();
687604 }
688605
689606 fn moveCValue(f: *Function, inst: Air.Inst.Index, ty: Type, src: CValue) !CValue {
......@@ -694,7 +611,7 @@ pub const Function = struct {
694611 else => {
695612 try freeCValue(f, inst, src);
696613 const dst = try f.allocLocal(inst, ty);
697 try f.copyCValue(try f.ctypeFromType(ty, .complete), dst, src);
614 try f.copyCValue(dst, src);
698615 return dst;
699616 },
700617 }
......@@ -708,51 +625,17 @@ pub const Function = struct {
708625 }
709626};
710627
711/// This data is available when outputting .c code for a `Zcu`.
712/// It is not available when generating .h file.
713pub const Object = struct {
714 dg: DeclGen,
715 code_header: Writer.Allocating,
716 code: Writer.Allocating,
717 indent_counter: usize,
718
719 const indent_width = 1;
720 const indent_char = ' ';
721
722 fn newline(o: *Object) !void {
723 const w = &o.code.writer;
724 try w.writeByte('\n');
725 try w.splatByteAll(indent_char, o.indent_counter);
726 }
727 fn indent(o: *Object) void {
728 o.indent_counter += indent_width;
729 }
730 fn outdent(o: *Object) !void {
731 o.indent_counter -= indent_width;
732 const written = o.code.written();
733 switch (written[written.len - 1]) {
734 indent_char => o.code.shrinkRetainingCapacity(written.len - indent_width),
735 '\n' => try o.code.writer.splatByteAll(indent_char, o.indent_counter),
736 else => {
737 std.debug.print("\"{f}\"\n", .{std.zig.fmtString(written[written.len -| 100..])});
738 unreachable;
739 },
740 }
741 }
742};
743
744/// This data is available both when outputting .c code and when outputting an .h file.
628/// This data is available when rendering *any* C source code (function or otherwise).
745629pub const DeclGen = struct {
746630 gpa: Allocator,
631 arena: Allocator,
747632 pt: Zcu.PerThread,
748633 mod: *Module,
749 pass: Pass,
634 owner_nav: InternPool.Nav.Index.Optional,
750635 is_naked_fn: bool,
751636 expected_block: ?u32,
752 fwd_decl: Writer.Allocating,
753637 error_msg: ?*Zcu.ErrorMsg,
754 ctype_pool: CType.Pool,
755 scratch: std.ArrayList(u32),
638 ctype_deps: CType.Dependencies,
756639 /// This map contains all the UAVs we saw generating this function.
757640 /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields.
758641 /// Key is the value of the UAV; value is the UAV's alignment, or
......@@ -760,16 +643,10 @@ pub const DeclGen = struct {
760643 /// less than the natural alignment.
761644 uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
762645
763 pub const Pass = union(enum) {
764 nav: InternPool.Nav.Index,
765 uav: InternPool.Index,
766 flush,
767 };
768
769646 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) Error {
770647 @branchHint(.cold);
771648 const zcu = dg.pt.zcu;
772 const src_loc = zcu.navSrcLoc(dg.pass.nav);
649 const src_loc = zcu.navSrcLoc(dg.owner_nav.unwrap().?);
773650 dg.error_msg = try Zcu.ErrorMsg.create(dg.gpa, src_loc, format, args);
774651 return error.AnalysisFail;
775652 }
......@@ -783,14 +660,13 @@ pub const DeclGen = struct {
783660 const pt = dg.pt;
784661 const zcu = pt.zcu;
785662 const ip = &zcu.intern_pool;
786 const ctype_pool = &dg.ctype_pool;
787663 const uav_val = Value.fromInterned(uav.val);
788664 const uav_ty = uav_val.typeOf(zcu);
789665
790666 // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
791667 const ptr_ty: Type = .fromInterned(uav.orig_ty);
792668 if (ptr_ty.isPtrAtRuntime(zcu) and !uav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) {
793 return dg.writeCValue(w, .{ .undef = ptr_ty });
669 return dg.renderUndefValue(w, ptr_ty, location);
794670 }
795671
796672 // Chase function values in order to be able to reference the original function.
......@@ -805,14 +681,12 @@ pub const DeclGen = struct {
805681 // them). The analysis until now should ensure that the C function
806682 // pointers are compatible. If they are not, then there is a bug
807683 // somewhere and we should let the C compiler tell us about it.
808 const ptr_ctype = try dg.ctypeFromType(ptr_ty, .complete);
809 const elem_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;
810 const uav_ctype = try dg.ctypeFromType(uav_ty, .complete);
811 const need_cast = !elem_ctype.eql(uav_ctype) and
812 (elem_ctype.info(ctype_pool) != .function or uav_ctype.info(ctype_pool) != .function);
684 const elem_ty = ptr_ty.childType(zcu);
685 const need_cast = elem_ty.toIntern() != uav_ty.toIntern() and
686 elem_ty.zigTypeTag(zcu) != .@"fn" or uav_ty.zigTypeTag(zcu) != .@"fn";
813687 if (need_cast) {
814688 try w.writeAll("((");
815 try dg.renderCType(w, ptr_ctype);
689 try dg.renderType(w, ptr_ty);
816690 try w.writeByte(')');
817691 }
818692 try w.writeByte('&');
......@@ -842,11 +716,9 @@ pub const DeclGen = struct {
842716 nav_index: InternPool.Nav.Index,
843717 location: ValueRenderLocation,
844718 ) Error!void {
845 _ = location;
846719 const pt = dg.pt;
847720 const zcu = pt.zcu;
848721 const ip = &zcu.intern_pool;
849 const ctype_pool = &dg.ctype_pool;
850722
851723 // Chase function values in order to be able to reference the original function.
852724 const owner_nav = switch (ip.getNav(nav_index).status) {
......@@ -863,25 +735,23 @@ pub const DeclGen = struct {
863735 const nav_ty: Type = .fromInterned(ip.getNav(owner_nav).typeOf(ip));
864736 const ptr_ty = try pt.navPtrType(owner_nav);
865737 if (!nav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) {
866 return dg.writeCValue(w, .{ .undef = ptr_ty });
738 return dg.renderUndefValue(w, ptr_ty, location);
867739 }
868740
869741 // We shouldn't cast C function pointers as this is UB (when you call
870742 // them). The analysis until now should ensure that the C function
871743 // pointers are compatible. If they are not, then there is a bug
872744 // somewhere and we should let the C compiler tell us about it.
873 const ctype = try dg.ctypeFromType(ptr_ty, .complete);
874 const elem_ctype = ctype.info(ctype_pool).pointer.elem_ctype;
875 const nav_ctype = try dg.ctypeFromType(nav_ty, .complete);
876 const need_cast = !elem_ctype.eql(nav_ctype) and
877 (elem_ctype.info(ctype_pool) != .function or nav_ctype.info(ctype_pool) != .function);
745 const elem_ty = ptr_ty.childType(zcu);
746 const need_cast = elem_ty.toIntern() != nav_ty.toIntern() and
747 elem_ty.zigTypeTag(zcu) != .@"fn" or nav_ty.zigTypeTag(zcu) != .@"fn";
878748 if (need_cast) {
879749 try w.writeAll("((");
880 try dg.renderCType(w, ctype);
750 try dg.renderType(w, ptr_ty);
881751 try w.writeByte(')');
882752 }
883753 try w.writeByte('&');
884 try dg.renderNavName(w, owner_nav);
754 try renderNavName(w, owner_nav, ip);
885755 if (need_cast) try w.writeByte(')');
886756 }
887757
......@@ -896,11 +766,10 @@ pub const DeclGen = struct {
896766 switch (derivation) {
897767 .comptime_alloc_ptr, .comptime_field_ptr => unreachable,
898768 .int => |int| {
899 const ptr_ctype = try dg.ctypeFromType(int.ptr_ty, .complete);
900769 const addr_val = try pt.intValue(.usize, int.addr);
901770 try w.writeByte('(');
902 try dg.renderCType(w, ptr_ctype);
903 try w.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .Other)});
771 try dg.renderType(w, int.ptr_ty);
772 try w.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .other)});
904773 },
905774
906775 .nav_ptr => |nav| try dg.renderNav(w, nav, location),
......@@ -915,14 +784,10 @@ pub const DeclGen = struct {
915784 .field_ptr => |field| {
916785 const parent_ptr_ty = try field.parent.ptrType(pt);
917786
918 // Ensure complete type definition is available before accessing fields.
919 _ = try dg.ctypeFromType(parent_ptr_ty.childType(zcu), .complete);
920
921787 switch (fieldLocation(parent_ptr_ty, field.result_ptr_ty, field.field_idx, zcu)) {
922788 .begin => {
923 const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete);
924789 try w.writeByte('(');
925 try dg.renderCType(w, ptr_ctype);
790 try dg.renderType(w, field.result_ptr_ty);
926791 try w.writeByte(')');
927792 try dg.renderPointer(w, field.parent.*, location);
928793 },
......@@ -933,51 +798,40 @@ pub const DeclGen = struct {
933798 try dg.writeCValue(w, name);
934799 },
935800 .byte_offset => |byte_offset| {
936 const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete);
937801 try w.writeByte('(');
938 try dg.renderCType(w, ptr_ctype);
802 try dg.renderType(w, field.result_ptr_ty);
939803 try w.writeByte(')');
940804 const offset_val = try pt.intValue(.usize, byte_offset);
941805 try w.writeAll("((char *)");
942806 try dg.renderPointer(w, field.parent.*, location);
943 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)});
807 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .other)});
944808 },
945809 }
946810 },
947811
948812 .elem_ptr => |elem| if (!(try elem.parent.ptrType(pt)).childType(zcu).hasRuntimeBits(zcu)) {
949813 // Element type is zero-bit, so lowers to `void`. The index is irrelevant; just cast the pointer.
950 const ptr_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete);
951814 try w.writeByte('(');
952 try dg.renderCType(w, ptr_ctype);
815 try dg.renderType(w, elem.result_ptr_ty);
953816 try w.writeByte(')');
954817 try dg.renderPointer(w, elem.parent.*, location);
955818 } else {
956819 const index_val = try pt.intValue(.usize, elem.elem_idx);
957 // We want to do pointer arithmetic on a pointer to the element type.
958 // We might have a pointer-to-array. In this case, we must cast first.
959 const result_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete);
960 const parent_ctype = try dg.ctypeFromType(try elem.parent.ptrType(pt), .complete);
961 if (result_ctype.eql(parent_ctype)) {
962 // The pointer already has an appropriate type - just do the arithmetic.
820 try w.writeByte('(');
821 // We want to do pointer arithmetic on a pointer to the element type, but the parent
822 // might be a pointer-to-array, in which case we must cast it.
823 if (elem.result_ptr_ty.toIntern() != (try elem.parent.ptrType(pt)).toIntern()) {
963824 try w.writeByte('(');
964 try dg.renderPointer(w, elem.parent.*, location);
965 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)});
966 } else {
967 // We probably have an array pointer `T (*)[n]`. Cast to an element pointer,
968 // and *then* apply the index.
969 try w.writeAll("((");
970 try dg.renderCType(w, result_ctype);
825 try dg.renderType(w, elem.result_ptr_ty);
971826 try w.writeByte(')');
972 try dg.renderPointer(w, elem.parent.*, location);
973 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)});
974827 }
828 try dg.renderPointer(w, elem.parent.*, location);
829 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .other)});
975830 },
976831
977832 .offset_and_cast => |oac| {
978 const ptr_ctype = try dg.ctypeFromType(oac.new_ptr_ty, .complete);
979833 try w.writeByte('(');
980 try dg.renderCType(w, ptr_ctype);
834 try dg.renderType(w, oac.new_ptr_ty);
981835 try w.writeByte(')');
982836 if (oac.byte_offset == 0) {
983837 try dg.renderPointer(w, oac.parent.*, location);
......@@ -985,14 +839,40 @@ pub const DeclGen = struct {
985839 const offset_val = try pt.intValue(.usize, oac.byte_offset);
986840 try w.writeAll("((char *)");
987841 try dg.renderPointer(w, oac.parent.*, location);
988 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)});
842 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .other)});
989843 }
990844 },
991845 }
992846 }
993847
994 fn renderErrorName(dg: *DeclGen, w: *Writer, err_name: InternPool.NullTerminatedString) !void {
995 try w.print("zig_error_{f}", .{fmtIdentUnsolo(err_name.toSlice(&dg.pt.zcu.intern_pool))});
848 fn renderValueAsLvalue(
849 dg: *DeclGen,
850 w: *Writer,
851 val: Value,
852 ) Error!void {
853 const zcu = dg.pt.zcu;
854
855 // If the type of `val` lowers to a C struct or union type, then `renderValue` will render
856 // it as a compound literal, and compound literals are already lvalues.
857 const ty = val.typeOf(zcu);
858 const is_aggregate: bool = switch (ty.zigTypeTag(zcu)) {
859 .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {
860 .auto, .@"extern" => true,
861 .@"packed" => false,
862 },
863 .array,
864 .vector,
865 .error_union,
866 .optional,
867 => true,
868 else => false,
869 };
870 if (is_aggregate) return renderValue(dg, w, val, .other);
871
872 // Otherwise, use a UAV.
873 const gop = try dg.uavs.getOrPut(dg.gpa, val.toIntern());
874 if (!gop.found_existing) gop.value_ptr.* = .none;
875 try renderUavName(w, val);
996876 }
997877
998878 fn renderValue(
......@@ -1005,16 +885,13 @@ pub const DeclGen = struct {
1005885 const zcu = pt.zcu;
1006886 const ip = &zcu.intern_pool;
1007887 const target = &dg.mod.resolved_target.result;
1008 const ctype_pool = &dg.ctype_pool;
1009888
1010889 const initializer_type: ValueRenderLocation = switch (location) {
1011 .StaticInitializer => .StaticInitializer,
1012 else => .Initializer,
890 .static_initializer => .static_initializer,
891 else => .initializer,
1013892 };
1014893
1015894 const ty = val.typeOf(zcu);
1016 if (val.isUndef(zcu)) return dg.renderUndefValue(w, ty, location);
1017 const ctype = try dg.ctypeFromType(ty, location.toCTypeKind());
1018895 switch (ip.indexToKey(val.toIntern())) {
1019896 // types, not values
1020897 .int_type,
......@@ -1037,7 +914,7 @@ pub const DeclGen = struct {
1037914 .memoized_call,
1038915 => unreachable,
1039916
1040 .undef => unreachable, // handled above
917 .undef => try dg.renderUndefValue(w, ty, location),
1041918 .simple_value => |simple_value| switch (simple_value) {
1042919 // non-runtime values
1043920 .void => unreachable,
......@@ -1053,46 +930,28 @@ pub const DeclGen = struct {
1053930 .enum_literal,
1054931 => unreachable, // non-runtime values
1055932 .int => try w.print("{f}", .{try dg.fmtIntLiteralDec(val, location)}),
1056 .err => |err| try dg.renderErrorName(w, err.name),
1057 .error_union => |error_union| switch (ctype.info(ctype_pool)) {
1058 .basic => switch (error_union.val) {
1059 .err_name => |err_name| try dg.renderErrorName(w, err_name),
933 .err => |err| try renderErrorName(w, err.name.toSlice(ip)),
934 .error_union => |error_union| {
935 if (!location.isInitializer()) {
936 try w.writeByte('(');
937 try dg.renderType(w, ty);
938 try w.writeByte(')');
939 }
940 try w.writeAll("{ .error = ");
941 switch (error_union.val) {
942 .err_name => |err_name| try renderErrorName(w, err_name.toSlice(ip)),
1060943 .payload => try w.writeByte('0'),
1061 },
1062 .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable,
1063 .aggregate => |aggregate| {
1064 if (!location.isInitializer()) {
1065 try w.writeByte('(');
1066 try dg.renderCType(w, ctype);
1067 try w.writeByte(')');
1068 }
1069 try w.writeByte('{');
1070 for (0..aggregate.fields.len) |field_index| {
1071 if (field_index > 0) try w.writeByte(',');
1072 switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
1073 .@"error" => switch (error_union.val) {
1074 .err_name => |err_name| try dg.renderErrorName(w, err_name),
1075 .payload => try w.writeByte('0'),
1076 },
1077 .payload => switch (error_union.val) {
1078 .err_name => try dg.renderUndefValue(
1079 w,
1080 ty.errorUnionPayload(zcu),
1081 initializer_type,
1082 ),
1083 .payload => |payload| try dg.renderValue(
1084 w,
1085 Value.fromInterned(payload),
1086 initializer_type,
1087 ),
1088 },
1089 else => unreachable,
1090 }
944 }
945 if (ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {
946 try w.writeAll(", .payload = ");
947 switch (error_union.val) {
948 .err_name => try dg.renderUndefValue(w, ty.errorUnionPayload(zcu), initializer_type),
949 .payload => |payload| try dg.renderValue(w, .fromInterned(payload), initializer_type),
1091950 }
1092 try w.writeByte('}');
1093 },
951 }
952 try w.writeAll(" }");
1094953 },
1095 .enum_tag => |enum_tag| try dg.renderValue(w, Value.fromInterned(enum_tag.int), location),
954 .enum_tag => |enum_tag| try dg.renderValue(w, .fromInterned(enum_tag.int), location),
1096955 .float => {
1097956 const bits = ty.floatBits(target);
1098957 const f128_val = val.toFloat(f128, zcu);
......@@ -1143,7 +1002,7 @@ pub const DeclGen = struct {
11431002 else
11441003 unreachable;
11451004
1146 if (location == .StaticInitializer) {
1005 if (location == .static_initializer) {
11471006 if (!std.math.isNan(f128_val) and std.math.isSignalNan(f128_val))
11481007 return dg.fail("TODO: C backend: implement nans rendering in static initializers", .{});
11491008
......@@ -1154,9 +1013,11 @@ pub const DeclGen = struct {
11541013 // return dg.fail("Only quiet nans are supported in global variable initializers", .{});
11551014 }
11561015
1157 try w.writeAll("zig_");
1158 try w.writeAll(if (location == .StaticInitializer) "init" else "make");
1159 try w.writeAll("_special_");
1016 if (location == .static_initializer) {
1017 try w.writeAll("zig_init_special_");
1018 } else {
1019 try w.writeAll("zig_make_special_");
1020 }
11601021 try dg.renderTypeForBuiltinFnName(w, ty);
11611022 try w.writeByte('(');
11621023 if (std.math.signbit(f128_val)) try w.writeByte('-');
......@@ -1183,105 +1044,85 @@ pub const DeclGen = struct {
11831044 if (!empty) try w.writeByte(')');
11841045 },
11851046 .slice => |slice| {
1186 const aggregate = ctype.info(ctype_pool).aggregate;
11871047 if (!location.isInitializer()) {
11881048 try w.writeByte('(');
1189 try dg.renderCType(w, ctype);
1049 try dg.renderType(w, ty);
11901050 try w.writeByte(')');
11911051 }
11921052 try w.writeByte('{');
1193 for (0..aggregate.fields.len) |field_index| {
1194 if (field_index > 0) try w.writeByte(',');
1195 try dg.renderValue(w, Value.fromInterned(
1196 switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
1197 .ptr => slice.ptr,
1198 .len => slice.len,
1199 else => unreachable,
1200 },
1201 ), initializer_type);
1202 }
1053 try dg.renderValue(w, .fromInterned(slice.ptr), initializer_type);
1054 try w.writeByte(',');
1055 try dg.renderValue(w, .fromInterned(slice.len), initializer_type);
12031056 try w.writeByte('}');
12041057 },
12051058 .ptr => {
1206 var arena = std.heap.ArenaAllocator.init(zcu.gpa);
1207 defer arena.deinit();
1208 const derivation = try val.pointerDerivation(arena.allocator(), pt, null);
1059 const derivation = try val.pointerDerivation(dg.arena, pt, null);
1060 try w.writeByte('(');
12091061 try dg.renderPointer(w, derivation, location);
1062 try w.writeByte(')');
12101063 },
1211 .opt => |opt| switch (ctype.info(ctype_pool)) {
1212 .basic => if (ctype.isBool()) try w.writeAll(switch (opt.val) {
1213 .none => "true",
1214 else => "false",
1215 }) else switch (opt.val) {
1064 .opt => |opt| switch (CType.classifyOptional(ty, zcu)) {
1065 .npv_payload => unreachable, // opv optional
1066 .opv_payload => {
1067 if (!location.isInitializer()) {
1068 try w.writeByte('(');
1069 try dg.renderType(w, ty);
1070 try w.writeByte(')');
1071 }
1072 try w.writeAll(switch (opt.val) {
1073 .none => "{.is_null = true}",
1074 else => "{.is_null = false}",
1075 });
1076 },
1077 .error_set => switch (opt.val) {
12161078 .none => try w.writeByte('0'),
1217 else => |payload| switch (ip.indexToKey(payload)) {
1218 .undef => |err_ty| try dg.renderUndefValue(
1219 w,
1220 .fromInterned(err_ty),
1221 location,
1222 ),
1223 .err => |err| try dg.renderErrorName(w, err.name),
1224 else => unreachable,
1225 },
1079 else => |payload_val| try dg.renderValue(w, .fromInterned(payload_val), location),
12261080 },
1227 .pointer => switch (opt.val) {
1081 .ptr_like => switch (opt.val) {
12281082 .none => try w.writeAll("NULL"),
1229 else => |payload| try dg.renderValue(w, Value.fromInterned(payload), location),
1083 else => |payload_val| try dg.renderValue(w, .fromInterned(payload_val), location),
12301084 },
1231 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
1232 .aggregate => |aggregate| {
1233 switch (opt.val) {
1234 .none => {},
1235 else => |payload| switch (aggregate.fields.at(0, ctype_pool).name.index) {
1236 .is_null, .payload => {},
1237 .ptr, .len => return dg.renderValue(
1238 w,
1239 Value.fromInterned(payload),
1240 location,
1241 ),
1242 else => unreachable,
1243 },
1244 }
1085 .slice_like => switch (opt.val) {
1086 .none => {
1087 if (!location.isInitializer()) {
1088 try w.writeByte('(');
1089 try dg.renderType(w, ty);
1090 try w.writeByte(')');
1091 }
1092 try w.writeAll("{NULL,");
1093 try dg.renderUndefValue(w, .usize, initializer_type);
1094 try w.writeByte('}');
1095 },
1096 else => |payload_val| try dg.renderValue(w, .fromInterned(payload_val), location),
1097 },
1098 .@"struct" => {
12451099 if (!location.isInitializer()) {
12461100 try w.writeByte('(');
1247 try dg.renderCType(w, ctype);
1101 try dg.renderType(w, ty);
12481102 try w.writeByte(')');
12491103 }
1250 try w.writeByte('{');
1251 for (0..aggregate.fields.len) |field_index| {
1252 if (field_index > 0) try w.writeByte(',');
1253 switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
1254 .is_null => try w.writeAll(switch (opt.val) {
1255 .none => "true",
1256 else => "false",
1257 }),
1258 .payload => switch (opt.val) {
1259 .none => try dg.renderUndefValue(
1260 w,
1261 ty.optionalChild(zcu),
1262 initializer_type,
1263 ),
1264 else => |payload| try dg.renderValue(
1265 w,
1266 Value.fromInterned(payload),
1267 initializer_type,
1268 ),
1269 },
1270 .ptr => try w.writeAll("NULL"),
1271 .len => try dg.renderUndefValue(w, .usize, initializer_type),
1272 else => unreachable,
1273 }
1104 switch (opt.val) {
1105 .none => {
1106 try w.writeAll("{ .is_null = true, .payload = ");
1107 try dg.renderUndefValue(w, ty.optionalChild(zcu), initializer_type);
1108 try w.writeAll(" }");
1109 },
1110 else => |payload_val| {
1111 try w.writeAll("{ .is_null = false, .payload = ");
1112 try dg.renderValue(w, .fromInterned(payload_val), initializer_type);
1113 try w.writeAll(" }");
1114 },
12741115 }
1275 try w.writeByte('}');
12761116 },
12771117 },
12781118 .aggregate => switch (ip.indexToKey(ty.toIntern())) {
12791119 .array_type, .vector_type => {
1280 if (location == .FunctionArgument) {
1120 if (!location.isInitializer()) {
12811121 try w.writeByte('(');
1282 try dg.renderCType(w, ctype);
1122 try dg.renderType(w, ty);
12831123 try w.writeByte(')');
12841124 }
1125 try w.writeByte('{');
12851126 const ai = ty.arrayInfo(zcu);
12861127 if (ai.elem_type.eql(.u8, zcu)) {
12871128 var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu)));
......@@ -1314,11 +1155,12 @@ pub const DeclGen = struct {
13141155 }
13151156 try w.writeByte('}');
13161157 }
1158 try w.writeByte('}');
13171159 },
13181160 .tuple_type => |tuple| {
13191161 if (!location.isInitializer()) {
13201162 try w.writeByte('(');
1321 try dg.renderCType(w, ctype);
1163 try dg.renderType(w, ty);
13221164 try w.writeByte(')');
13231165 }
13241166
......@@ -1354,7 +1196,7 @@ pub const DeclGen = struct {
13541196
13551197 if (!location.isInitializer()) {
13561198 try w.writeByte('(');
1357 try dg.renderCType(w, ctype);
1199 try dg.renderType(w, ty);
13581200 try w.writeByte(')');
13591201 }
13601202
......@@ -1385,69 +1227,60 @@ pub const DeclGen = struct {
13851227 .un => |un| {
13861228 const loaded_union = ip.loadUnionType(ty.toIntern());
13871229 if (un.tag == .none) {
1388 const backing_ty = try ty.externUnionBackingType(pt);
13891230 assert(loaded_union.layout == .@"extern");
1390 if (location == .StaticInitializer) {
1231 if (location == .static_initializer) {
13911232 return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});
13921233 }
13931234
13941235 const ptr_ty = try pt.singleConstPtrType(ty);
1395 try w.writeAll("*((");
1236 try w.writeAll("*(");
13961237 try dg.renderType(w, ptr_ty);
1397 try w.writeAll(")(");
1398 try dg.renderType(w, backing_ty);
1399 try w.writeAll("){");
1400 try dg.renderValue(w, Value.fromInterned(un.val), location);
1401 try w.writeAll("})");
1238 try w.writeAll(")&");
1239 // We need an lvalue for '&'.
1240 try dg.renderValueAsLvalue(w, .fromInterned(un.val));
14021241 } else {
14031242 if (!location.isInitializer()) {
14041243 try w.writeByte('(');
1405 try dg.renderCType(w, ctype);
1244 try dg.renderType(w, ty);
14061245 try w.writeByte(')');
14071246 }
1247 if (ty.unionHasAllZeroBitFieldTypes(zcu)) {
1248 assert(loaded_union.has_runtime_tag); // otherwise it does not have runtime bits
1249 try w.writeAll("{ .tag = ");
1250 try dg.renderValue(w, .fromInterned(un.tag), initializer_type);
1251 try w.writeAll(" }");
1252 return;
1253 }
14081254
1409 const field_index = zcu.unionTagFieldIndex(loaded_union, Value.fromInterned(un.tag)).?;
1410 const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
1411 const field_name = ip.loadEnumType(loaded_union.enum_tag_type).field_names.get(ip)[field_index];
1255 if (loaded_union.layout == .auto) try w.writeByte('{');
14121256
1413 const has_tag = loaded_union.has_runtime_tag;
1414 if (has_tag) try w.writeByte('{');
1415 const aggregate = ctype.info(ctype_pool).aggregate;
1416 for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| {
1417 if (outer_field_index > 0) try w.writeByte(',');
1418 switch (if (has_tag)
1419 aggregate.fields.at(outer_field_index, ctype_pool).name.index
1420 else
1421 .payload) {
1422 .tag => try dg.renderValue(
1423 w,
1424 Value.fromInterned(un.tag),
1425 initializer_type,
1426 ),
1427 .payload => {
1428 try w.writeByte('{');
1429 if (field_ty.hasRuntimeBits(zcu)) {
1430 try w.print(" .{f} = ", .{fmtIdentSolo(field_name.toSlice(ip))});
1431 try dg.renderValue(
1432 w,
1433 Value.fromInterned(un.val),
1434 initializer_type,
1435 );
1436 try w.writeByte(' ');
1437 } else for (0..loaded_union.field_types.len) |inner_field_index| {
1438 const inner_field_ty: Type = .fromInterned(
1439 loaded_union.field_types.get(ip)[inner_field_index],
1440 );
1441 if (!inner_field_ty.hasRuntimeBits(zcu)) continue;
1442 try dg.renderUndefValue(w, inner_field_ty, initializer_type);
1443 break;
1444 }
1445 try w.writeByte('}');
1446 },
1447 else => unreachable,
1448 }
1257 if (loaded_union.has_runtime_tag) {
1258 try w.writeAll(" .tag = ");
1259 try dg.renderValue(w, .fromInterned(un.tag), initializer_type);
1260 try w.writeAll(", .payload = ");
1261 }
1262
1263 const enum_tag_ty: Type = .fromInterned(loaded_union.enum_tag_type);
1264 const active_field_index = enum_tag_ty.enumTagFieldIndex(.fromInterned(un.tag), zcu).?;
1265 const active_field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[active_field_index]);
1266 if (active_field_ty.hasRuntimeBits(zcu)) {
1267 const active_field_name = enum_tag_ty.enumFieldName(active_field_index, zcu);
1268 try w.print("{{ .{f} = ", .{fmtIdentSolo(active_field_name.toSlice(ip))});
1269 try dg.renderValue(w, .fromInterned(un.val), initializer_type);
1270 try w.writeAll(" }");
1271 } else {
1272 const first_field_ty: Type = for (loaded_union.field_types.get(ip)) |field_ty_ip| {
1273 const field_ty: Type = .fromInterned(field_ty_ip);
1274 if (!field_ty.hasRuntimeBits(pt.zcu)) continue;
1275 break field_ty;
1276 } else unreachable;
1277 try w.writeByte('{');
1278 try dg.renderUndefValue(w, first_field_ty, initializer_type);
1279 try w.writeByte('}');
14491280 }
1450 if (has_tag) try w.writeByte('}');
1281
1282 if (loaded_union.has_runtime_tag) try w.writeByte(' ');
1283 if (loaded_union.layout == .auto) try w.writeByte('}');
14511284 }
14521285 },
14531286 }
......@@ -1463,11 +1296,10 @@ pub const DeclGen = struct {
14631296 const zcu = pt.zcu;
14641297 const ip = &zcu.intern_pool;
14651298 const target = &dg.mod.resolved_target.result;
1466 const ctype_pool = &dg.ctype_pool;
14671299
14681300 const initializer_type: ValueRenderLocation = switch (location) {
1469 .StaticInitializer => .StaticInitializer,
1470 else => .Initializer,
1301 .static_initializer => .static_initializer,
1302 else => .initializer,
14711303 };
14721304
14731305 const safety_on = switch (zcu.optimizeMode()) {
......@@ -1475,7 +1307,6 @@ pub const DeclGen = struct {
14751307 .ReleaseFast, .ReleaseSmall => false,
14761308 };
14771309
1478 const ctype = try dg.ctypeFromType(ty, location.toCTypeKind());
14791310 switch (ty.toIntern()) {
14801311 .c_longdouble_type,
14811312 .f16_type,
......@@ -1500,76 +1331,109 @@ pub const DeclGen = struct {
15001331 else => unreachable,
15011332 }
15021333 try w.writeAll(", ");
1503 try dg.renderUndefValue(w, repr_ty, .FunctionArgument);
1334 try dg.renderUndefValue(w, repr_ty, .other);
15041335 return w.writeByte(')');
15051336 },
15061337 .bool_type => try w.writeAll(if (safety_on) "0xaa" else "false"),
15071338 else => switch (ip.indexToKey(ty.toIntern())) {
1508 .simple_type,
1339 .simple_type, // anyerror, c_char (etc), usize, isize
15091340 .int_type,
15101341 .enum_type,
15111342 .error_set_type,
15121343 .inferred_error_set_type,
1513 => return w.print("{f}", .{
1514 try dg.fmtIntLiteralHex(try pt.undefValue(ty), location),
1515 }),
1344 => switch (CType.classifyInt(ty, zcu)) {
1345 .void => unreachable, // opv
1346 .small => |s| {
1347 const int = ty.intInfo(zcu);
1348 var buf: [std.math.big.int.calcTwosCompLimbCount(128)]std.math.big.Limb = undefined;
1349 var bigint: std.math.big.int.Mutable = .init(&buf, undefPattern(u128));
1350 bigint.truncate(bigint.toConst(), int.signedness, int.bits);
1351 const fmt_undef: FormatInt128 = .{
1352 .target = zcu.getTarget(),
1353 .int_cty = s,
1354 .val = bigint.toConst(),
1355 .is_global = location == .static_initializer,
1356 .base = 16,
1357 .case = .lower,
1358 };
1359 try w.print("{f}", .{fmt_undef});
1360 },
1361 .big => |big| {
1362 var buf: [std.math.big.int.calcTwosCompLimbCount(128)]std.math.big.Limb = undefined;
1363 var limb_bigint: std.math.big.int.Mutable = .init(&buf, undefPattern(u128));
1364 limb_bigint.truncate(limb_bigint.toConst(), .unsigned, big.limb_size.bits());
1365 const fmt_undef_limb: FormatInt128 = .{
1366 .target = zcu.getTarget(),
1367 .int_cty = big.limb_size.unsigned(),
1368 .val = limb_bigint.toConst(),
1369 .is_global = location == .static_initializer,
1370 .base = 16,
1371 .case = .lower,
1372 };
1373
1374 if (!location.isInitializer()) {
1375 try w.writeByte('(');
1376 try dg.renderType(w, ty);
1377 try w.writeByte(')');
1378 }
1379 try w.writeAll("{{");
1380 try w.print("{f}", .{fmt_undef_limb});
1381 for (1..big.limbs_len) |_| {
1382 try w.print(",{f}", .{fmt_undef_limb});
1383 }
1384 try w.writeAll("}}");
1385 },
1386 },
15161387 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
15171388 .one, .many, .c => {
15181389 try w.writeAll("((");
1519 try dg.renderCType(w, ctype);
1520 return w.print("){f})", .{
1521 try dg.fmtIntLiteralHex(.undef_usize, .Other),
1522 });
1390 try dg.renderType(w, ty);
1391 try w.writeByte(')');
1392 try dg.renderUndefValue(w, .usize, location);
1393 try w.writeByte(')');
15231394 },
15241395 .slice => {
15251396 if (!location.isInitializer()) {
15261397 try w.writeByte('(');
1527 try dg.renderCType(w, ctype);
1398 try dg.renderType(w, ty);
15281399 try w.writeByte(')');
15291400 }
15301401
1531 try w.writeAll("{(");
1532 const ptr_ty = ty.slicePtrFieldType(zcu);
1533 try dg.renderType(w, ptr_ty);
1534 return w.print("){f}, {0f}}}", .{
1535 try dg.fmtIntLiteralHex(.undef_usize, .Other),
1536 });
1402 try w.writeByte('{');
1403 try dg.renderUndefValue(w, ty.slicePtrFieldType(zcu), initializer_type);
1404 try w.writeByte(',');
1405 try dg.renderUndefValue(w, .usize, initializer_type);
1406 try w.writeByte('}');
15371407 },
15381408 },
1539 .opt_type => |child_type| switch (ctype.info(ctype_pool)) {
1540 .basic, .pointer => try dg.renderUndefValue(
1541 w,
1542 .fromInterned(if (ctype.isBool()) .bool_type else child_type),
1543 location,
1544 ),
1545 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
1546 .aggregate => |aggregate| {
1547 switch (aggregate.fields.at(0, ctype_pool).name.index) {
1548 .is_null, .payload => {},
1549 .ptr, .len => return dg.renderUndefValue(
1550 w,
1551 .fromInterned(child_type),
1552 location,
1553 ),
1554 else => unreachable,
1555 }
1409 .opt_type => |child_type| switch (CType.classifyOptional(ty, zcu)) {
1410 .npv_payload => unreachable, // opv optional
1411
1412 .error_set,
1413 .ptr_like,
1414 .slice_like,
1415 => try dg.renderUndefValue(w, .fromInterned(child_type), location),
1416
1417 .opv_payload => {
15561418 if (!location.isInitializer()) {
15571419 try w.writeByte('(');
1558 try dg.renderCType(w, ctype);
1420 try dg.renderType(w, ty);
15591421 try w.writeByte(')');
15601422 }
1561 try w.writeByte('{');
1562 for (0..aggregate.fields.len) |field_index| {
1563 if (field_index > 0) try w.writeByte(',');
1564 try dg.renderUndefValue(w, .fromInterned(
1565 switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
1566 .is_null => .bool_type,
1567 .payload => child_type,
1568 else => unreachable,
1569 },
1570 ), initializer_type);
1423 try w.writeAll(if (safety_on) "{.is_null=0xaa}" else "{.is_null=false}");
1424 },
1425
1426 .@"struct" => {
1427 if (!location.isInitializer()) {
1428 try w.writeByte('(');
1429 try dg.renderType(w, ty);
1430 try w.writeByte(')');
15711431 }
1572 try w.writeByte('}');
1432 try w.writeAll("{ .is_null = ");
1433 try dg.renderUndefValue(w, .bool, initializer_type);
1434 try w.writeAll(", .payload = ");
1435 try dg.renderUndefValue(w, .fromInterned(child_type), initializer_type);
1436 try w.writeAll(" }");
15731437 },
15741438 },
15751439 .struct_type => {
......@@ -1578,10 +1442,9 @@ pub const DeclGen = struct {
15781442 .auto, .@"extern" => {
15791443 if (!location.isInitializer()) {
15801444 try w.writeByte('(');
1581 try dg.renderCType(w, ctype);
1445 try dg.renderType(w, ty);
15821446 try w.writeByte(')');
15831447 }
1584
15851448 try w.writeByte('{');
15861449 var field_it = loaded_struct.iterateRuntimeOrder(ip);
15871450 var need_comma = false;
......@@ -1601,7 +1464,7 @@ pub const DeclGen = struct {
16011464 .tuple_type => |tuple_info| {
16021465 if (!location.isInitializer()) {
16031466 try w.writeByte('(');
1604 try dg.renderCType(w, ctype);
1467 try dg.renderType(w, ty);
16051468 try w.writeByte(')');
16061469 }
16071470
......@@ -1624,80 +1487,61 @@ pub const DeclGen = struct {
16241487 .auto, .@"extern" => {
16251488 if (!location.isInitializer()) {
16261489 try w.writeByte('(');
1627 try dg.renderCType(w, ctype);
1490 try dg.renderType(w, ty);
16281491 try w.writeByte(')');
16291492 }
16301493
1631 const has_tag = loaded_union.has_runtime_tag;
1632 if (has_tag) try w.writeByte('{');
1633 const aggregate = ctype.info(ctype_pool).aggregate;
1634 for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| {
1635 if (outer_field_index > 0) try w.writeByte(',');
1636 switch (if (has_tag)
1637 aggregate.fields.at(outer_field_index, ctype_pool).name.index
1638 else
1639 .payload) {
1640 .tag => try dg.renderUndefValue(
1641 w,
1642 .fromInterned(loaded_union.enum_tag_type),
1643 initializer_type,
1644 ),
1645 .payload => {
1646 try w.writeByte('{');
1647 for (0..loaded_union.field_types.len) |inner_field_index| {
1648 const inner_field_ty: Type = .fromInterned(
1649 loaded_union.field_types.get(ip)[inner_field_index],
1650 );
1651 if (!inner_field_ty.hasRuntimeBits(pt.zcu)) continue;
1652 try dg.renderUndefValue(
1653 w,
1654 inner_field_ty,
1655 initializer_type,
1656 );
1657 break;
1658 }
1659 try w.writeByte('}');
1660 },
1661 else => unreachable,
1662 }
1494 const first_field_ty: Type = for (loaded_union.field_types.get(ip)) |field_ty_ip| {
1495 const field_ty: Type = .fromInterned(field_ty_ip);
1496 if (!field_ty.hasRuntimeBits(pt.zcu)) continue;
1497 break field_ty;
1498 } else {
1499 assert(loaded_union.has_runtime_tag); // otherwise it does not have runtime bits
1500 try w.writeAll("{ .tag = ");
1501 try dg.renderUndefValue(w, .fromInterned(loaded_union.enum_tag_type), initializer_type);
1502 try w.writeAll(" }");
1503 return;
1504 };
1505
1506 if (loaded_union.layout == .auto) try w.writeByte('{');
1507
1508 if (loaded_union.has_runtime_tag) {
1509 try w.writeAll(" .tag = ");
1510 try dg.renderUndefValue(w, .fromInterned(loaded_union.enum_tag_type), initializer_type);
1511 try w.writeAll(", .payload = ");
16631512 }
1664 if (has_tag) try w.writeByte('}');
1513
1514 try w.writeByte('{');
1515 try dg.renderUndefValue(w, first_field_ty, initializer_type);
1516 try w.writeByte('}');
1517
1518 if (loaded_union.has_runtime_tag) try w.writeByte(' ');
1519 if (loaded_union.layout == .auto) try w.writeByte('}');
16651520 },
16661521 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
16671522 }
16681523 },
1669 .error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) {
1670 .basic => try dg.renderUndefValue(
1671 w,
1672 .fromInterned(error_union_type.error_set_type),
1673 location,
1674 ),
1675 .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable,
1676 .aggregate => |aggregate| {
1677 if (!location.isInitializer()) {
1678 try w.writeByte('(');
1679 try dg.renderCType(w, ctype);
1680 try w.writeByte(')');
1681 }
1682 try w.writeByte('{');
1683 for (0..aggregate.fields.len) |field_index| {
1684 if (field_index > 0) try w.writeByte(',');
1685 try dg.renderUndefValue(
1686 w,
1687 .fromInterned(
1688 switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
1689 .@"error" => error_union_type.error_set_type,
1690 .payload => error_union_type.payload_type,
1691 else => unreachable,
1692 },
1693 ),
1694 initializer_type,
1695 );
1696 }
1697 try w.writeByte('}');
1698 },
1524 .error_union_type => |error_union| {
1525 if (!location.isInitializer()) {
1526 try w.writeByte('(');
1527 try dg.renderType(w, ty);
1528 try w.writeByte(')');
1529 }
1530 try w.writeAll("{ .error = ");
1531 try dg.renderUndefValue(w, .fromInterned(error_union.error_set_type), initializer_type);
1532 if (Type.fromInterned(error_union.payload_type).hasRuntimeBits(zcu)) {
1533 try w.writeAll(", .payload = ");
1534 try dg.renderUndefValue(w, .fromInterned(error_union.payload_type), initializer_type);
1535 }
1536 try w.writeAll(" }");
16991537 },
17001538 .array_type, .vector_type => {
1539 if (!location.isInitializer()) {
1540 try w.writeByte('(');
1541 try dg.renderType(w, ty);
1542 try w.writeByte(')');
1543 }
1544 try w.writeByte('{');
17011545 const ai = ty.arrayInfo(zcu);
17021546 if (ai.elem_type.eql(.u8, zcu)) {
17031547 var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu)));
......@@ -1708,14 +1552,8 @@ pub const DeclGen = struct {
17081552 const s_u8: u8 = @intCast(s.toUnsignedInt(zcu));
17091553 if (s_u8 != 0) try literal.writeChar(s_u8);
17101554 }
1711 return literal.end();
1555 try literal.end();
17121556 } else {
1713 if (!location.isInitializer()) {
1714 try w.writeByte('(');
1715 try dg.renderCType(w, ctype);
1716 try w.writeByte(')');
1717 }
1718
17191557 try w.writeByte('{');
17201558 var index: u64 = 0;
17211559 while (index < ai.len) : (index += 1) {
......@@ -1726,8 +1564,9 @@ pub const DeclGen = struct {
17261564 if (index > 0) try w.writeAll(", ");
17271565 try dg.renderValue(w, s, location);
17281566 }
1729 return w.writeByte('}');
1567 try w.writeByte('}');
17301568 }
1569 try w.writeByte('}');
17311570 },
17321571 .anyframe_type,
17331572 .opaque_type,
......@@ -1762,10 +1601,11 @@ pub const DeclGen = struct {
17621601 w: *Writer,
17631602 fn_val: Value,
17641603 fn_align: InternPool.Alignment,
1765 kind: CType.Kind,
1604 kind: enum { forward_decl, definition },
17661605 name: union(enum) {
17671606 nav: InternPool.Nav.Index,
1768 fmt_ctype_pool_string: std.fmt.Alt(CTypePoolStringFormatData, formatCTypePoolString),
1607 nav_never_tail: InternPool.Nav.Index,
1608 nav_never_inline: InternPool.Nav.Index,
17691609 @"export": struct {
17701610 main_name: InternPool.NullTerminatedString,
17711611 extern_name: InternPool.NullTerminatedString,
......@@ -1776,14 +1616,12 @@ pub const DeclGen = struct {
17761616 const ip = &zcu.intern_pool;
17771617
17781618 const fn_ty = fn_val.typeOf(zcu);
1779 const fn_ctype = try dg.ctypeFromType(fn_ty, kind);
17801619
17811620 const fn_info = zcu.typeToFunc(fn_ty).?;
17821621 if (fn_info.cc == .naked) {
17831622 switch (kind) {
1784 .forward => try w.writeAll("zig_naked_decl "),
1785 .complete => try w.writeAll("zig_naked "),
1786 else => unreachable,
1623 .forward_decl => try w.writeAll("zig_naked_decl "),
1624 .definition => try w.writeAll("zig_naked "),
17871625 }
17881626 }
17891627
......@@ -1793,45 +1631,63 @@ pub const DeclGen = struct {
17931631 if (func_analysis.branch_hint == .cold)
17941632 try w.writeAll("zig_cold ");
17951633
1796 if (kind == .complete and func_analysis.disable_intrinsics or dg.mod.no_builtin)
1634 if (kind == .definition and func_analysis.disable_intrinsics or dg.mod.no_builtin)
17971635 try w.writeAll("zig_no_builtin ");
17981636 }
17991637
18001638 if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn ");
18011639
1802 var trailing = try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, fn_ctype, .suffix, .{});
1640 // While incomplete types are usually an acceptable substitute for "void", this is not true
1641 // in function return types, where "void" is the only incomplete type permitted.
1642 const actual_return_type: Type = .fromInterned(fn_info.return_type);
1643 const effective_return_type: Type = switch (actual_return_type.classify(zcu)) {
1644 .no_possible_value => .noreturn,
1645 .one_possible_value, .fully_comptime => .void, // no runtime bits
1646 .partially_comptime, .runtime => actual_return_type, // yes runtime bits
1647 };
18031648
1649 const ret_cty: CType = try .lower(effective_return_type, &dg.ctype_deps, dg.arena, zcu);
1650 try w.print("{f}", .{ret_cty.fmtDeclaratorPrefix(zcu)});
18041651 if (toCallingConvention(fn_info.cc, zcu)) |call_conv| {
1805 try w.print("{f}zig_callconv({s})", .{ trailing, call_conv });
1806 trailing = .maybe_space;
1652 try w.print("zig_callconv({s}) ", .{call_conv});
18071653 }
1808
1809 try w.print("{f}", .{trailing});
18101654 switch (name) {
1811 .nav => |nav| try dg.renderNavName(w, nav),
1812 .fmt_ctype_pool_string => |fmt| try w.print("{f}", .{fmt}),
1655 .nav => |nav| try renderNavName(w, nav, ip),
1656 .nav_never_tail => |nav| try w.print("zig_never_tail_{f}__{d}", .{
1657 fmtIdentUnsolo(ip.getNav(nav).name.toSlice(ip)), @intFromEnum(nav),
1658 }),
1659 .nav_never_inline => |nav| try w.print("zig_never_inline_{f}__{d}", .{
1660 fmtIdentUnsolo(ip.getNav(nav).name.toSlice(ip)), @intFromEnum(nav),
1661 }),
18131662 .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}),
18141663 }
1815
1816 try renderTypeSuffix(
1817 dg.pass,
1818 &dg.ctype_pool,
1819 zcu,
1820 w,
1821 fn_ctype,
1822 .suffix,
1823 CQualifiers.init(.{ .@"const" = switch (kind) {
1824 .forward => false,
1825 .complete => true,
1826 else => unreachable,
1827 } }),
1828 );
1664 {
1665 try w.writeByte('(');
1666 var c_param_index: u32 = 0;
1667 for (fn_info.param_types.get(ip)) |param_ty_ip| {
1668 const param_ty: Type = .fromInterned(param_ty_ip);
1669 if (!param_ty.hasRuntimeBits(zcu)) continue;
1670 if (c_param_index != 0) try w.writeAll(", ");
1671 try dg.renderTypeAndName(w, param_ty, .{ .arg = c_param_index }, .{
1672 .@"const" = kind == .definition,
1673 }, .none);
1674 c_param_index += 1;
1675 }
1676 if (fn_info.is_var_args) {
1677 if (c_param_index != 0) try w.writeAll(", ");
1678 try w.writeAll("...");
1679 } else if (c_param_index == 0) {
1680 try w.writeAll("void");
1681 }
1682 try w.writeByte(')');
1683 }
1684 try w.print("{f}", .{ret_cty.fmtDeclaratorSuffixIgnoreNonstring(zcu)});
18291685
18301686 switch (kind) {
1831 .forward => {
1687 .forward_decl => {
18321688 if (fn_align.toByteUnits()) |a| try w.print(" zig_align_fn({})", .{a});
18331689 switch (name) {
1834 .nav, .fmt_ctype_pool_string => {},
1690 .nav, .nav_never_tail, .nav_never_inline => {},
18351691 .@"export" => |@"export"| {
18361692 const extern_name = @"export".extern_name.toSlice(ip);
18371693 const is_mangled = isMangledIdent(extern_name, true);
......@@ -1855,38 +1711,16 @@ pub const DeclGen = struct {
18551711 },
18561712 }
18571713 },
1858 .complete => {},
1859 else => unreachable,
1714 .definition => {},
18601715 }
18611716 }
18621717
1863 fn ctypeFromType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType {
1864 defer std.debug.assert(dg.scratch.items.len == 0);
1865 return dg.ctype_pool.fromType(dg.gpa, &dg.scratch, ty, dg.pt, dg.mod, kind);
1866 }
1867
1868 fn byteSize(dg: *DeclGen, ctype: CType) u64 {
1869 return ctype.byteSize(&dg.ctype_pool, dg.mod);
1870 }
1871
1872 /// Renders a type as a single identifier, generating intermediate typedefs
1873 /// if necessary.
1874 ///
1875 /// This is guaranteed to be valid in both typedefs and declarations/definitions.
1876 ///
1877 /// There are three type formats in total that we support rendering:
1878 /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) |
1879 /// |---------------------|-----------------|---------------------|
1880 /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
1881 /// | `renderType` | "uint8_t *" | "uint8_t *[10]" |
1882 ///
1883 fn renderType(dg: *DeclGen, w: *Writer, t: Type) Error!void {
1884 try dg.renderCType(w, try dg.ctypeFromType(t, .complete));
1885 }
1886
1887 fn renderCType(dg: *DeclGen, w: *Writer, ctype: CType) Error!void {
1888 _ = try renderTypePrefix(dg.pass, &dg.ctype_pool, dg.pt.zcu, w, ctype, .suffix, .{});
1889 try renderTypeSuffix(dg.pass, &dg.ctype_pool, dg.pt.zcu, w, ctype, .suffix, .{});
1718 /// Renders the C lowering of the given Zig type to `w`. This renders the type name---to render
1719 /// a declarator with this type, see instead `renderTypeAndName`.
1720 fn renderType(dg: *DeclGen, w: *Writer, ty: Type) (Writer.Error || Allocator.Error)!void {
1721 const zcu = dg.pt.zcu;
1722 const cty: CType = try .lower(ty, &dg.ctype_deps, dg.arena, zcu);
1723 try w.print("{f}", .{cty.fmtTypeName(zcu)});
18901724 }
18911725
18921726 const IntCastContext = union(enum) {
......@@ -1990,7 +1824,7 @@ pub const DeclGen = struct {
19901824 try w.writeAll("zig_lo_");
19911825 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
19921826 try w.writeByte('(');
1993 try context.writeValue(dg, w, .FunctionArgument);
1827 try context.writeValue(dg, w, .other);
19941828 try w.writeByte(')');
19951829 } else if (dest_bits > 64 and src_bits <= 64) {
19961830 try w.writeAll("zig_make_");
......@@ -2001,7 +1835,7 @@ pub const DeclGen = struct {
20011835 try dg.renderType(w, src_eff_ty);
20021836 try w.writeByte(')');
20031837 }
2004 try context.writeValue(dg, w, .FunctionArgument);
1838 try context.writeValue(dg, w, .other);
20051839 try w.writeByte(')');
20061840 } else {
20071841 assert(!src_is_ptr);
......@@ -2010,23 +1844,16 @@ pub const DeclGen = struct {
20101844 try w.writeAll("(zig_hi_");
20111845 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
20121846 try w.writeByte('(');
2013 try context.writeValue(dg, w, .FunctionArgument);
1847 try context.writeValue(dg, w, .other);
20141848 try w.writeAll("), zig_lo_");
20151849 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
20161850 try w.writeByte('(');
2017 try context.writeValue(dg, w, .FunctionArgument);
1851 try context.writeValue(dg, w, .other);
20181852 try w.writeAll("))");
20191853 }
20201854 }
20211855
2022 /// Renders a type and name in field declaration/definition format.
2023 ///
2024 /// There are three type formats in total that we support rendering:
2025 /// | Function | Example 1 (*u8) | Example 2 ([10]*u8) |
2026 /// |---------------------|-----------------|---------------------|
2027 /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
2028 /// | `renderType` | "uint8_t *" | "uint8_t *[10]" |
2029 ///
1856 /// Renders to `w` a C declarator whose type is the C lowering of the given Zig type.
20301857 fn renderTypeAndName(
20311858 dg: *DeclGen,
20321859 w: *Writer,
......@@ -2034,73 +1861,47 @@ pub const DeclGen = struct {
20341861 name: CValue,
20351862 qualifiers: CQualifiers,
20361863 alignment: Alignment,
2037 kind: CType.Kind,
2038 ) !void {
2039 try dg.renderCTypeAndName(
2040 w,
2041 try dg.ctypeFromType(ty, kind),
2042 name,
2043 qualifiers,
2044 CType.AlignAs.fromAlignment(.{
2045 .@"align" = alignment,
2046 .abi = ty.abiAlignment(dg.pt.zcu),
2047 }),
2048 );
2049 }
2050
2051 fn renderCTypeAndName(
2052 dg: *DeclGen,
2053 w: *Writer,
2054 ctype: CType,
2055 name: CValue,
2056 qualifiers: CQualifiers,
2057 alignas: CType.AlignAs,
20581864 ) !void {
20591865 const zcu = dg.pt.zcu;
2060 switch (alignas.abiOrder()) {
2061 .lt => try w.print("zig_under_align({}) ", .{alignas.toByteUnits()}),
1866 const ip = &zcu.intern_pool;
1867 const cty: CType = try .lower(ty, &dg.ctype_deps, dg.arena, zcu);
1868 try w.print("{f}", .{cty.fmtDeclaratorPrefix(zcu)});
1869 if (alignment != .none) switch (alignment.order(ty.abiAlignment(zcu))) {
1870 .lt => try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?}),
20621871 .eq => {},
2063 .gt => try w.print("zig_align({}) ", .{alignas.toByteUnits()}),
2064 }
2065
2066 try w.print("{f}", .{
2067 try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, ctype, .suffix, qualifiers),
2068 });
2069 try dg.writeName(w, name);
2070 try renderTypeSuffix(dg.pass, &dg.ctype_pool, zcu, w, ctype, .suffix, .{});
2071 if (ctype.isNonString(&dg.ctype_pool)) try w.writeAll(" zig_nonstring");
2072 }
2073
2074 fn writeName(dg: *DeclGen, w: *Writer, c_value: CValue) !void {
2075 switch (c_value) {
1872 .gt => try w.print("zig_align({d}) ", .{alignment.toByteUnits().?}),
1873 };
1874 if (qualifiers.@"const") try w.writeAll("const ");
1875 if (qualifiers.@"volatile") try w.writeAll("volatile ");
1876 if (qualifiers.restrict) try w.writeAll("restrict ");
1877 switch (name) {
20761878 .new_local, .local => |i| try w.print("t{d}", .{i}),
1879 .arg => |i| try w.print("a{d}", .{i}),
20771880 .constant => |uav| try renderUavName(w, uav),
2078 .nav => |nav| try dg.renderNavName(w, nav),
1881 .nav => |nav| try renderNavName(w, nav, ip),
20791882 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
20801883 else => unreachable,
20811884 }
1885 try w.print("{f}", .{cty.fmtDeclaratorSuffix(zcu)});
20821886 }
20831887
20841888 fn writeCValue(dg: *DeclGen, w: *Writer, c_value: CValue) Error!void {
20851889 switch (c_value) {
20861890 .none, .new_local, .local, .local_ref => unreachable,
20871891 .constant => |uav| try renderUavName(w, uav),
2088 .arg, .arg_array => unreachable,
1892 .arg => unreachable,
20891893 .field => |i| try w.print("f{d}", .{i}),
2090 .nav => |nav| try dg.renderNavName(w, nav),
1894 .nav => |nav| try renderNavName(w, nav, &dg.pt.zcu.intern_pool),
20911895 .nav_ref => |nav| {
20921896 try w.writeByte('&');
2093 try dg.renderNavName(w, nav);
1897 try renderNavName(w, nav, &dg.pt.zcu.intern_pool);
20941898 },
2095 .undef => |ty| try dg.renderUndefValue(w, ty, .Other),
1899 .undef => |ty| try dg.renderUndefValue(w, ty, .other),
20961900 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
20971901 .payload_identifier => |ident| try w.print("{f}.{f}", .{
20981902 fmtIdentSolo("payload"),
20991903 fmtIdentSolo(ident),
21001904 }),
2101 .ctype_pool_string => |string| try w.print("{f}", .{
2102 fmtCTypePoolString(string, &dg.ctype_pool, true),
2103 }),
21041905 }
21051906 }
21061907
......@@ -2112,16 +1913,14 @@ pub const DeclGen = struct {
21121913 .local_ref,
21131914 .constant,
21141915 .arg,
2115 .arg_array,
2116 .ctype_pool_string,
21171916 => unreachable,
21181917 .field => |i| try w.print("f{d}", .{i}),
21191918 .nav => |nav| {
21201919 try w.writeAll("(*");
2121 try dg.renderNavName(w, nav);
1920 try renderNavName(w, nav, &dg.pt.zcu.intern_pool);
21221921 try w.writeByte(')');
21231922 },
2124 .nav_ref => |nav| try dg.renderNavName(w, nav),
1923 .nav_ref => |nav| try renderNavName(w, nav, &dg.pt.zcu.intern_pool),
21251924 .undef => unreachable,
21261925 .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}),
21271926 .payload_identifier => |ident| try w.print("(*{f}.{f})", .{
......@@ -2157,8 +1956,6 @@ pub const DeclGen = struct {
21571956 .field,
21581957 .undef,
21591958 .arg,
2160 .arg_array,
2161 .ctype_pool_string,
21621959 => unreachable,
21631960 .nav, .identifier, .payload_identifier => {
21641961 try dg.writeCValue(w, c_value);
......@@ -2172,101 +1969,36 @@ pub const DeclGen = struct {
21721969 try dg.writeCValue(w, member);
21731970 }
21741971
2175 fn renderFwdDecl(
2176 dg: *DeclGen,
2177 nav_index: InternPool.Nav.Index,
2178 flags: packed struct {
2179 is_const: bool,
2180 is_threadlocal: bool,
2181 linkage: std.builtin.GlobalLinkage,
2182 visibility: std.builtin.SymbolVisibility,
2183 },
2184 ) !void {
1972 fn renderTypeForBuiltinFnName(dg: *DeclGen, w: *Writer, ty: Type) !void {
21851973 const zcu = dg.pt.zcu;
2186 const ip = &zcu.intern_pool;
2187 const nav = ip.getNav(nav_index);
2188 const fwd = &dg.fwd_decl.writer;
2189 try fwd.writeAll(switch (flags.linkage) {
2190 .internal => "static ",
2191 .strong, .weak, .link_once => "zig_extern ",
2192 });
2193 switch (flags.linkage) {
2194 .internal, .strong => {},
2195 .weak => try fwd.writeAll("zig_weak_linkage "),
2196 .link_once => return dg.fail("TODO: CBE: implement linkonce linkage?", .{}),
1974 switch (ty.zigTypeTag(zcu)) {
1975 .bool => return w.writeAll("u8"),
1976 .float => return w.print("f{d}", .{ty.floatBits(zcu.getTarget())}),
1977 else => {},
21971978 }
2198 switch (flags.linkage) {
2199 .internal => {},
2200 .strong, .weak, .link_once => try fwd.print("zig_visibility({s}) ", .{@tagName(flags.visibility)}),
1979 if (ty.isPtrAtRuntime(zcu)) {
1980 return w.print("p{d}", .{zcu.getTarget().ptrBitWidth()});
22011981 }
2202 if (flags.is_threadlocal and !dg.mod.single_threaded) try fwd.writeAll("zig_threadlocal ");
2203 try dg.renderTypeAndName(
2204 fwd,
2205 .fromInterned(nav.typeOf(ip)),
2206 .{ .nav = nav_index },
2207 CQualifiers.init(.{ .@"const" = flags.is_const }),
2208 nav.getAlignment(),
2209 .complete,
2210 );
2211 try fwd.writeAll(";\n");
2212 }
2213
2214 fn renderNavName(dg: *DeclGen, w: *Writer, nav_index: InternPool.Nav.Index) !void {
2215 const zcu = dg.pt.zcu;
2216 const ip = &zcu.intern_pool;
2217 const nav = ip.getNav(nav_index);
2218 if (nav.getExtern(ip)) |@"extern"| {
2219 try w.print("{f}", .{
2220 fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
2221 });
2222 } else {
2223 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
2224 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
2225 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
2226 try w.print("{f}__{d}", .{
2227 fmtIdentUnsolo(fqn_slice[0..@min(fqn_slice.len, 100)]),
2228 @intFromEnum(nav_index),
2229 });
2230 }
2231 }
2232
2233 fn renderUavName(w: *Writer, uav: Value) !void {
2234 try w.print("__anon_{d}", .{@intFromEnum(uav.toIntern())});
2235 }
2236
2237 fn renderTypeForBuiltinFnName(dg: *DeclGen, w: *Writer, ty: Type) !void {
2238 try dg.renderCTypeForBuiltinFnName(w, try dg.ctypeFromType(ty, .complete));
2239 }
2240
2241 fn renderCTypeForBuiltinFnName(dg: *DeclGen, w: *Writer, ctype: CType) !void {
2242 switch (ctype.info(&dg.ctype_pool)) {
2243 else => |ctype_info| try w.print("{c}{d}", .{
2244 if (ctype.isBool())
2245 signAbbrev(.unsigned)
2246 else if (ctype.isInteger())
2247 signAbbrev(ctype.signedness(dg.mod))
2248 else if (ctype.isFloat())
2249 @as(u8, 'f')
2250 else if (ctype_info == .pointer)
2251 @as(u8, 'p')
2252 else
2253 return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for {s} type", .{@tagName(ctype_info)}),
2254 if (ctype.isFloat()) ctype.floatActiveBits(dg.mod) else dg.byteSize(ctype) * 8,
1982 switch (CType.classifyInt(ty, zcu)) {
1983 .void => unreachable, // opv
1984 .small => try w.print("{c}{d}", .{
1985 signAbbrev(ty.intInfo(zcu).signedness),
1986 ty.abiSize(zcu) * 8,
22551987 }),
2256 .array => try w.writeAll("big"),
1988 .big => try w.writeAll("big"),
22571989 }
22581990 }
22591991
22601992 fn renderBuiltinInfo(dg: *DeclGen, w: *Writer, ty: Type, info: BuiltinInfo) !void {
2261 const ctype = try dg.ctypeFromType(ty, .complete);
2262 const is_big = ctype.info(&dg.ctype_pool) == .array;
1993 const pt = dg.pt;
1994 const zcu = pt.zcu;
1995
1996 const is_big = lowersToBigInt(ty, zcu);
22631997 switch (info) {
22641998 .none => if (!is_big) return,
22651999 .bits => {},
22662000 }
22672001
2268 const pt = dg.pt;
2269 const zcu = pt.zcu;
22702002 const int_info: std.builtin.Type.Int = if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else .{
22712003 .signedness = .unsigned,
22722004 .bits = @intCast(ty.bitSize(zcu)),
......@@ -2275,7 +2007,7 @@ pub const DeclGen = struct {
22752007 if (is_big) try w.print(", {}", .{int_info.signedness == .signed});
22762008 try w.print(", {f}", .{try dg.fmtIntLiteralDec(
22772009 try pt.intValue(if (is_big) .u16 else .u8, int_info.bits),
2278 .FunctionArgument,
2010 .other,
22792011 )});
22802012 }
22812013
......@@ -2286,15 +2018,13 @@ pub const DeclGen = struct {
22862018 base: u8,
22872019 case: std.fmt.Case,
22882020 ) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) {
2289 const zcu = dg.pt.zcu;
2290 const kind = loc.toCTypeKind();
2291 const ty = val.typeOf(zcu);
2021 // If there's a bigint type involved, mark a dependency on it.
2022 const cty: CType = try .lower(val.typeOf(dg.pt.zcu), &dg.ctype_deps, dg.arena, dg.pt.zcu);
22922023 return .{ .data = .{
22932024 .dg = dg,
2294 .int_info = ty.intInfo(zcu),
2295 .kind = kind,
2296 .ctype = try dg.ctypeFromType(ty, kind),
2025 .loc = loc,
22972026 .val = val,
2027 .cty = cty,
22982028 .base = base,
22992029 .case = case,
23002030 } };
......@@ -2317,339 +2047,11 @@ pub const DeclGen = struct {
23172047 }
23182048};
23192049
2320const CTypeFix = enum { prefix, suffix };
2321const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict });
2322const Const = CQualifiers.init(.{ .@"const" = true });
2323const RenderCTypeTrailing = enum {
2324 no_space,
2325 maybe_space,
2326
2327 pub fn format(self: @This(), w: *Writer) Writer.Error!void {
2328 switch (self) {
2329 .no_space => {},
2330 .maybe_space => try w.writeByte(' '),
2331 }
2332 }
2050const CQualifiers = packed struct {
2051 @"const": bool = false,
2052 @"volatile": bool = false,
2053 restrict: bool = false,
23332054};
2334fn renderAlignedTypeName(w: *Writer, ctype: CType) !void {
2335 try w.print("anon__aligned_{d}", .{@intFromEnum(ctype.index)});
2336}
2337fn renderFwdDeclTypeName(
2338 zcu: *Zcu,
2339 w: *Writer,
2340 ctype: CType,
2341 fwd_decl: CType.Info.FwdDecl,
2342 attributes: []const u8,
2343) !void {
2344 const ip = &zcu.intern_pool;
2345 try w.print("{s} {s}", .{ @tagName(fwd_decl.tag), attributes });
2346 switch (fwd_decl.name) {
2347 .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}),
2348 .index => |index| try w.print("{f}__{d}", .{
2349 fmtIdentUnsolo(Type.fromInterned(index).containerTypeName(ip).toSlice(&zcu.intern_pool)),
2350 @intFromEnum(index),
2351 }),
2352 }
2353}
2354fn renderTypePrefix(
2355 pass: DeclGen.Pass,
2356 ctype_pool: *const CType.Pool,
2357 zcu: *Zcu,
2358 w: *Writer,
2359 ctype: CType,
2360 parent_fix: CTypeFix,
2361 qualifiers: CQualifiers,
2362) Writer.Error!RenderCTypeTrailing {
2363 var trailing = RenderCTypeTrailing.maybe_space;
2364 switch (ctype.info(ctype_pool)) {
2365 .basic => |basic_info| try w.writeAll(@tagName(basic_info)),
2366
2367 .pointer => |pointer_info| {
2368 try w.print("{f}*", .{try renderTypePrefix(
2369 pass,
2370 ctype_pool,
2371 zcu,
2372 w,
2373 pointer_info.elem_ctype,
2374 .prefix,
2375 CQualifiers.init(.{
2376 .@"const" = pointer_info.@"const",
2377 .@"volatile" = pointer_info.@"volatile",
2378 }),
2379 )});
2380 trailing = .no_space;
2381 },
2382
2383 .aligned => switch (pass) {
2384 .nav => |nav| try w.print("nav__{d}_{d}", .{
2385 @intFromEnum(nav), @intFromEnum(ctype.index),
2386 }),
2387 .uav => |uav| try w.print("uav__{d}_{d}", .{
2388 @intFromEnum(uav), @intFromEnum(ctype.index),
2389 }),
2390 .flush => try renderAlignedTypeName(w, ctype),
2391 },
2392
2393 .array, .vector => |sequence_info| {
2394 const child_trailing = try renderTypePrefix(
2395 pass,
2396 ctype_pool,
2397 zcu,
2398 w,
2399 sequence_info.elem_ctype,
2400 .suffix,
2401 qualifiers,
2402 );
2403 switch (parent_fix) {
2404 .prefix => {
2405 try w.print("{f}(", .{child_trailing});
2406 return .no_space;
2407 },
2408 .suffix => return child_trailing,
2409 }
2410 },
2411
2412 .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) {
2413 .anon => switch (pass) {
2414 .nav => |nav| try w.print("nav__{d}_{d}", .{
2415 @intFromEnum(nav), @intFromEnum(ctype.index),
2416 }),
2417 .uav => |uav| try w.print("uav__{d}_{d}", .{
2418 @intFromEnum(uav), @intFromEnum(ctype.index),
2419 }),
2420 .flush => try renderFwdDeclTypeName(zcu, w, ctype, fwd_decl_info, ""),
2421 },
2422 .index => try renderFwdDeclTypeName(zcu, w, ctype, fwd_decl_info, ""),
2423 },
2424
2425 .aggregate => |aggregate_info| switch (aggregate_info.name) {
2426 .anon => {
2427 try w.print("{s} {s}", .{
2428 @tagName(aggregate_info.tag),
2429 if (aggregate_info.@"packed") "zig_packed(" else "",
2430 });
2431 try renderFields(zcu, w, ctype_pool, aggregate_info, 1);
2432 if (aggregate_info.@"packed") try w.writeByte(')');
2433 },
2434 .fwd_decl => |fwd_decl| return renderTypePrefix(
2435 pass,
2436 ctype_pool,
2437 zcu,
2438 w,
2439 fwd_decl,
2440 parent_fix,
2441 qualifiers,
2442 ),
2443 },
2444
2445 .function => |function_info| {
2446 const child_trailing = try renderTypePrefix(
2447 pass,
2448 ctype_pool,
2449 zcu,
2450 w,
2451 function_info.return_ctype,
2452 .suffix,
2453 .{},
2454 );
2455 switch (parent_fix) {
2456 .prefix => {
2457 try w.print("{f}(", .{child_trailing});
2458 return .no_space;
2459 },
2460 .suffix => return child_trailing,
2461 }
2462 },
2463 }
2464 var qualifier_it = qualifiers.iterator();
2465 while (qualifier_it.next()) |qualifier| {
2466 try w.print("{f}{s}", .{ trailing, @tagName(qualifier) });
2467 trailing = .maybe_space;
2468 }
2469 return trailing;
2470}
2471fn renderTypeSuffix(
2472 pass: DeclGen.Pass,
2473 ctype_pool: *const CType.Pool,
2474 zcu: *Zcu,
2475 w: *Writer,
2476 ctype: CType,
2477 parent_fix: CTypeFix,
2478 qualifiers: CQualifiers,
2479) Writer.Error!void {
2480 switch (ctype.info(ctype_pool)) {
2481 .basic, .aligned, .fwd_decl, .aggregate => {},
2482 .pointer => |pointer_info| try renderTypeSuffix(
2483 pass,
2484 ctype_pool,
2485 zcu,
2486 w,
2487 pointer_info.elem_ctype,
2488 .prefix,
2489 .{},
2490 ),
2491 .array, .vector => |sequence_info| {
2492 switch (parent_fix) {
2493 .prefix => try w.writeByte(')'),
2494 .suffix => {},
2495 }
2496
2497 try w.print("[{}]", .{sequence_info.len});
2498 try renderTypeSuffix(pass, ctype_pool, zcu, w, sequence_info.elem_ctype, .suffix, .{});
2499 },
2500 .function => |function_info| {
2501 switch (parent_fix) {
2502 .prefix => try w.writeByte(')'),
2503 .suffix => {},
2504 }
2505
2506 try w.writeByte('(');
2507 var need_comma = false;
2508 for (0..function_info.param_ctypes.len) |param_index| {
2509 const param_type = function_info.param_ctypes.at(param_index, ctype_pool);
2510 if (need_comma) try w.writeAll(", ");
2511 need_comma = true;
2512 const trailing =
2513 try renderTypePrefix(pass, ctype_pool, zcu, w, param_type, .suffix, qualifiers);
2514 if (qualifiers.contains(.@"const")) try w.print("{f}a{d}", .{ trailing, param_index });
2515 try renderTypeSuffix(pass, ctype_pool, zcu, w, param_type, .suffix, .{});
2516 }
2517 if (function_info.varargs) {
2518 if (need_comma) try w.writeAll(", ");
2519 need_comma = true;
2520 try w.writeAll("...");
2521 }
2522 if (!need_comma) try w.writeAll("void");
2523 try w.writeByte(')');
2524
2525 try renderTypeSuffix(pass, ctype_pool, zcu, w, function_info.return_ctype, .suffix, .{});
2526 },
2527 }
2528}
2529fn renderFields(
2530 zcu: *Zcu,
2531 w: *Writer,
2532 ctype_pool: *const CType.Pool,
2533 aggregate_info: CType.Info.Aggregate,
2534 indent: usize,
2535) !void {
2536 try w.writeAll("{\n");
2537 for (0..aggregate_info.fields.len) |field_index| {
2538 const field_info = aggregate_info.fields.at(field_index, ctype_pool);
2539 try w.splatByteAll(' ', indent + 1);
2540 switch (field_info.alignas.abiOrder()) {
2541 .lt => {
2542 std.debug.assert(aggregate_info.@"packed");
2543 if (field_info.alignas.@"align" != .@"1") try w.print("zig_under_align({}) ", .{
2544 field_info.alignas.toByteUnits(),
2545 });
2546 },
2547 .eq => if (aggregate_info.@"packed" and field_info.alignas.@"align" != .@"1")
2548 try w.print("zig_align({}) ", .{field_info.alignas.toByteUnits()}),
2549 .gt => {
2550 std.debug.assert(field_info.alignas.@"align" != .@"1");
2551 try w.print("zig_align({}) ", .{field_info.alignas.toByteUnits()});
2552 },
2553 }
2554 const trailing = try renderTypePrefix(
2555 .flush,
2556 ctype_pool,
2557 zcu,
2558 w,
2559 field_info.ctype,
2560 .suffix,
2561 .{},
2562 );
2563 try w.print("{f}{f}", .{ trailing, fmtCTypePoolString(field_info.name, ctype_pool, true) });
2564 try renderTypeSuffix(.flush, ctype_pool, zcu, w, field_info.ctype, .suffix, .{});
2565 if (field_info.ctype.isNonString(ctype_pool)) try w.writeAll(" zig_nonstring");
2566 try w.writeAll(";\n");
2567 }
2568 try w.splatByteAll(' ', indent);
2569 try w.writeByte('}');
2570}
2571
2572pub fn genTypeDecl(
2573 zcu: *Zcu,
2574 w: *Writer,
2575 global_ctype_pool: *const CType.Pool,
2576 global_ctype: CType,
2577 pass: DeclGen.Pass,
2578 decl_ctype_pool: *const CType.Pool,
2579 decl_ctype: CType,
2580 found_existing: bool,
2581) !void {
2582 switch (global_ctype.info(global_ctype_pool)) {
2583 .basic, .pointer, .array, .vector, .function => {},
2584 .aligned => |aligned_info| {
2585 if (!found_existing) {
2586 std.debug.assert(aligned_info.alignas.abiOrder().compare(.lt));
2587 try w.print("typedef zig_under_align({d}) ", .{aligned_info.alignas.toByteUnits()});
2588 try w.print("{f}", .{try renderTypePrefix(
2589 .flush,
2590 global_ctype_pool,
2591 zcu,
2592 w,
2593 aligned_info.ctype,
2594 .suffix,
2595 .{},
2596 )});
2597 try renderAlignedTypeName(w, global_ctype);
2598 try renderTypeSuffix(.flush, global_ctype_pool, zcu, w, aligned_info.ctype, .suffix, .{});
2599 try w.writeAll(";\n");
2600 }
2601 switch (pass) {
2602 .nav, .uav => {
2603 try w.writeAll("typedef ");
2604 _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, w, global_ctype, .suffix, .{});
2605 try w.writeByte(' ');
2606 _ = try renderTypePrefix(pass, decl_ctype_pool, zcu, w, decl_ctype, .suffix, .{});
2607 try w.writeAll(";\n");
2608 },
2609 .flush => {},
2610 }
2611 },
2612 .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) {
2613 .anon => switch (pass) {
2614 .nav, .uav => {
2615 try w.writeAll("typedef ");
2616 _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, w, global_ctype, .suffix, .{});
2617 try w.writeByte(' ');
2618 _ = try renderTypePrefix(pass, decl_ctype_pool, zcu, w, decl_ctype, .suffix, .{});
2619 try w.writeAll(";\n");
2620 },
2621 .flush => {},
2622 },
2623 .index => |index| if (!found_existing) {
2624 const ip = &zcu.intern_pool;
2625 const ty: Type = .fromInterned(index);
2626 _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, w, global_ctype, .suffix, .{});
2627 try w.writeByte(';');
2628 const file_scope = ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip);
2629 if (!zcu.fileByIndex(file_scope).mod.?.strip) try w.print(" /* {f} */", .{
2630 ty.containerTypeName(ip).fmt(ip),
2631 });
2632 try w.writeByte('\n');
2633 },
2634 },
2635 .aggregate => |aggregate_info| switch (aggregate_info.name) {
2636 .anon => {},
2637 .fwd_decl => |fwd_decl| if (!found_existing) {
2638 try renderFwdDeclTypeName(
2639 zcu,
2640 w,
2641 fwd_decl,
2642 fwd_decl.info(global_ctype_pool).fwd_decl,
2643 if (aggregate_info.@"packed") "zig_packed(" else "",
2644 );
2645 try w.writeByte(' ');
2646 try renderFields(zcu, w, global_ctype_pool, aggregate_info, 0);
2647 if (aggregate_info.@"packed") try w.writeByte(')');
2648 try w.writeAll(";\n");
2649 },
2650 },
2651 }
2652}
26532055
26542056pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {
26552057 for (zcu.global_assembly.values()) |asm_source| {
......@@ -2657,200 +2059,128 @@ pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {
26572059 }
26582060}
26592061
2660pub fn genErrDecls(o: *Object) Error!void {
2661 const pt = o.dg.pt;
2662 const zcu = pt.zcu;
2062pub fn genErrDecls(
2063 zcu: *const Zcu,
2064 w: *Writer,
2065 slice_const_u8_sentinel_0_type_name: []const u8,
2066) Writer.Error!void {
26632067 const ip = &zcu.intern_pool;
2664 const w = &o.code.writer;
26652068
2666 var max_name_len: usize = 0;
2667 // do not generate an invalid empty enum when the global error set is empty
26682069 const names = ip.global_error_set.getNamesFromMainThread();
2070 // Don't generate an invalid empty enum if the global error set is empty!
26692071 if (names.len > 0) {
2670 try w.writeAll("enum {");
2671 o.indent();
2672 try o.newline();
2072 try w.writeAll("enum {\n");
26732073 for (names, 1..) |name_nts, value| {
2674 const name = name_nts.toSlice(ip);
2675 max_name_len = @max(name.len, max_name_len);
2676 const err_val = try pt.intern(.{ .err = .{
2677 .ty = .anyerror_type,
2678 .name = name_nts,
2679 } });
2680 try o.dg.renderValue(w, Value.fromInterned(err_val), .Other);
2681 try w.print(" = {d}u,", .{value});
2682 try o.newline();
2074 try w.writeByte(' ');
2075 try renderErrorName(w, name_nts.toSlice(ip));
2076 try w.print(" = {d}u,\n", .{value});
26832077 }
2684 try o.outdent();
2685 try w.writeAll("};");
2686 try o.newline();
2687 }
2688 const array_identifier = "zig_errorName";
2689 const name_prefix = array_identifier ++ "_";
2690 const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len);
2691 defer o.dg.gpa.free(name_buf);
2692
2693 @memcpy(name_buf[0..name_prefix.len], name_prefix);
2694 for (names) |name| {
2695 const name_slice = name.toSlice(ip);
2696 @memcpy(name_buf[name_prefix.len..][0..name_slice.len], name_slice);
2697 const identifier = name_buf[0 .. name_prefix.len + name_slice.len];
2698
2699 const name_ty = try pt.arrayType(.{
2700 .len = name_slice.len,
2701 .child = .u8_type,
2702 .sentinel = .zero_u8,
2703 });
2704 const name_val = try pt.intern(.{ .aggregate = .{
2705 .ty = name_ty.toIntern(),
2706 .storage = .{ .bytes = name.toString() },
2707 } });
2078 try w.writeAll("};\n");
2079 }
27082080
2709 try w.writeAll("static ");
2710 try o.dg.renderTypeAndName(
2711 w,
2712 name_ty,
2713 .{ .identifier = identifier },
2714 Const,
2715 .none,
2716 .complete,
2081 for (names) |name_nts| {
2082 const name = name_nts.toSlice(ip);
2083 try w.print(
2084 "static uint8_t const zig_errorName_{f}[] = {f};\n",
2085 .{ fmtIdentUnsolo(name), fmtStringLiteral(name, 0) },
27172086 );
2718 try w.writeAll(" = ");
2719 try o.dg.renderValue(w, Value.fromInterned(name_val), .StaticInitializer);
2720 try w.writeByte(';');
2721 try o.newline();
27222087 }
27232088
2724 const name_array_ty = try pt.arrayType(.{
2725 .len = 1 + names.len,
2726 .child = .slice_const_u8_sentinel_0_type,
2727 });
2728
2729 try w.writeAll("static ");
2730 try o.dg.renderTypeAndName(
2731 w,
2732 name_array_ty,
2733 .{ .identifier = array_identifier },
2734 Const,
2735 .none,
2736 .complete,
2089 try w.print(
2090 "static {s} const zig_errorName[{d}] = {{",
2091 .{ slice_const_u8_sentinel_0_type_name, names.len },
27372092 );
2738 try w.writeAll(" = {");
2739 for (names, 1..) |name_nts, val| {
2093 if (names.len > 0) try w.writeByte('\n');
2094 for (names) |name_nts| {
27402095 const name = name_nts.toSlice(ip);
2741 if (val > 1) try w.writeAll(", ");
2742 try w.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{
2743 fmtIdentUnsolo(name),
2744 try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, name.len), .StaticInitializer),
2096 try w.print(
2097 " {{zig_errorName_{f},{d}}},\n",
2098 .{ fmtIdentUnsolo(name), name.len },
2099 );
2100 }
2101 try w.writeAll("};\n");
2102}
2103
2104pub fn genTagNameFn(
2105 zcu: *const Zcu,
2106 w: *Writer,
2107 slice_const_u8_sentinel_0_type_name: []const u8,
2108 enum_ty: Type,
2109 enum_type_name: []const u8,
2110) Writer.Error!void {
2111 const ip = &zcu.intern_pool;
2112 const loaded_enum = ip.loadEnumType(enum_ty.toIntern());
2113 assert(loaded_enum.field_names.len > 0);
2114 if (Type.fromInterned(loaded_enum.int_tag_type).bitSize(zcu) > 64) {
2115 @panic("TODO CBE: tagName for enum over 128 bits");
2116 }
2117
2118 try w.print("static {s} zig_tagName_{f}__{d}({s} tag) {{\n", .{
2119 slice_const_u8_sentinel_0_type_name,
2120 fmtIdentUnsolo(loaded_enum.name.toSlice(ip)),
2121 @intFromEnum(enum_ty.toIntern()),
2122 enum_type_name,
2123 });
2124 for (loaded_enum.field_names.get(ip), 0..) |field_name, field_index| {
2125 try w.print(" static uint8_t const name{d}[] = {f};\n", .{
2126 field_index, fmtStringLiteral(field_name.toSlice(ip), 0),
2127 });
2128 }
2129
2130 try w.writeAll(" switch (tag) {\n");
2131 const field_values = loaded_enum.field_values.get(ip);
2132 for (loaded_enum.field_names.get(ip), 0..) |field_name, field_index| {
2133 const field_int: u64 = int: {
2134 if (field_values.len == 0) break :int field_index;
2135 const field_val: Value = .fromInterned(field_values[field_index]);
2136 break :int field_val.toUnsignedInt(zcu);
2137 };
2138 try w.print(" case {d}: return ({s}){{name{d},{d}}};\n", .{
2139 field_int,
2140 slice_const_u8_sentinel_0_type_name,
2141 field_index,
2142 field_name.toSlice(ip).len,
27452143 });
27462144 }
2747 try w.writeAll("};");
2748 try o.newline();
2145 try w.writeAll(
2146 \\ }
2147 \\ zig_unreachable();
2148 \\}
2149 \\
2150 );
27492151}
27502152
2751pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFnMap.Entry) Error!void {
2752 const pt = o.dg.pt;
2753 const zcu = pt.zcu;
2153pub fn genLazyCallModifierFn(
2154 dg: *DeclGen,
2155 fn_nav: InternPool.Nav.Index,
2156 kind: enum { never_tail, never_inline },
2157 w: *Writer,
2158) Error!void {
2159 const zcu = dg.pt.zcu;
27542160 const ip = &zcu.intern_pool;
2755 const ctype_pool = &o.dg.ctype_pool;
2756 const w = &o.code.writer;
2757 const key = lazy_fn.key_ptr.*;
2758 const val = lazy_fn.value_ptr;
2759 switch (key) {
2760 .tag_name => |enum_ty_ip| {
2761 const enum_ty: Type = .fromInterned(enum_ty_ip);
2762 const name_slice_ty: Type = .slice_const_u8_sentinel_0;
2763
2764 try w.writeAll("static ");
2765 try o.dg.renderType(w, name_slice_ty);
2766 try w.print(" {f}(", .{val.fn_name.fmt(lazy_ctype_pool)});
2767 try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, .none, .complete);
2768 try w.writeAll(") {");
2769 o.indent();
2770 try o.newline();
2771 try w.writeAll("switch (tag) {");
2772 o.indent();
2773 try o.newline();
2774 const tag_names = enum_ty.enumFields(zcu);
2775 for (0..tag_names.len) |tag_index| {
2776 const tag_name = tag_names.get(ip)[tag_index];
2777 const tag_name_len = tag_name.length(ip);
2778 const tag_val = try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index));
2779
2780 const name_ty = try pt.arrayType(.{
2781 .len = tag_name_len,
2782 .child = .u8_type,
2783 .sentinel = .zero_u8,
2784 });
2785 const name_val = try pt.intern(.{ .aggregate = .{
2786 .ty = name_ty.toIntern(),
2787 .storage = .{ .bytes = tag_name.toString() },
2788 } });
27892161
2790 try w.print("case {f}: {{", .{
2791 try o.dg.fmtIntLiteralDec(tag_val.intFromEnum(zcu), .Other),
2792 });
2793 o.indent();
2794 try o.newline();
2795 try w.writeAll("static ");
2796 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, .none, .complete);
2797 try w.writeAll(" = ");
2798 try o.dg.renderValue(w, Value.fromInterned(name_val), .StaticInitializer);
2799 try w.writeByte(';');
2800 try o.newline();
2801 try w.writeAll("return (");
2802 try o.dg.renderType(w, name_slice_ty);
2803 try w.print("){{{f}, {f}}};", .{
2804 fmtIdentUnsolo("name"),
2805 try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, tag_name_len), .Other),
2806 });
2807 try o.newline();
2808 try o.outdent();
2809 try w.writeByte('}');
2810 try o.newline();
2811 }
2812 try o.outdent();
2813 try w.writeByte('}');
2814 try o.newline();
2815 try airUnreach(o);
2816 try o.outdent();
2817 try w.writeByte('}');
2818 try o.newline();
2819 },
2820 .never_tail, .never_inline => |fn_nav_index| {
2821 const fn_val = zcu.navValue(fn_nav_index);
2822 const fn_ctype = try o.dg.ctypeFromType(fn_val.typeOf(zcu), .complete);
2823 const fn_info = fn_ctype.info(ctype_pool).function;
2824 const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool, true);
2825
2826 const fwd = &o.dg.fwd_decl.writer;
2827 try fwd.print("static zig_{s} ", .{@tagName(key)});
2828 try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{
2829 .fmt_ctype_pool_string = fn_name,
2830 });
2831 try fwd.writeAll(";\n");
2162 const fn_val = zcu.navValue(fn_nav);
28322163
2833 try w.print("zig_{s} ", .{@tagName(key)});
2834 try o.dg.renderFunctionSignature(w, fn_val, .none, .complete, .{
2835 .fmt_ctype_pool_string = fn_name,
2836 });
2837 try w.writeAll(" {");
2838 o.indent();
2839 try o.newline();
2840 try w.writeAll("return ");
2841 try o.dg.renderNavName(w, fn_nav_index);
2842 try w.writeByte('(');
2843 for (0..fn_info.param_ctypes.len) |arg| {
2844 if (arg > 0) try w.writeAll(", ");
2845 try w.print("a{d}", .{arg});
2846 }
2847 try w.writeAll(");");
2848 try o.newline();
2849 try o.outdent();
2850 try w.writeByte('}');
2851 try o.newline();
2852 },
2164 try w.print("static zig_{t} ", .{kind});
2165 try dg.renderFunctionSignature(w, fn_val, .none, .definition, switch (kind) {
2166 .never_tail => .{ .nav_never_tail = fn_nav },
2167 .never_inline => .{ .nav_never_inline = fn_nav },
2168 });
2169 try w.writeAll(" {\n return ");
2170 try renderNavName(w, fn_nav, ip);
2171 try w.writeByte('(');
2172 {
2173 const func_type = ip.indexToKey(fn_val.typeOf(zcu).toIntern()).func_type;
2174 var c_param_index: u32 = 0;
2175 for (func_type.param_types.get(ip)) |param_ty_ip| {
2176 const param_ty: Type = .fromInterned(param_ty_ip);
2177 if (!param_ty.hasRuntimeBits(zcu)) continue;
2178 if (c_param_index != 0) try w.writeAll(", ");
2179 try w.print("a{d}", .{c_param_index});
2180 c_param_index += 1;
2181 }
28532182 }
2183 try w.writeAll(");\n}\n");
28542184}
28552185
28562186pub fn generate(
......@@ -2869,110 +2199,109 @@ pub fn generate(
28692199
28702200 const func = zcu.funcInfo(func_index);
28712201
2202 var arena: std.heap.ArenaAllocator = .init(gpa);
2203 defer arena.deinit();
2204
28722205 var function: Function = .{
28732206 .value_map = .init(gpa),
28742207 .air = air.*,
28752208 .liveness = liveness.*.?,
28762209 .func_index = func_index,
2877 .object = .{
2878 .dg = .{
2879 .gpa = gpa,
2880 .pt = pt,
2881 .mod = zcu.navFileScope(func.owner_nav).mod.?,
2882 .error_msg = null,
2883 .pass = .{ .nav = func.owner_nav },
2884 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
2885 .expected_block = null,
2886 .fwd_decl = .init(gpa),
2887 .ctype_pool = .empty,
2888 .scratch = .empty,
2889 .uavs = .empty,
2890 },
2891 .code_header = .init(gpa),
2892 .code = .init(gpa),
2893 .indent_counter = 0,
2210 .dg = .{
2211 .gpa = gpa,
2212 .arena = arena.allocator(),
2213 .pt = pt,
2214 .mod = zcu.navFileScope(func.owner_nav).mod.?,
2215 .error_msg = null,
2216 .owner_nav = func.owner_nav.toOptional(),
2217 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
2218 .expected_block = null,
2219 .ctype_deps = .empty,
2220 .uavs = .empty,
28942221 },
2895 .lazy_fns = .empty,
2222 .code = .init(gpa),
2223 .indent_counter = 0,
2224 .need_tag_name_funcs = .empty,
2225 .need_never_tail_funcs = .empty,
2226 .need_never_inline_funcs = .empty,
28962227 };
28972228 defer {
2898 function.object.code_header.deinit();
2899 function.object.code.deinit();
2900 function.object.dg.fwd_decl.deinit();
2901 function.object.dg.ctype_pool.deinit(gpa);
2902 function.object.dg.scratch.deinit(gpa);
2903 function.object.dg.uavs.deinit(gpa);
2229 function.code.deinit();
2230 function.dg.ctype_deps.deinit(gpa);
2231 function.dg.uavs.deinit(gpa);
29042232 function.deinit();
29052233 }
2906 try function.object.dg.ctype_pool.init(gpa);
29072234
2908 genFunc(&function) catch |err| switch (err) {
2909 error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?),
2910 error.OutOfMemory => return error.OutOfMemory,
2235 var fwd_decl: Writer.Allocating = .init(gpa);
2236 defer fwd_decl.deinit();
2237
2238 var code_header: Writer.Allocating = .init(gpa);
2239 defer code_header.deinit();
2240
2241 genFunc(&function, &fwd_decl.writer, &code_header.writer) catch |err| switch (err) {
2242 error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.dg.error_msg.?),
29112243 error.WriteFailed => return error.OutOfMemory,
2244 error.OutOfMemory => |e| return e,
29122245 };
29132246
29142247 var mir: Mir = .{
2915 .uavs = .empty,
2916 .code = &.{},
2917 .code_header = &.{},
29182248 .fwd_decl = &.{},
2919 .ctype_pool = .empty,
2920 .lazy_fns = .empty,
2249 .code_header = &.{},
2250 .code = &.{},
2251 .ctype_deps = function.dg.ctype_deps.move(),
2252 .need_uavs = function.dg.uavs.move(),
2253 .need_tag_name_funcs = function.need_tag_name_funcs.move(),
2254 .need_never_tail_funcs = function.need_never_tail_funcs.move(),
2255 .need_never_inline_funcs = function.need_never_inline_funcs.move(),
29212256 };
29222257 errdefer mir.deinit(gpa);
2923 mir.uavs = function.object.dg.uavs.move();
2924 mir.code_header = try function.object.code_header.toOwnedSlice();
2925 mir.code = try function.object.code.toOwnedSlice();
2926 mir.fwd_decl = try function.object.dg.fwd_decl.toOwnedSlice();
2927 mir.ctype_pool = function.object.dg.ctype_pool.move();
2928 mir.lazy_fns = function.lazy_fns.move();
2258 mir.fwd_decl = try fwd_decl.toOwnedSlice();
2259 mir.code_header = try code_header.toOwnedSlice();
2260 mir.code = try function.code.toOwnedSlice();
29292261 return mir;
29302262}
29312263
2932pub fn genFunc(f: *Function) Error!void {
2264pub fn genFunc(f: *Function, fwd_decl_writer: *Writer, header_writer: *Writer) Error!void {
29332265 const tracy = trace(@src());
29342266 defer tracy.end();
29352267
2936 const o = &f.object;
2937 const zcu = o.dg.pt.zcu;
2268 const zcu = f.dg.pt.zcu;
29382269 const ip = &zcu.intern_pool;
2939 const gpa = o.dg.gpa;
2940 const nav_index = o.dg.pass.nav;
2270 const gpa = f.dg.gpa;
2271 const nav_index = f.dg.owner_nav.unwrap().?;
29412272 const nav_val = zcu.navValue(nav_index);
29422273 const nav = ip.getNav(nav_index);
29432274
2944 const fwd = &o.dg.fwd_decl.writer;
2945 try fwd.writeAll("static ");
2946 try o.dg.renderFunctionSignature(
2947 fwd,
2275 try fwd_decl_writer.writeAll("static ");
2276 try f.dg.renderFunctionSignature(
2277 fwd_decl_writer,
29482278 nav_val,
29492279 nav.status.fully_resolved.alignment,
2950 .forward,
2280 .forward_decl,
29512281 .{ .nav = nav_index },
29522282 );
2953 try fwd.writeAll(";\n");
2283 try fwd_decl_writer.writeAll(";\n");
29542284
2955 const ch = &o.code_header.writer;
29562285 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|
2957 try ch.print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)});
2958 try o.dg.renderFunctionSignature(
2959 ch,
2286 try header_writer.print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)});
2287 try f.dg.renderFunctionSignature(
2288 header_writer,
29602289 nav_val,
29612290 .none,
2962 .complete,
2291 .definition,
29632292 .{ .nav = nav_index },
29642293 );
2965 try ch.writeAll(" {\n ");
2294 try header_writer.writeAll(" {\n ");
29662295
29672296 f.free_locals_map.clearRetainingCapacity();
29682297
29692298 const main_body = f.air.getMainBody();
2970 o.indent();
2299 f.indent();
29712300 try genBodyResolveState(f, undefined, &.{}, main_body, true);
2972 try o.outdent();
2973 try o.code.writer.writeByte('}');
2974 try o.newline();
2975 if (o.dg.expected_block) |_|
2301 try f.outdent();
2302 try f.code.writer.writeByte('}');
2303 try f.newline();
2304 if (f.dg.expected_block) |_|
29762305 return f.fail("runtime code not allowed in naked function", .{});
29772306
29782307 // Take advantage of the free_locals map to bucket locals per type. All
......@@ -2986,155 +2315,204 @@ pub fn genFunc(f: *Function) Error!void {
29862315 if (!should_emit) continue;
29872316 const local = f.locals.items[local_index];
29882317 log.debug("inserting local {d} into free_locals", .{local_index});
2989 const gop = try free_locals.getOrPut(gpa, local.getType());
2318 const gop = try free_locals.getOrPut(gpa, local);
29902319 if (!gop.found_existing) gop.value_ptr.* = .{};
29912320 try gop.value_ptr.putNoClobber(gpa, local_index, {});
29922321 }
29932322
29942323 const SortContext = struct {
2324 zcu: *const Zcu,
29952325 keys: []const LocalType,
29962326
29972327 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
2998 const lhs_ty = ctx.keys[lhs_index];
2999 const rhs_ty = ctx.keys[rhs_index];
3000 return lhs_ty.alignas.order(rhs_ty.alignas).compare(.gt);
2328 const lhs = ctx.keys[lhs_index];
2329 const rhs = ctx.keys[rhs_index];
2330 const lhs_align = switch (lhs.alignment) {
2331 .none => lhs.type.abiAlignment(ctx.zcu),
2332 else => |a| a,
2333 };
2334 const rhs_align = switch (rhs.alignment) {
2335 .none => rhs.type.abiAlignment(ctx.zcu),
2336 else => |a| a,
2337 };
2338 return Alignment.compareStrict(lhs_align, .gt, rhs_align);
30012339 }
30022340 };
3003 free_locals.sort(SortContext{ .keys = free_locals.keys() });
2341 free_locals.sort(SortContext{
2342 .zcu = zcu,
2343 .keys = free_locals.keys(),
2344 });
30042345
30052346 for (free_locals.values()) |list| {
30062347 for (list.keys()) |local_index| {
30072348 const local = f.locals.items[local_index];
3008 try o.dg.renderCTypeAndName(ch, local.ctype, .{ .local = local_index }, .{}, local.flags.alignas);
3009 try ch.writeAll(";\n ");
2349 try f.dg.renderTypeAndName(header_writer, local.type, .{ .local = local_index }, .{}, local.alignment);
2350 try header_writer.writeAll(";\n ");
30102351 }
30112352 }
30122353}
30132354
3014pub fn genDecl(o: *Object) Error!void {
2355pub fn genDecl(dg: *DeclGen, w: *Writer) Error!void {
30152356 const tracy = trace(@src());
30162357 defer tracy.end();
30172358
3018 const pt = o.dg.pt;
2359 const pt = dg.pt;
30192360 const zcu = pt.zcu;
30202361 const ip = &zcu.intern_pool;
3021 const nav = ip.getNav(o.dg.pass.nav);
2362 const nav = ip.getNav(dg.owner_nav.unwrap().?);
30222363 const nav_ty: Type = .fromInterned(nav.typeOf(ip));
30232364
3024 if (!nav_ty.hasRuntimeBits(zcu)) return;
3025 switch (ip.indexToKey(nav.status.fully_resolved.val)) {
3026 .@"extern" => |@"extern"| {
3027 if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{
3028 .is_const = @"extern".is_const,
3029 .is_threadlocal = @"extern".is_threadlocal,
3030 .linkage = @"extern".linkage,
3031 .visibility = @"extern".visibility,
3032 });
2365 const is_const: bool, const is_threadlocal: bool, const init_val: Value = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
2366 else => .{ true, false, .fromInterned(nav.status.fully_resolved.val) },
2367 .variable => |v| .{ false, v.is_threadlocal, .fromInterned(v.init) },
2368 .@"extern" => return,
2369 };
30332370
3034 const fwd = &o.dg.fwd_decl.writer;
3035 try fwd.writeAll("zig_extern ");
3036 try o.dg.renderFunctionSignature(
3037 fwd,
3038 Value.fromInterned(nav.status.fully_resolved.val),
3039 nav.status.fully_resolved.alignment,
3040 .forward,
3041 .{ .@"export" = .{
3042 .main_name = nav.name,
3043 .extern_name = nav.name,
3044 } },
3045 );
3046 try fwd.writeAll(";\n");
3047 },
3048 .variable => |variable| {
3049 try o.dg.renderFwdDecl(o.dg.pass.nav, .{
3050 .is_const = false,
3051 .is_threadlocal = variable.is_threadlocal,
3052 .linkage = .internal,
3053 .visibility = .default,
3054 });
3055 const w = &o.code.writer;
3056 if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
3057 if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
3058 try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});
3059 try o.dg.renderTypeAndName(
3060 w,
3061 nav_ty,
3062 .{ .nav = o.dg.pass.nav },
3063 .{},
3064 nav.status.fully_resolved.alignment,
3065 .complete,
3066 );
3067 try w.writeAll(" = ");
3068 try o.dg.renderValue(w, Value.fromInterned(variable.init), .StaticInitializer);
3069 try w.writeByte(';');
3070 try o.newline();
3071 },
3072 else => try genDeclValue(
3073 o,
3074 Value.fromInterned(nav.status.fully_resolved.val),
3075 .{ .nav = o.dg.pass.nav },
3076 nav.status.fully_resolved.alignment,
3077 nav.status.fully_resolved.@"linksection",
3078 ),
2371 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s| {
2372 try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});
30792373 }
2374
2375 // We don't bother underaligning---it's unnecessary and hurts compatibility.
2376 const a = nav.status.fully_resolved.alignment;
2377 if (a != .none and a.compareStrict(.gt, nav_ty.abiAlignment(zcu))) {
2378 try w.print("zig_align({d}) ", .{a.toByteUnits().?});
2379 }
2380
2381 try genDeclValue(dg, w, .{
2382 .name = .{ .nav = dg.owner_nav.unwrap().? },
2383 .@"const" = is_const,
2384 .@"threadlocal" = is_threadlocal,
2385 .init_val = init_val,
2386 });
30802387}
2388pub fn genDeclFwd(dg: *DeclGen, w: *Writer) Error!void {
2389 const tracy = trace(@src());
2390 defer tracy.end();
30812391
3082pub fn genDeclValue(
3083 o: *Object,
3084 val: Value,
3085 decl_c_value: CValue,
3086 alignment: Alignment,
3087 @"linksection": InternPool.OptionalNullTerminatedString,
3088) Error!void {
3089 const zcu = o.dg.pt.zcu;
3090 const ty = val.typeOf(zcu);
2392 const pt = dg.pt;
2393 const zcu = pt.zcu;
2394 const ip = &zcu.intern_pool;
2395 const nav = ip.getNav(dg.owner_nav.unwrap().?);
2396 const nav_ty: Type = .fromInterned(nav.typeOf(ip));
30912397
3092 const fwd = &o.dg.fwd_decl.writer;
3093 try fwd.writeAll("static ");
3094 try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete);
3095 try fwd.writeAll(";\n");
2398 const is_const: bool, const is_threadlocal: bool, const init_val: Value = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
2399 else => .{ true, false, .fromInterned(nav.status.fully_resolved.val) },
2400 .variable => |v| .{ false, v.is_threadlocal, .fromInterned(v.init) },
30962401
3097 const w = &o.code.writer;
3098 if (@"linksection".toSlice(&zcu.intern_pool)) |s|
3099 try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});
3100 try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete);
2402 .@"extern" => |@"extern"| switch (nav_ty.zigTypeTag(zcu)) {
2403 .@"fn" => {
2404 try w.writeAll("zig_extern ");
2405 try dg.renderFunctionSignature(
2406 w,
2407 Value.fromInterned(nav.status.fully_resolved.val),
2408 nav.status.fully_resolved.alignment,
2409 .forward_decl,
2410 .{ .@"export" = .{
2411 .main_name = nav.name,
2412 .extern_name = nav.name,
2413 } },
2414 );
2415 try w.writeAll(";\n");
2416 return;
2417 },
2418 else => {
2419 switch (@"extern".linkage) {
2420 .internal => try w.writeAll("static "),
2421 .strong => try w.print("zig_extern zig_visibility({t}) ", .{@"extern".visibility}),
2422 .weak => try w.print("zig_extern zig_weak_linkage zig_visibility({t}) ", .{@"extern".visibility}),
2423 .link_once => return dg.fail("TODO: CBE: implement linkonce linkage?", .{}),
2424 }
2425 if (@"extern".is_threadlocal and !dg.mod.single_threaded) {
2426 try w.writeAll("zig_threadlocal ");
2427 }
2428 try dg.renderTypeAndName(
2429 w,
2430 .fromInterned(nav.typeOf(ip)),
2431 .{ .nav = dg.owner_nav.unwrap().? },
2432 .{ .@"const" = @"extern".is_const },
2433 nav.getAlignment(),
2434 );
2435 try w.writeAll(";\n");
2436 return;
2437 },
2438 },
2439 };
2440
2441 // We don't bother underaligning---it's unnecessary and hurts compatibility.
2442 const a = nav.status.fully_resolved.alignment;
2443 if (a != .none and a.compareStrict(.gt, nav_ty.abiAlignment(zcu))) {
2444 try w.print("zig_align({d}) ", .{a.toByteUnits().?});
2445 }
2446
2447 try genDeclValueFwd(dg, w, .{
2448 .name = .{ .nav = dg.owner_nav.unwrap().? },
2449 .@"const" = is_const,
2450 .@"threadlocal" = is_threadlocal,
2451 .init_val = init_val,
2452 });
2453}
2454pub fn genDeclValue(dg: *DeclGen, w: *Writer, options: struct {
2455 name: CValue,
2456 @"const": bool,
2457 @"threadlocal": bool,
2458 init_val: Value,
2459}) Error!void {
2460 const zcu = dg.pt.zcu;
2461 const ty = options.init_val.typeOf(zcu);
2462 if (options.@"threadlocal" and !dg.mod.single_threaded) {
2463 try w.writeAll("zig_threadlocal ");
2464 }
2465 try dg.renderTypeAndName(w, ty, options.name, .{ .@"const" = options.@"const" }, .none);
31012466 try w.writeAll(" = ");
3102 try o.dg.renderValue(w, val, .StaticInitializer);
3103 try w.writeByte(';');
3104 try o.newline();
2467 try dg.renderValue(w, options.init_val, .static_initializer);
2468 try w.writeAll(";\n");
2469}
2470pub fn genDeclValueFwd(dg: *DeclGen, w: *Writer, options: struct {
2471 name: CValue,
2472 @"const": bool,
2473 @"threadlocal": bool,
2474 init_val: Value,
2475}) Error!void {
2476 const zcu = dg.pt.zcu;
2477 const ty = options.init_val.typeOf(zcu);
2478 try w.writeAll("static ");
2479 if (options.@"threadlocal" and !dg.mod.single_threaded) {
2480 try w.writeAll("zig_threadlocal ");
2481 }
2482 try dg.renderTypeAndName(w, ty, options.name, .{ .@"const" = options.@"const" }, .none);
2483 try w.writeAll(";\n");
31052484}
31062485
3107pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void {
2486pub fn genExports(dg: *DeclGen, w: *Writer, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void {
31082487 const zcu = dg.pt.zcu;
31092488 const ip = &zcu.intern_pool;
3110 const fwd = &dg.fwd_decl.writer;
31112489
31122490 const main_name = export_indices[0].ptr(zcu).opts.name;
3113 try fwd.writeAll("#define ");
2491 try w.writeAll("#define ");
31142492 switch (exported) {
3115 .nav => |nav| try dg.renderNavName(fwd, nav),
3116 .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)),
2493 .nav => |nav| try renderNavName(w, nav, ip),
2494 .uav => |uav| try renderUavName(w, Value.fromInterned(uav)),
31172495 }
3118 try fwd.writeByte(' ');
3119 try fwd.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))});
3120 try fwd.writeByte('\n');
2496 try w.writeByte(' ');
2497 try w.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))});
2498 try w.writeByte('\n');
31212499
31222500 const exported_val = exported.getValue(zcu);
31232501 if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| {
31242502 const @"export" = export_index.ptr(zcu);
3125 try fwd.writeAll("zig_extern ");
3126 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn ");
2503 try w.writeAll("zig_extern ");
2504 if (@"export".opts.linkage == .weak) try w.writeAll("zig_weak_linkage_fn ");
31272505 try dg.renderFunctionSignature(
3128 fwd,
2506 w,
31292507 exported.getValue(zcu),
31302508 exported.getAlign(zcu),
3131 .forward,
2509 .forward_decl,
31322510 .{ .@"export" = .{
31332511 .main_name = main_name,
31342512 .extern_name = @"export".opts.name,
31352513 } },
31362514 );
3137 try fwd.writeAll(";\n");
2515 try w.writeAll(";\n");
31382516 };
31392517 const is_const = switch (ip.indexToKey(exported_val.toIntern())) {
31402518 .func => unreachable,
......@@ -3144,39 +2522,38 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
31442522 };
31452523 for (export_indices) |export_index| {
31462524 const @"export" = export_index.ptr(zcu);
3147 try fwd.writeAll("zig_extern ");
3148 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage ");
3149 if (@"export".opts.section.toSlice(ip)) |s| try fwd.print("zig_linksection({f}) ", .{
2525 try w.writeAll("zig_extern ");
2526 if (@"export".opts.linkage == .weak) try w.writeAll("zig_weak_linkage ");
2527 if (@"export".opts.section.toSlice(ip)) |s| try w.print("zig_linksection({f}) ", .{
31502528 fmtStringLiteral(s, null),
31512529 });
31522530 const extern_name = @"export".opts.name.toSlice(ip);
31532531 const is_mangled = isMangledIdent(extern_name, true);
31542532 const is_export = @"export".opts.name != main_name;
31552533 try dg.renderTypeAndName(
3156 fwd,
2534 w,
31572535 exported.getValue(zcu).typeOf(zcu),
31582536 .{ .identifier = extern_name },
3159 CQualifiers.init(.{ .@"const" = is_const }),
2537 .{ .@"const" = is_const },
31602538 exported.getAlign(zcu),
3161 .complete,
31622539 );
31632540 if (is_mangled and is_export) {
3164 try fwd.print(" zig_mangled_export({f}, {f}, {f})", .{
2541 try w.print(" zig_mangled_export({f}, {f}, {f})", .{
31652542 fmtIdentSolo(extern_name),
31662543 fmtStringLiteral(extern_name, null),
31672544 fmtStringLiteral(main_name.toSlice(ip), null),
31682545 });
31692546 } else if (is_mangled) {
3170 try fwd.print(" zig_mangled({f}, {f})", .{
2547 try w.print(" zig_mangled({f}, {f})", .{
31712548 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),
31722549 });
31732550 } else if (is_export) {
3174 try fwd.print(" zig_export({f}, {f})", .{
2551 try w.print(" zig_export({f}, {f})", .{
31752552 fmtStringLiteral(main_name.toSlice(ip), null),
31762553 fmtStringLiteral(extern_name, null),
31772554 });
31782555 }
3179 try fwd.writeAll(";\n");
2556 try w.writeAll(";\n");
31802557 }
31812558}
31822559
......@@ -3185,15 +2562,15 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
31852562/// have been added to `free_locals_map`. For a version of this function that restores this state,
31862563/// see `genBodyResolveState`.
31872564fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
3188 const w = &f.object.code.writer;
2565 const w = &f.code.writer;
31892566 if (body.len == 0) {
31902567 try w.writeAll("{}");
31912568 } else {
31922569 try w.writeByte('{');
3193 f.object.indent();
3194 try f.object.newline();
2570 f.indent();
2571 try f.newline();
31952572 try genBodyInner(f, body);
3196 try f.object.outdent();
2573 try f.outdent();
31972574 try w.writeByte('}');
31982575 }
31992576}
......@@ -3207,13 +2584,13 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
32072584fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void {
32082585 if (body.len == 0) {
32092586 // Don't go to the expense of cloning everything!
3210 if (!inner) try f.object.code.writer.writeAll("{}");
2587 if (!inner) try f.code.writer.writeAll("{}");
32112588 return;
32122589 }
32132590
32142591 // TODO: we can probably avoid the copies in some other common cases too.
32152592
3216 const gpa = f.object.dg.gpa;
2593 const gpa = f.dg.gpa;
32172594
32182595 // Save the original value_map and free_locals_map so that we can restore them after the body.
32192596 var old_value_map = try f.value_map.clone();
......@@ -3254,13 +2631,13 @@ fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []con
32542631}
32552632
32562633fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
3257 const zcu = f.object.dg.pt.zcu;
2634 const zcu = f.dg.pt.zcu;
32582635 const ip = &zcu.intern_pool;
32592636 const air_tags = f.air.instructions.items(.tag);
32602637 const air_datas = f.air.instructions.items(.data);
32612638
32622639 for (body) |inst| {
3263 if (f.object.dg.expected_block) |_|
2640 if (f.dg.expected_block) |_|
32642641 return f.fail("runtime code not allowed in naked function", .{});
32652642 if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip))
32662643 continue;
......@@ -3529,8 +2906,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
35292906 .ret => return airRet(f, inst, false),
35302907 .ret_safe => return airRet(f, inst, false), // TODO
35312908 .ret_load => return airRet(f, inst, true),
3532 .trap => return airTrap(f, &f.object.code.writer),
3533 .unreach => return airUnreach(&f.object),
2909 .trap => return airTrap(f),
2910 .unreach => return airUnreach(f),
35342911
35352912 // Instructions which may be `noreturn`.
35362913 .block => res: {
......@@ -3573,21 +2950,21 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [
35732950 const operand = try f.resolveInst(ty_op.operand);
35742951 try reap(f, inst, &.{ty_op.operand});
35752952
3576 const w = &f.object.code.writer;
2953 const w = &f.code.writer;
35772954 const local = try f.allocLocal(inst, inst_ty);
3578 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3579 try f.writeCValue(w, local, .Other);
3580 try a.assign(f, w);
2955 try f.writeCValue(w, local, .other);
2956 try w.writeAll(" = ");
35812957 if (is_ptr) {
35822958 try w.writeByte('&');
35832959 try f.writeCValueDerefMember(w, operand, .{ .identifier = field_name });
35842960 } else try f.writeCValueMember(w, operand, .{ .identifier = field_name });
3585 try a.end(f, w);
2961 try w.writeByte(';');
2962 try f.newline();
35862963 return local;
35872964}
35882965
35892966fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3590 const zcu = f.object.dg.pt.zcu;
2967 const zcu = f.dg.pt.zcu;
35912968 const inst_ty = f.typeOfIndex(inst);
35922969 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
35932970 assert(inst_ty.hasRuntimeBits(zcu));
......@@ -3596,21 +2973,24 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
35962973 const index = try f.resolveInst(bin_op.rhs);
35972974 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
35982975
3599 const w = &f.object.code.writer;
2976 const w = &f.code.writer;
36002977 const local = try f.allocLocal(inst, inst_ty);
3601 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3602 try f.writeCValue(w, local, .Other);
3603 try a.assign(f, w);
3604 try f.writeCValue(w, ptr, .Other);
2978 try f.writeCValue(w, local, .other);
2979 try w.writeAll(" = ");
2980 switch (f.typeOf(bin_op.lhs).ptrSize(zcu)) {
2981 .one => try f.writeCValueDerefMember(w, ptr, .{ .identifier = "array" }),
2982 .many, .c => try f.writeCValue(w, ptr, .other),
2983 .slice => unreachable,
2984 }
36052985 try w.writeByte('[');
3606 try f.writeCValue(w, index, .Other);
3607 try w.writeByte(']');
3608 try a.end(f, w);
2986 try f.writeCValue(w, index, .other);
2987 try w.writeAll("];");
2988 try f.newline();
36092989 return local;
36102990}
36112991
36122992fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3613 const pt = f.object.dg.pt;
2993 const pt = f.dg.pt;
36142994 const zcu = pt.zcu;
36152995 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
36162996 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
......@@ -3623,28 +3003,26 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
36233003 const index = try f.resolveInst(bin_op.rhs);
36243004 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36253005
3626 const w = &f.object.code.writer;
3006 const w = &f.code.writer;
36273007 const local = try f.allocLocal(inst, inst_ty);
3628 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3629 try f.writeCValue(w, local, .Other);
3630 try a.assign(f, w);
3631 try w.writeByte('(');
3632 try f.renderType(w, inst_ty);
3633 try w.writeByte(')');
3008 try f.writeCValue(w, local, .other);
3009 try w.writeAll(" = ");
36343010 try w.writeByte('&');
36353011 if (ptr_ty.ptrSize(zcu) == .one) {
3636 // It's a pointer to an array, so we need to de-reference.
3637 try f.writeCValueDeref(w, ptr);
3638 } else try f.writeCValue(w, ptr, .Other);
3012 // `*[n]T` was turned into a pointer to `struct { T array[n]; }`
3013 try f.writeCValueDerefMember(w, ptr, .{ .identifier = "array" });
3014 } else {
3015 try f.writeCValue(w, ptr, .other);
3016 }
36393017 try w.writeByte('[');
3640 try f.writeCValue(w, index, .Other);
3641 try w.writeByte(']');
3642 try a.end(f, w);
3018 try f.writeCValue(w, index, .other);
3019 try w.writeAll("];");
3020 try f.newline();
36433021 return local;
36443022}
36453023
36463024fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3647 const zcu = f.object.dg.pt.zcu;
3025 const zcu = f.dg.pt.zcu;
36483026 const inst_ty = f.typeOfIndex(inst);
36493027 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
36503028 assert(inst_ty.hasRuntimeBits(zcu));
......@@ -3653,21 +3031,20 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
36533031 const index = try f.resolveInst(bin_op.rhs);
36543032 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36553033
3656 const w = &f.object.code.writer;
3034 const w = &f.code.writer;
36573035 const local = try f.allocLocal(inst, inst_ty);
3658 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3659 try f.writeCValue(w, local, .Other);
3660 try a.assign(f, w);
3036 try f.writeCValue(w, local, .other);
3037 try w.writeAll(" = ");
36613038 try f.writeCValueMember(w, slice, .{ .identifier = "ptr" });
36623039 try w.writeByte('[');
3663 try f.writeCValue(w, index, .Other);
3664 try w.writeByte(']');
3665 try a.end(f, w);
3040 try f.writeCValue(w, index, .other);
3041 try w.writeAll("];");
3042 try f.newline();
36663043 return local;
36673044}
36683045
36693046fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3670 const pt = f.object.dg.pt;
3047 const pt = f.dg.pt;
36713048 const zcu = pt.zcu;
36723049 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
36733050 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
......@@ -3681,22 +3058,21 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
36813058 const index = try f.resolveInst(bin_op.rhs);
36823059 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36833060
3684 const w = &f.object.code.writer;
3061 const w = &f.code.writer;
36853062 const local = try f.allocLocal(inst, inst_ty);
3686 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3687 try f.writeCValue(w, local, .Other);
3688 try a.assign(f, w);
3063 try f.writeCValue(w, local, .other);
3064 try w.writeAll(" = ");
36893065 try w.writeByte('&');
36903066 try f.writeCValueMember(w, slice, .{ .identifier = "ptr" });
36913067 try w.writeByte('[');
3692 try f.writeCValue(w, index, .Other);
3693 try w.writeByte(']');
3694 try a.end(f, w);
3068 try f.writeCValue(w, index, .other);
3069 try w.writeAll("];");
3070 try f.newline();
36953071 return local;
36963072}
36973073
36983074fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3699 const zcu = f.object.dg.pt.zcu;
3075 const zcu = f.dg.pt.zcu;
37003076 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
37013077 const inst_ty = f.typeOfIndex(inst);
37023078 assert(inst_ty.hasRuntimeBits(zcu));
......@@ -3705,32 +3081,28 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
37053081 const index = try f.resolveInst(bin_op.rhs);
37063082 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
37073083
3708 const w = &f.object.code.writer;
3084 const w = &f.code.writer;
37093085 const local = try f.allocLocal(inst, inst_ty);
3710 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
3711 try f.writeCValue(w, local, .Other);
3712 try a.assign(f, w);
3713 try f.writeCValue(w, array, .Other);
3086 try f.writeCValue(w, local, .other);
3087 try w.writeAll(" = ");
3088 try f.writeCValueMember(w, array, .{ .identifier = "array" });
37143089 try w.writeByte('[');
3715 try f.writeCValue(w, index, .Other);
3716 try w.writeByte(']');
3717 try a.end(f, w);
3090 try f.writeCValue(w, index, .other);
3091 try w.writeAll("];");
3092 try f.newline();
37183093 return local;
37193094}
37203095
37213096fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
3722 const pt = f.object.dg.pt;
3097 const pt = f.dg.pt;
37233098 const zcu = pt.zcu;
37243099 const inst_ty = f.typeOfIndex(inst);
37253100 const elem_ty = inst_ty.childType(zcu);
37263101 if (!elem_ty.hasRuntimeBits(zcu)) return .{ .undef = inst_ty };
37273102
37283103 const local = try f.allocLocalValue(.{
3729 .ctype = try f.ctypeFromType(elem_ty, .complete),
3730 .alignas = CType.AlignAs.fromAlignment(.{
3731 .@"align" = inst_ty.ptrInfo(zcu).flags.alignment,
3732 .abi = elem_ty.abiAlignment(zcu),
3733 }),
3104 .type = elem_ty,
3105 .alignment = inst_ty.ptrInfo(zcu).flags.alignment,
37343106 });
37353107 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
37363108 try f.allocs.put(zcu.gpa, local.new_local, true);
......@@ -3741,11 +3113,11 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
37413113 // For packed aggregates, we zero-initialize to try and work around a design flaw
37423114 // related to how `packed`, `undefined`, and RLS interact. See comment in `airStore`
37433115 // for details.
3744 const w = &f.object.code.writer;
3116 const w = &f.code.writer;
37453117 try w.print("memset(&t{d}, 0x00, sizeof(", .{local.new_local});
37463118 try f.renderType(w, elem_ty);
37473119 try w.writeAll("));");
3748 try f.object.newline();
3120 try f.newline();
37493121 },
37503122 .auto, .@"extern" => {},
37513123 },
......@@ -3756,18 +3128,15 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
37563128}
37573129
37583130fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3759 const pt = f.object.dg.pt;
3131 const pt = f.dg.pt;
37603132 const zcu = pt.zcu;
37613133 const inst_ty = f.typeOfIndex(inst);
37623134 const elem_ty = inst_ty.childType(zcu);
37633135 if (!elem_ty.hasRuntimeBits(zcu)) return .{ .undef = inst_ty };
37643136
37653137 const local = try f.allocLocalValue(.{
3766 .ctype = try f.ctypeFromType(elem_ty, .complete),
3767 .alignas = CType.AlignAs.fromAlignment(.{
3768 .@"align" = inst_ty.ptrInfo(zcu).flags.alignment,
3769 .abi = elem_ty.abiAlignment(zcu),
3770 }),
3138 .type = elem_ty,
3139 .alignment = inst_ty.ptrInfo(zcu).flags.alignment,
37713140 });
37723141 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
37733142 try f.allocs.put(zcu.gpa, local.new_local, true);
......@@ -3778,11 +3147,11 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
37783147 // For packed aggregates, we zero-initialize to try and work around a design flaw
37793148 // related to how `packed`, `undefined`, and RLS interact. See comment in `airStore`
37803149 // for details.
3781 const w = &f.object.code.writer;
3150 const w = &f.code.writer;
37823151 try w.print("memset(&t{d}, 0x00, sizeof(", .{local.new_local});
37833152 try f.renderType(w, elem_ty);
37843153 try w.writeAll("));");
3785 try f.object.newline();
3154 try f.newline();
37863155 },
37873156 .auto, .@"extern" => {},
37883157 },
......@@ -3793,24 +3162,18 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
37933162}
37943163
37953164fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
3796 const inst_ty = f.typeOfIndex(inst);
3797 const inst_ctype = try f.ctypeFromType(inst_ty, .parameter);
3798
37993165 const i = f.next_arg_index;
38003166 f.next_arg_index += 1;
3801 const result: CValue = if (inst_ctype.eql(try f.ctypeFromType(inst_ty, .complete)))
3802 .{ .arg = i }
3803 else
3804 .{ .arg_array = i };
3167 const result: CValue = .{ .arg = i };
38053168
38063169 if (f.liveness.isUnused(inst)) {
3807 const w = &f.object.code.writer;
3170 const w = &f.code.writer;
38083171 try w.writeByte('(');
38093172 try f.renderType(w, .void);
38103173 try w.writeByte(')');
3811 try f.writeCValue(w, result, .Other);
3174 try f.writeCValue(w, result, .other);
38123175 try w.writeByte(';');
3813 try f.object.newline();
3176 try f.newline();
38143177 return .none;
38153178 }
38163179
......@@ -3818,7 +3181,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
38183181}
38193182
38203183fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3821 const pt = f.object.dg.pt;
3184 const pt = f.dg.pt;
38223185 const zcu = pt.zcu;
38233186 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
38243187
......@@ -3841,94 +3204,69 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
38413204 ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)
38423205 else
38433206 true;
3844 const is_array = lowersToArray(src_ty, zcu);
3845 const need_memcpy = !is_aligned or is_array;
38463207
3847 const w = &f.object.code.writer;
3208 const w = &f.code.writer;
38483209 const local = try f.allocLocal(inst, src_ty);
38493210 const v = try Vectorize.start(f, inst, w, ptr_ty);
38503211
3851 if (need_memcpy) {
3852 try w.writeAll("memcpy(");
3853 if (!is_array) try w.writeByte('&');
3854 try f.writeCValue(w, local, .Other);
3212 if (!is_aligned) {
3213 try w.writeAll("memcpy(&");
3214 try f.writeCValue(w, local, .other);
38553215 try v.elem(f, w);
38563216 try w.writeAll(", (const char *)");
3857 try f.writeCValue(w, operand, .Other);
3217 try f.writeCValue(w, operand, .other);
38583218 try v.elem(f, w);
38593219 try w.writeAll(", sizeof(");
38603220 try f.renderType(w, src_ty);
38613221 try w.writeAll("))");
38623222 } else {
3863 try f.writeCValue(w, local, .Other);
3223 try f.writeCValue(w, local, .other);
38643224 try v.elem(f, w);
38653225 try w.writeAll(" = ");
38663226 try f.writeCValueDeref(w, operand);
38673227 try v.elem(f, w);
38683228 }
38693229 try w.writeByte(';');
3870 try f.object.newline();
3230 try f.newline();
38713231 try v.end(f, inst, w);
38723232
38733233 return local;
38743234}
38753235
38763236fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
3877 const pt = f.object.dg.pt;
3237 const pt = f.dg.pt;
38783238 const zcu = pt.zcu;
38793239 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3880 const w = &f.object.code.writer;
3240 const w = &f.code.writer;
38813241 const op_inst = un_op.toIndex();
38823242 const op_ty = f.typeOf(un_op);
38833243 const ret_ty = if (is_ptr) op_ty.childType(zcu) else op_ty;
3884 const ret_ctype = try f.ctypeFromType(ret_ty, .parameter);
38853244
38863245 if (op_inst != null and f.air.instructions.items(.tag)[@intFromEnum(op_inst.?)] == .call_always_tail) {
38873246 try reap(f, inst, &.{un_op});
38883247 _ = try airCall(f, op_inst.?, .always_tail);
3889 } else if (ret_ctype.index != .void) {
3248 } else if (ret_ty.hasRuntimeBits(zcu)) {
38903249 const operand = try f.resolveInst(un_op);
38913250 try reap(f, inst, &.{un_op});
3892 var deref = is_ptr;
3893 const is_array = lowersToArray(ret_ty, zcu);
3894 const ret_val = if (is_array) ret_val: {
3895 const array_local = try f.allocAlignedLocal(inst, .{
3896 .ctype = ret_ctype,
3897 .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(zcu)),
3898 });
3899 try w.writeAll("memcpy(");
3900 try f.writeCValueMember(w, array_local, .{ .identifier = "array" });
3901 try w.writeAll(", ");
3902 if (deref)
3903 try f.writeCValueDeref(w, operand)
3904 else
3905 try f.writeCValue(w, operand, .FunctionArgument);
3906 deref = false;
3907 try w.writeAll(", sizeof(");
3908 try f.renderType(w, ret_ty);
3909 try w.writeAll("));");
3910 try f.object.newline();
3911 break :ret_val array_local;
3912 } else operand;
39133251
39143252 try w.writeAll("return ");
3915 if (deref)
3916 try f.writeCValueDeref(w, ret_val)
3917 else
3918 try f.writeCValue(w, ret_val, .Other);
3919 try w.writeAll(";\n");
3920 if (is_array) {
3921 try freeLocal(f, inst, ret_val.new_local, null);
3253 if (is_ptr) {
3254 try f.writeCValueDeref(w, operand);
3255 } else switch (operand) {
3256 // Instead of 'return &local', emit 'return undefined'.
3257 .local_ref => try f.dg.renderUndefValue(w, ret_ty, .other),
3258 else => try f.writeCValue(w, operand, .other),
39223259 }
3260 try w.writeAll(";\n");
39233261 } else {
39243262 try reap(f, inst, &.{un_op});
39253263 // Not even allowed to return void in a naked function.
3926 if (!f.object.dg.is_naked_fn) try w.writeAll("return;\n");
3264 if (!f.dg.is_naked_fn) try w.writeAll("return;\n");
39273265 }
39283266}
39293267
39303268fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
3931 const pt = f.object.dg.pt;
3269 const pt = f.dg.pt;
39323270 const zcu = pt.zcu;
39333271 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
39343272
......@@ -3940,23 +3278,23 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
39403278 const operand_ty = f.typeOf(ty_op.operand);
39413279 const scalar_ty = operand_ty.scalarType(zcu);
39423280
3943 if (f.object.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand);
3281 if (f.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand);
39443282
3945 const w = &f.object.code.writer;
3283 const w = &f.code.writer;
39463284 const local = try f.allocLocal(inst, inst_ty);
39473285 const v = try Vectorize.start(f, inst, w, operand_ty);
3948 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));
3949 try f.writeCValue(w, local, .Other);
3286 try f.writeCValue(w, local, .other);
39503287 try v.elem(f, w);
3951 try a.assign(f, w);
3952 try f.renderIntCast(w, inst_scalar_ty, operand, v, scalar_ty, .Other);
3953 try a.end(f, w);
3288 try w.writeAll(" = ");
3289 try f.renderIntCast(w, inst_scalar_ty, operand, v, scalar_ty, .other);
3290 try w.writeByte(';');
3291 try f.newline();
39543292 try v.end(f, inst, w);
39553293 return local;
39563294}
39573295
39583296fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3959 const pt = f.object.dg.pt;
3297 const pt = f.dg.pt;
39603298 const zcu = pt.zcu;
39613299 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
39623300
......@@ -3978,13 +3316,12 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
39783316 const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits);
39793317 if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand);
39803318
3981 const w = &f.object.code.writer;
3319 const w = &f.code.writer;
39823320 const local = try f.allocLocal(inst, inst_ty);
39833321 const v = try Vectorize.start(f, inst, w, operand_ty);
3984 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_scalar_ty, .complete));
3985 try f.writeCValue(w, local, .Other);
3322 try f.writeCValue(w, local, .other);
39863323 try v.elem(f, w);
3987 try a.assign(f, w);
3324 try w.writeAll(" = ");
39883325 if (need_cast) {
39893326 try w.writeByte('(');
39903327 try f.renderType(w, inst_scalar_ty);
......@@ -3992,18 +3329,18 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
39923329 }
39933330 if (need_lo) {
39943331 try w.writeAll("zig_lo_");
3995 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3332 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
39963333 try w.writeByte('(');
39973334 }
39983335 if (!need_mask) {
3999 try f.writeCValue(w, operand, .Other);
3336 try f.writeCValue(w, operand, .other);
40003337 try v.elem(f, w);
40013338 } else switch (dest_int_info.signedness) {
40023339 .unsigned => {
40033340 try w.writeAll("zig_and_");
4004 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3341 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
40053342 try w.writeByte('(');
4006 try f.writeCValue(w, operand, .FunctionArgument);
3343 try f.writeCValue(w, operand, .other);
40073344 try v.elem(f, w);
40083345 try w.print(", {f})", .{
40093346 try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)),
......@@ -4015,7 +3352,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
40153352 const shift_val = try pt.intValue(.u8, c_bits - dest_bits);
40163353
40173354 try w.writeAll("zig_shr_");
4018 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3355 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
40193356 if (c_bits == 128) {
40203357 try w.print("(zig_bitCast_i{d}(", .{c_bits});
40213358 } else {
......@@ -4027,7 +3364,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
40273364 } else {
40283365 try w.print("(uint{d}_t)", .{c_bits});
40293366 }
4030 try f.writeCValue(w, operand, .FunctionArgument);
3367 try f.writeCValue(w, operand, .other);
40313368 try v.elem(f, w);
40323369 if (c_bits == 128) try w.writeByte(')');
40333370 try w.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)});
......@@ -4036,13 +3373,14 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
40363373 },
40373374 }
40383375 if (need_lo) try w.writeByte(')');
4039 try a.end(f, w);
3376 try w.writeByte(';');
3377 try f.newline();
40403378 try v.end(f, inst, w);
40413379 return local;
40423380}
40433381
40443382fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
4045 const pt = f.object.dg.pt;
3383 const pt = f.dg.pt;
40463384 const zcu = pt.zcu;
40473385 // *a = b;
40483386 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
......@@ -4060,7 +3398,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
40603398
40613399 const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndef(zcu) else false;
40623400
4063 const w = &f.object.code.writer;
3401 const w = &f.code.writer;
40643402 if (val_is_undef) {
40653403 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
40663404 if (safety and ptr_info.packed_offset.host_size == 0) {
......@@ -4080,11 +3418,11 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
40803418 },
40813419 };
40823420 try w.writeAll("memset(");
4083 try f.writeCValue(w, ptr_val, .FunctionArgument);
3421 try f.writeCValue(w, ptr_val, .other);
40843422 try w.print(", {s}, sizeof(", .{byte_str});
40853423 try f.renderType(w, .fromInterned(ptr_info.child));
40863424 try w.writeAll("));");
4087 try f.object.newline();
3425 try f.newline();
40883426 }
40893427 return .none;
40903428 }
......@@ -4093,46 +3431,29 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
40933431 ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)
40943432 else
40953433 true;
4096 const is_array = lowersToArray(.fromInterned(ptr_info.child), zcu);
4097 const need_memcpy = !is_aligned or is_array;
40983434
40993435 const src_val = try f.resolveInst(bin_op.rhs);
41003436 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
41013437
4102 const src_scalar_ctype = try f.ctypeFromType(src_ty.scalarType(zcu), .complete);
4103 if (need_memcpy) {
3438 if (!is_aligned) {
41043439 // For this memcpy to safely work we need the rhs to have the same
41053440 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
41063441 assert(src_ty.eql(.fromInterned(ptr_info.child), zcu));
41073442
4108 // If the source is a constant, writeCValue will emit a brace initialization
4109 // so work around this by initializing into new local.
4110 // TODO this should be done by manually initializing elements of the dest array
4111 const array_src = if (src_val == .constant) blk: {
4112 const new_local = try f.allocLocal(inst, src_ty);
4113 try f.writeCValue(w, new_local, .Other);
4114 try w.writeAll(" = ");
4115 try f.writeCValue(w, src_val, .Other);
4116 try w.writeByte(';');
4117 try f.object.newline();
4118
4119 break :blk new_local;
4120 } else src_val;
4121
41223443 const v = try Vectorize.start(f, inst, w, ptr_ty);
41233444 try w.writeAll("memcpy((char *)");
4124 try f.writeCValue(w, ptr_val, .FunctionArgument);
3445 try f.writeCValue(w, ptr_val, .other);
41253446 try v.elem(f, w);
4126 try w.writeAll(", ");
4127 if (!is_array) try w.writeByte('&');
4128 try f.writeCValue(w, array_src, .FunctionArgument);
3447 try w.writeAll(", &");
3448 switch (src_val) {
3449 .constant => |val| try f.dg.renderValueAsLvalue(w, val),
3450 else => try f.writeCValue(w, src_val, .other),
3451 }
41293452 try v.elem(f, w);
41303453 try w.writeAll(", sizeof(");
41313454 try f.renderType(w, src_ty);
4132 try w.writeAll("))");
4133 try f.freeCValue(inst, array_src);
4134 try w.writeByte(';');
4135 try f.object.newline();
3455 try w.writeAll("));");
3456 try f.newline();
41363457 try v.end(f, inst, w);
41373458 } else {
41383459 switch (ptr_val) {
......@@ -4144,20 +3465,20 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
41443465 else => {},
41453466 }
41463467 const v = try Vectorize.start(f, inst, w, ptr_ty);
4147 const a = try Assignment.start(f, w, src_scalar_ctype);
41483468 try f.writeCValueDeref(w, ptr_val);
41493469 try v.elem(f, w);
4150 try a.assign(f, w);
4151 try f.writeCValue(w, src_val, .Other);
3470 try w.writeAll(" = ");
3471 try f.writeCValue(w, src_val, .other);
41523472 try v.elem(f, w);
4153 try a.end(f, w);
3473 try w.writeByte(';');
3474 try f.newline();
41543475 try v.end(f, inst, w);
41553476 }
41563477 return .none;
41573478}
41583479
41593480fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue {
4160 const pt = f.object.dg.pt;
3481 const pt = f.dg.pt;
41613482 const zcu = pt.zcu;
41623483 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
41633484 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
......@@ -4170,7 +3491,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
41703491 const operand_ty = f.typeOf(bin_op.lhs);
41713492 const scalar_ty = operand_ty.scalarType(zcu);
41723493
4173 const w = &f.object.code.writer;
3494 const w = &f.code.writer;
41743495 const local = try f.allocLocal(inst, inst_ty);
41753496 const v = try Vectorize.start(f, inst, w, operand_ty);
41763497 try f.writeCValueMember(w, local, .{ .field = 1 });
......@@ -4178,26 +3499,26 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
41783499 try w.writeAll(" = zig_");
41793500 try w.writeAll(operation);
41803501 try w.writeAll("o_");
4181 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3502 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
41823503 try w.writeAll("(&");
41833504 try f.writeCValueMember(w, local, .{ .field = 0 });
41843505 try v.elem(f, w);
41853506 try w.writeAll(", ");
4186 try f.writeCValue(w, lhs, .FunctionArgument);
3507 try f.writeCValue(w, lhs, .other);
41873508 try v.elem(f, w);
41883509 try w.writeAll(", ");
4189 try f.writeCValue(w, rhs, .FunctionArgument);
3510 try f.writeCValue(w, rhs, .other);
41903511 if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, w);
4191 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);
3512 try f.dg.renderBuiltinInfo(w, scalar_ty, info);
41923513 try w.writeAll(");");
4193 try f.object.newline();
3514 try f.newline();
41943515 try v.end(f, inst, w);
41953516
41963517 return local;
41973518}
41983519
41993520fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
4200 const pt = f.object.dg.pt;
3521 const pt = f.dg.pt;
42013522 const zcu = pt.zcu;
42023523 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
42033524 const operand_ty = f.typeOf(ty_op.operand);
......@@ -4209,17 +3530,17 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
42093530
42103531 const inst_ty = f.typeOfIndex(inst);
42113532
4212 const w = &f.object.code.writer;
3533 const w = &f.code.writer;
42133534 const local = try f.allocLocal(inst, inst_ty);
42143535 const v = try Vectorize.start(f, inst, w, operand_ty);
4215 try f.writeCValue(w, local, .Other);
3536 try f.writeCValue(w, local, .other);
42163537 try v.elem(f, w);
42173538 try w.writeAll(" = ");
42183539 try w.writeByte('!');
4219 try f.writeCValue(w, op, .Other);
3540 try f.writeCValue(w, op, .other);
42203541 try v.elem(f, w);
42213542 try w.writeByte(';');
4222 try f.object.newline();
3543 try f.newline();
42233544 try v.end(f, inst, w);
42243545
42253546 return local;
......@@ -4232,7 +3553,7 @@ fn airBinOp(
42323553 operation: []const u8,
42333554 info: BuiltinInfo,
42343555) !CValue {
4235 const pt = f.object.dg.pt;
3556 const pt = f.dg.pt;
42363557 const zcu = pt.zcu;
42373558 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
42383559 const operand_ty = f.typeOf(bin_op.lhs);
......@@ -4246,21 +3567,21 @@ fn airBinOp(
42463567
42473568 const inst_ty = f.typeOfIndex(inst);
42483569
4249 const w = &f.object.code.writer;
3570 const w = &f.code.writer;
42503571 const local = try f.allocLocal(inst, inst_ty);
42513572 const v = try Vectorize.start(f, inst, w, operand_ty);
4252 try f.writeCValue(w, local, .Other);
3573 try f.writeCValue(w, local, .other);
42533574 try v.elem(f, w);
42543575 try w.writeAll(" = ");
4255 try f.writeCValue(w, lhs, .Other);
3576 try f.writeCValue(w, lhs, .other);
42563577 try v.elem(f, w);
42573578 try w.writeByte(' ');
42583579 try w.writeAll(operator);
42593580 try w.writeByte(' ');
4260 try f.writeCValue(w, rhs, .Other);
3581 try f.writeCValue(w, rhs, .other);
42613582 try v.elem(f, w);
42623583 try w.writeByte(';');
4263 try f.object.newline();
3584 try f.newline();
42643585 try v.end(f, inst, w);
42653586
42663587 return local;
......@@ -4272,7 +3593,7 @@ fn airCmpOp(
42723593 data: anytype,
42733594 operator: std.math.CompareOperator,
42743595) !CValue {
4275 const pt = f.object.dg.pt;
3596 const pt = f.dg.pt;
42763597 const zcu = pt.zcu;
42773598 const lhs_ty = f.typeOf(data.lhs);
42783599 const scalar_ty = lhs_ty.scalarType(zcu);
......@@ -4297,26 +3618,26 @@ fn airCmpOp(
42973618
42983619 const rhs_ty = f.typeOf(data.rhs);
42993620 const need_cast = lhs_ty.isSinglePointer(zcu) or rhs_ty.isSinglePointer(zcu);
4300 const w = &f.object.code.writer;
3621 const w = &f.code.writer;
43013622 const local = try f.allocLocal(inst, inst_ty);
43023623 const v = try Vectorize.start(f, inst, w, lhs_ty);
4303 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));
4304 try f.writeCValue(w, local, .Other);
3624 try f.writeCValue(w, local, .other);
43053625 try v.elem(f, w);
4306 try a.assign(f, w);
3626 try w.writeAll(" = ");
43073627 if (lhs != .undef and lhs.eql(rhs)) try w.writeAll(switch (operator) {
43083628 .lt, .neq, .gt => "false",
43093629 .lte, .eq, .gte => "true",
43103630 }) else {
43113631 if (need_cast) try w.writeAll("(void*)");
4312 try f.writeCValue(w, lhs, .Other);
3632 try f.writeCValue(w, lhs, .other);
43133633 try v.elem(f, w);
43143634 try w.writeAll(compareOperatorC(operator));
43153635 if (need_cast) try w.writeAll("(void*)");
4316 try f.writeCValue(w, rhs, .Other);
3636 try f.writeCValue(w, rhs, .other);
43173637 try v.elem(f, w);
43183638 }
4319 try a.end(f, w);
3639 try w.writeByte(';');
3640 try f.newline();
43203641 try v.end(f, inst, w);
43213642
43223643 return local;
......@@ -4327,9 +3648,8 @@ fn airEquality(
43273648 inst: Air.Inst.Index,
43283649 operator: std.math.CompareOperator,
43293650) !CValue {
4330 const pt = f.object.dg.pt;
3651 const pt = f.dg.pt;
43313652 const zcu = pt.zcu;
4332 const ctype_pool = &f.object.dg.ctype_pool;
43333653 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
43343654
43353655 const operand_ty = f.typeOf(bin_op.lhs);
......@@ -4350,54 +3670,64 @@ fn airEquality(
43503670 const rhs = try f.resolveInst(bin_op.rhs);
43513671 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
43523672
4353 const w = &f.object.code.writer;
3673 if (lhs.eql(rhs)) {
3674 // Avoid emitting a tautological comparison.
3675 return .{ .constant = .makeBool(switch (operator) {
3676 .eq, .lte, .gte => true,
3677 .neq, .lt, .gt => false,
3678 }) };
3679 }
3680
3681 const w = &f.code.writer;
43543682 const local = try f.allocLocal(inst, .bool);
4355 const a = try Assignment.start(f, w, .bool);
4356 try f.writeCValue(w, local, .Other);
4357 try a.assign(f, w);
3683 try f.writeCValue(w, local, .other);
3684 try w.writeAll(" = ");
43583685
4359 const operand_ctype = try f.ctypeFromType(operand_ty, .complete);
4360 if (lhs != .undef and lhs.eql(rhs)) try w.writeAll(switch (operator) {
4361 .lt, .lte, .gte, .gt => unreachable,
4362 .neq => "false",
4363 .eq => "true",
4364 }) else switch (operand_ctype.info(ctype_pool)) {
4365 .basic, .pointer => {
4366 try f.writeCValue(w, lhs, .Other);
4367 try w.writeAll(compareOperatorC(operator));
4368 try f.writeCValue(w, rhs, .Other);
4369 },
4370 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
4371 .aggregate => |aggregate| if (aggregate.fields.len == 2 and
4372 (aggregate.fields.at(0, ctype_pool).name.index == .is_null or
4373 aggregate.fields.at(1, ctype_pool).name.index == .is_null))
4374 {
4375 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });
4376 try w.writeAll(" || ");
4377 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });
4378 try w.writeAll(" ? ");
4379 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });
4380 try w.writeAll(compareOperatorC(operator));
4381 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });
4382 try w.writeAll(" : ");
4383 try f.writeCValueMember(w, lhs, .{ .identifier = "payload" });
4384 try w.writeAll(compareOperatorC(operator));
4385 try f.writeCValueMember(w, rhs, .{ .identifier = "payload" });
4386 } else for (0..aggregate.fields.len) |field_index| {
4387 if (field_index > 0) try w.writeAll(switch (operator) {
4388 .lt, .lte, .gte, .gt => unreachable,
4389 .eq => " && ",
4390 .neq => " || ",
4391 });
4392 const field_name: CValue = .{
4393 .ctype_pool_string = aggregate.fields.at(field_index, ctype_pool).name,
4394 };
4395 try f.writeCValueMember(w, lhs, field_name);
4396 try w.writeAll(compareOperatorC(operator));
4397 try f.writeCValueMember(w, rhs, field_name);
3686 switch (operand_ty.zigTypeTag(zcu)) {
3687 .optional => switch (CType.classifyOptional(operand_ty, zcu)) {
3688 .npv_payload => unreachable, // opv optional
3689
3690 .error_set, .ptr_like => {},
3691
3692 .slice_like => unreachable, // equality is not defined on slices
3693
3694 .opv_payload => {
3695 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });
3696 try w.writeAll(compareOperatorC(operator));
3697 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });
3698 try w.writeByte(';');
3699 try f.newline();
3700 return local;
3701 },
3702
3703 .@"struct" => {
3704 // `lhs.is_null || rhs.is_null ? lhs.is_null == rhs.is_null : lhs.payload == rhs.payload`
3705 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });
3706 try w.writeAll(" || ");
3707 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });
3708 try w.writeAll(" ? ");
3709 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });
3710 try w.writeAll(compareOperatorC(operator));
3711 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });
3712 try w.writeAll(" : ");
3713 try f.writeCValueMember(w, lhs, .{ .identifier = "payload" });
3714 try w.writeAll(compareOperatorC(operator));
3715 try f.writeCValueMember(w, rhs, .{ .identifier = "payload" });
3716 try w.writeByte(';');
3717 try f.newline();
3718 return local;
3719 },
43983720 },
3721 .bool, .int, .pointer, .@"enum", .error_set => {},
3722 .@"struct", .@"union" => assert(operand_ty.containerLayout(zcu) == .@"packed"),
3723 else => unreachable,
43993724 }
4400 try a.end(f, w);
3725
3726 try f.writeCValue(w, lhs, .other);
3727 try w.writeAll(compareOperatorC(operator));
3728 try f.writeCValue(w, rhs, .other);
3729 try w.writeByte(';');
3730 try f.newline();
44013731
44023732 return local;
44033733}
......@@ -4408,18 +3738,18 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
44083738 const operand = try f.resolveInst(un_op);
44093739 try reap(f, inst, &.{un_op});
44103740
4411 const w = &f.object.code.writer;
3741 const w = &f.code.writer;
44123742 const local = try f.allocLocal(inst, .bool);
4413 try f.writeCValue(w, local, .Other);
3743 try f.writeCValue(w, local, .other);
44143744 try w.writeAll(" = ");
4415 try f.writeCValue(w, operand, .Other);
3745 try f.writeCValue(w, operand, .other);
44163746 try w.print(" < sizeof({f}) / sizeof(*{0f});", .{fmtIdentSolo("zig_errorName")});
4417 try f.object.newline();
3747 try f.newline();
44183748 return local;
44193749}
44203750
44213751fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
4422 const pt = f.object.dg.pt;
3752 const pt = f.dg.pt;
44233753 const zcu = pt.zcu;
44243754 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
44253755 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
......@@ -4432,38 +3762,34 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
44323762 const inst_scalar_ty = inst_ty.scalarType(zcu);
44333763 const elem_ty = inst_scalar_ty.indexableElem(zcu);
44343764 assert(elem_ty.hasRuntimeBits(zcu));
4435 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
44363765
44373766 const local = try f.allocLocal(inst, inst_ty);
4438 const w = &f.object.code.writer;
3767 const w = &f.code.writer;
44393768 const v = try Vectorize.start(f, inst, w, inst_ty);
4440 const a = try Assignment.start(f, w, inst_scalar_ctype);
4441 try f.writeCValue(w, local, .Other);
3769 try f.writeCValue(w, local, .other);
44423770 try v.elem(f, w);
4443 try a.assign(f, w);
3771 try w.writeAll(" = ");
44443772 // We must convert to and from integer types to prevent UB if the operation
44453773 // results in a NULL pointer, or if LHS is NULL. The operation is only UB
44463774 // if the result is NULL and then dereferenced.
44473775 try w.writeByte('(');
4448 try f.renderCType(w, inst_scalar_ctype);
3776 try f.renderType(w, inst_scalar_ty);
44493777 try w.writeAll(")(((uintptr_t)");
4450 try f.writeCValue(w, lhs, .Other);
3778 try f.writeCValue(w, lhs, .other);
44513779 try v.elem(f, w);
4452 try w.writeAll(") ");
4453 try w.writeByte(operator);
4454 try w.writeAll(" (");
4455 try f.writeCValue(w, rhs, .Other);
3780 try w.print(") {c} (", .{operator});
3781 try f.writeCValue(w, rhs, .other);
44563782 try v.elem(f, w);
44573783 try w.writeAll("*sizeof(");
44583784 try f.renderType(w, elem_ty);
4459 try w.writeAll(")))");
4460 try a.end(f, w);
3785 try w.writeAll(")));");
3786 try f.newline();
44613787 try v.end(f, inst, w);
44623788 return local;
44633789}
44643790
44653791fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue {
4466 const pt = f.object.dg.pt;
3792 const pt = f.dg.pt;
44673793 const zcu = pt.zcu;
44683794 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
44693795
......@@ -4477,36 +3803,34 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
44773803 const rhs = try f.resolveInst(bin_op.rhs);
44783804 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
44793805
4480 const w = &f.object.code.writer;
3806 const w = &f.code.writer;
44813807 const local = try f.allocLocal(inst, inst_ty);
44823808 const v = try Vectorize.start(f, inst, w, inst_ty);
4483 try f.writeCValue(w, local, .Other);
3809 try f.writeCValue(w, local, .other);
44843810 try v.elem(f, w);
44853811 // (lhs <> rhs) ? lhs : rhs
44863812 try w.writeAll(" = (");
4487 try f.writeCValue(w, lhs, .Other);
3813 try f.writeCValue(w, lhs, .other);
44883814 try v.elem(f, w);
44893815 try w.writeByte(' ');
44903816 try w.writeByte(operator);
44913817 try w.writeByte(' ');
4492 try f.writeCValue(w, rhs, .Other);
3818 try f.writeCValue(w, rhs, .other);
44933819 try v.elem(f, w);
44943820 try w.writeAll(") ? ");
4495 try f.writeCValue(w, lhs, .Other);
3821 try f.writeCValue(w, lhs, .other);
44963822 try v.elem(f, w);
44973823 try w.writeAll(" : ");
4498 try f.writeCValue(w, rhs, .Other);
3824 try f.writeCValue(w, rhs, .other);
44993825 try v.elem(f, w);
45003826 try w.writeByte(';');
4501 try f.object.newline();
3827 try f.newline();
45023828 try v.end(f, inst, w);
45033829
45043830 return local;
45053831}
45063832
45073833fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
4508 const pt = f.object.dg.pt;
4509 const zcu = pt.zcu;
45103834 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
45113835 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
45123836
......@@ -4515,24 +3839,22 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
45153839 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
45163840
45173841 const inst_ty = f.typeOfIndex(inst);
4518 const ptr_ty = inst_ty.slicePtrFieldType(zcu);
45193842
4520 const w = &f.object.code.writer;
3843 const w = &f.code.writer;
45213844 const local = try f.allocLocal(inst, inst_ty);
4522 {
4523 const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete));
4524 try f.writeCValueMember(w, local, .{ .identifier = "ptr" });
4525 try a.assign(f, w);
4526 try f.writeCValue(w, ptr, .Other);
4527 try a.end(f, w);
4528 }
4529 {
4530 const a = try Assignment.start(f, w, .usize);
4531 try f.writeCValueMember(w, local, .{ .identifier = "len" });
4532 try a.assign(f, w);
4533 try f.writeCValue(w, len, .Other);
4534 try a.end(f, w);
4535 }
3845
3846 try f.writeCValueMember(w, local, .{ .identifier = "ptr" });
3847 try w.writeAll(" = ");
3848 try f.writeCValue(w, ptr, .other);
3849 try w.writeByte(';');
3850 try f.newline();
3851
3852 try f.writeCValueMember(w, local, .{ .identifier = "len" });
3853 try w.writeAll(" = ");
3854 try f.writeCValue(w, len, .other);
3855 try w.writeByte(';');
3856 try f.newline();
3857
45363858 return local;
45373859}
45383860
......@@ -4541,14 +3863,14 @@ fn airCall(
45413863 inst: Air.Inst.Index,
45423864 modifier: std.builtin.CallModifier,
45433865) !CValue {
4544 const pt = f.object.dg.pt;
3866 const pt = f.dg.pt;
45453867 const zcu = pt.zcu;
45463868 const ip = &zcu.intern_pool;
45473869 // Not even allowed to call panic in a naked function.
4548 if (f.object.dg.is_naked_fn) return .none;
3870 if (f.dg.is_naked_fn) return .none;
45493871
4550 const gpa = f.object.dg.gpa;
4551 const w = &f.object.code.writer;
3872 const gpa = f.dg.gpa;
3873 const w = &f.code.writer;
45523874
45533875 const call = f.air.unwrapCall(inst);
45543876 const args = call.args;
......@@ -4557,27 +3879,11 @@ fn airCall(
45573879 defer gpa.free(resolved_args);
45583880 for (resolved_args, args) |*resolved_arg, arg| {
45593881 const arg_ty = f.typeOf(arg);
4560 const arg_ctype = try f.ctypeFromType(arg_ty, .parameter);
4561 if (arg_ctype.index == .void) {
3882 if (!arg_ty.hasRuntimeBits(zcu)) {
45623883 resolved_arg.* = .none;
45633884 continue;
45643885 }
45653886 resolved_arg.* = try f.resolveInst(arg);
4566 if (!arg_ctype.eql(try f.ctypeFromType(arg_ty, .complete))) {
4567 const array_local = try f.allocAlignedLocal(inst, .{
4568 .ctype = arg_ctype,
4569 .alignas = CType.AlignAs.fromAbiAlignment(arg_ty.abiAlignment(zcu)),
4570 });
4571 try w.writeAll("memcpy(");
4572 try f.writeCValueMember(w, array_local, .{ .identifier = "array" });
4573 try w.writeAll(", ");
4574 try f.writeCValue(w, resolved_arg.*, .FunctionArgument);
4575 try w.writeAll(", sizeof(");
4576 try f.renderCType(w, arg_ctype);
4577 try w.writeAll("));");
4578 try f.object.newline();
4579 resolved_arg.* = array_local;
4580 }
45813887 }
45823888
45833889 const callee = try f.resolveInst(call.callee);
......@@ -4596,28 +3902,22 @@ fn airCall(
45963902 };
45973903 const fn_info = zcu.typeToFunc(if (callee_is_ptr) callee_ty.childType(zcu) else callee_ty).?;
45983904 const ret_ty: Type = .fromInterned(fn_info.return_type);
4599 const ret_ctype: CType = if (ret_ty.isNoReturn(zcu))
4600 .void
4601 else
4602 try f.ctypeFromType(ret_ty, .parameter);
46033905
46043906 const result_local = result: {
46053907 if (modifier == .always_tail) {
46063908 try w.writeAll("zig_always_tail return ");
46073909 break :result .none;
4608 } else if (ret_ctype.index == .void) {
3910 } else if (!ret_ty.hasRuntimeBits(zcu)) {
46093911 break :result .none;
46103912 } else if (f.liveness.isUnused(inst)) {
4611 try w.writeByte('(');
4612 try f.renderCType(w, .void);
4613 try w.writeByte(')');
3913 try w.writeAll("(void)");
46143914 break :result .none;
46153915 } else {
46163916 const local = try f.allocAlignedLocal(inst, .{
4617 .ctype = ret_ctype,
4618 .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(zcu)),
3917 .type = ret_ty,
3918 .alignment = .none,
46193919 });
4620 try f.writeCValue(w, local, .Other);
3920 try f.writeCValue(w, local, .other);
46213921 try w.writeAll(" = ");
46223922 break :result local;
46233923 }
......@@ -4644,8 +3944,19 @@ fn airCall(
46443944 if (!callee_is_ptr) try w.writeByte('&');
46453945 }
46463946 switch (modifier) {
4647 .auto, .always_tail => try f.object.dg.renderNavName(w, fn_nav),
4648 inline .never_tail, .never_inline => |m| try w.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))),
3947 .auto, .always_tail => try renderNavName(w, fn_nav, ip),
3948 .never_tail => {
3949 try f.need_never_tail_funcs.put(gpa, fn_nav, {});
3950 try w.print("zig_never_tail_{f}__{d}", .{
3951 fmtIdentUnsolo(ip.getNav(fn_nav).name.toSlice(ip)), @intFromEnum(fn_nav),
3952 });
3953 },
3954 .never_inline => {
3955 try f.need_never_inline_funcs.put(gpa, fn_nav, {});
3956 try w.print("zig_never_inline_{f}__{d}", .{
3957 fmtIdentUnsolo(ip.getNav(fn_nav).name.toSlice(ip)), @intFromEnum(fn_nav),
3958 });
3959 },
46493960 else => unreachable,
46503961 }
46513962 if (need_cast) try w.writeByte(')');
......@@ -4658,7 +3969,7 @@ fn airCall(
46583969 else => unreachable,
46593970 }
46603971 // Fall back to function pointer call.
4661 try f.writeCValue(w, callee, .Other);
3972 try f.writeCValue(w, callee, .other);
46623973 }
46633974
46643975 try w.writeByte('(');
......@@ -4667,38 +3978,20 @@ fn airCall(
46673978 if (resolved_arg == .none) continue;
46683979 if (need_comma) try w.writeAll(", ");
46693980 need_comma = true;
4670 try f.writeCValue(w, resolved_arg, .FunctionArgument);
4671 try f.freeCValue(inst, resolved_arg);
3981 try f.writeCValue(w, resolved_arg, .other);
46723982 }
46733983 try w.writeAll(");");
46743984 switch (modifier) {
46753985 .always_tail => try w.writeByte('\n'),
4676 else => try f.object.newline(),
3986 else => try f.newline(),
46773987 }
46783988
4679 const result = result: {
4680 if (result_local == .none or !lowersToArray(ret_ty, zcu))
4681 break :result result_local;
4682
4683 const array_local = try f.allocLocal(inst, ret_ty);
4684 try w.writeAll("memcpy(");
4685 try f.writeCValue(w, array_local, .FunctionArgument);
4686 try w.writeAll(", ");
4687 try f.writeCValueMember(w, result_local, .{ .identifier = "array" });
4688 try w.writeAll(", sizeof(");
4689 try f.renderType(w, ret_ty);
4690 try w.writeAll("));");
4691 try f.object.newline();
4692 try freeLocal(f, inst, result_local.new_local, null);
4693 break :result array_local;
4694 };
4695
4696 return result;
3989 return result_local;
46973990}
46983991
46993992fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
47003993 const dbg_stmt = f.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
4701 const w = &f.object.code.writer;
3994 const w = &f.code.writer;
47023995 // TODO re-evaluate whether to emit these or not. If we naively emit
47033996 // these directives, the output file will report bogus line numbers because
47043997 // every newline after the #line directive adds one to the line.
......@@ -4707,32 +4000,32 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
47074000 // newlines until the next dbg_stmt occurs.
47084001 // Perhaps an additional compilation option is in order?
47094002 //try w.print("#line {d}", .{dbg_stmt.line + 1});
4710 //try f.object.newline();
4003 //try f.newline();
47114004 try w.print("/* file:{d}:{d} */", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
4712 try f.object.newline();
4005 try f.newline();
47134006 return .none;
47144007}
47154008
47164009fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue {
4717 try f.object.code.writer.writeAll("(void)0;");
4718 try f.object.newline();
4010 try f.code.writer.writeAll("(void)0;");
4011 try f.newline();
47194012 return .none;
47204013}
47214014
47224015fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
4723 const pt = f.object.dg.pt;
4016 const pt = f.dg.pt;
47244017 const zcu = pt.zcu;
47254018 const ip = &zcu.intern_pool;
47264019 const block = f.air.unwrapDbgBlock(inst);
47274020 const owner_nav = ip.getNav(zcu.funcInfo(block.func).owner_nav);
4728 const w = &f.object.code.writer;
4021 const w = &f.code.writer;
47294022 try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)});
4730 try f.object.newline();
4023 try f.newline();
47314024 return lowerBlock(f, inst, block.body);
47324025}
47334026
47344027fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
4735 const pt = f.object.dg.pt;
4028 const pt = f.dg.pt;
47364029 const zcu = pt.zcu;
47374030 const tag = f.air.instructions.items(.tag)[@intFromEnum(inst)];
47384031 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
......@@ -4741,9 +4034,9 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
47414034 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);
47424035
47434036 try reap(f, inst, &.{pl_op.operand});
4744 const w = &f.object.code.writer;
4037 const w = &f.code.writer;
47454038 try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) });
4746 try f.object.newline();
4039 try f.newline();
47474040 return .none;
47484041}
47494042
......@@ -4753,13 +4046,13 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
47534046}
47544047
47554048fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) !CValue {
4756 const pt = f.object.dg.pt;
4049 const pt = f.dg.pt;
47574050 const zcu = pt.zcu;
47584051 const liveness_block = f.liveness.getBlock(inst);
47594052
47604053 const block_id = f.next_block_index;
47614054 f.next_block_index += 1;
4762 const w = &f.object.code.writer;
4055 const w = &f.code.writer;
47634056
47644057 const inst_ty = f.typeOfIndex(inst);
47654058 const result = if (inst_ty.hasRuntimeBits(zcu) and !f.liveness.isUnused(inst))
......@@ -4767,7 +4060,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
47674060 else
47684061 .none;
47694062
4770 try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{
4063 try f.blocks.putNoClobber(f.dg.gpa, inst, .{
47714064 .block_id = block_id,
47724065 .result = result,
47734066 });
......@@ -4782,23 +4075,23 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
47824075 }
47834076
47844077 // noreturn blocks have no `br` instructions reaching them, so we don't want a label
4785 if (f.object.dg.is_naked_fn) {
4786 if (f.object.dg.expected_block) |expected_block| {
4078 if (f.dg.is_naked_fn) {
4079 if (f.dg.expected_block) |expected_block| {
47874080 if (block_id != expected_block)
47884081 return f.fail("runtime code not allowed in naked function", .{});
4789 f.object.dg.expected_block = null;
4082 f.dg.expected_block = null;
47904083 }
47914084 } else if (!f.typeOfIndex(inst).isNoReturn(zcu)) {
47924085 // label must be followed by an expression, include an empty one.
47934086 try w.print("\nzig_block_{d}:;", .{block_id});
4794 try f.object.newline();
4087 try f.newline();
47954088 }
47964089
47974090 return result;
47984091}
47994092
48004093fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {
4801 const pt = f.object.dg.pt;
4094 const pt = f.dg.pt;
48024095 const unwrapped_try = f.air.unwrapTry(inst);
48034096 const body = unwrapped_try.else_body;
48044097 const err_union_ty = f.air.typeOf(unwrapped_try.error_union, &pt.zcu.intern_pool);
......@@ -4806,7 +4099,7 @@ fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {
48064099}
48074100
48084101fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue {
4809 const pt = f.object.dg.pt;
4102 const pt = f.dg.pt;
48104103 const unwrapped_try = f.air.unwrapTryPtr(inst);
48114104 const body = unwrapped_try.else_body;
48124105 const err_union_ty = f.air.typeOf(unwrapped_try.error_union_ptr, &pt.zcu.intern_pool).childType(pt.zcu);
......@@ -4821,46 +4114,38 @@ fn lowerTry(
48214114 err_union_ty: Type,
48224115 is_ptr: bool,
48234116) !CValue {
4824 const pt = f.object.dg.pt;
4117 const pt = f.dg.pt;
48254118 const zcu = pt.zcu;
48264119 const err_union = try f.resolveInst(operand);
48274120 const inst_ty = f.typeOfIndex(inst);
48284121 const liveness_condbr = f.liveness.getCondBr(inst);
4829 const w = &f.object.code.writer;
4122 const w = &f.code.writer;
48304123 const payload_ty = err_union_ty.errorUnionPayload(zcu);
4831 const payload_has_bits = payload_ty.hasRuntimeBits(zcu);
48324124
4833 if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
4834 try w.writeAll("if (");
4835 if (!payload_has_bits) {
4836 if (is_ptr)
4837 try f.writeCValueDeref(w, err_union)
4838 else
4839 try f.writeCValue(w, err_union, .Other);
4840 } else {
4841 // Reap the operand so that it can be reused inside genBody.
4842 // Remember we must avoid calling reap() twice for the same operand
4843 // in this function.
4844 try reap(f, inst, &.{operand});
4845 if (is_ptr)
4846 try f.writeCValueDerefMember(w, err_union, .{ .identifier = "error" })
4847 else
4848 try f.writeCValueMember(w, err_union, .{ .identifier = "error" });
4849 }
4850 try w.writeAll(") ");
4125 try w.writeAll("if (");
48514126
4852 try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);
4853 try f.object.newline();
4854 if (f.object.dg.expected_block) |_|
4855 return f.fail("runtime code not allowed in naked function", .{});
4856 }
4127 // Reap the operand so that it can be reused inside genBody.
4128 // Remember we must avoid calling reap() twice for the same operand
4129 // in this function.
4130 try reap(f, inst, &.{operand});
4131 if (is_ptr)
4132 try f.writeCValueDerefMember(w, err_union, .{ .identifier = "error" })
4133 else
4134 try f.writeCValueMember(w, err_union, .{ .identifier = "error" });
4135
4136 try w.writeAll(") ");
4137
4138 try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);
4139 try f.newline();
4140 if (f.dg.expected_block) |_|
4141 return f.fail("runtime code not allowed in naked function", .{});
48574142
48584143 // Now we have the "then branch" (in terms of the liveness data); process any deaths.
48594144 for (liveness_condbr.then_deaths) |death| {
48604145 try die(f, inst, death.toRef());
48614146 }
48624147
4863 if (!payload_has_bits) {
4148 if (!payload_ty.hasRuntimeBits(zcu)) {
48644149 if (!is_ptr) {
48654150 return .none;
48664151 } else {
......@@ -4873,14 +4158,14 @@ fn lowerTry(
48734158 if (f.liveness.isUnused(inst)) return .none;
48744159
48754160 const local = try f.allocLocal(inst, inst_ty);
4876 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
4877 try f.writeCValue(w, local, .Other);
4878 try a.assign(f, w);
4161 try f.writeCValue(w, local, .other);
4162 try w.writeAll(" = ");
48794163 if (is_ptr) {
48804164 try w.writeByte('&');
48814165 try f.writeCValueDerefMember(w, err_union, .{ .identifier = "payload" });
48824166 } else try f.writeCValueMember(w, err_union, .{ .identifier = "payload" });
4883 try a.end(f, w);
4167 try w.writeByte(';');
4168 try f.newline();
48844169 return local;
48854170}
48864171
......@@ -4888,25 +4173,24 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
48884173 const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
48894174 const block = f.blocks.get(branch.block_inst).?;
48904175 const result = block.result;
4891 const w = &f.object.code.writer;
4176 const w = &f.code.writer;
48924177
4893 if (f.object.dg.is_naked_fn) {
4178 if (f.dg.is_naked_fn) {
48944179 if (result != .none) return f.fail("runtime code not allowed in naked function", .{});
4895 f.object.dg.expected_block = block.block_id;
4180 f.dg.expected_block = block.block_id;
48964181 return;
48974182 }
48984183
48994184 // If result is .none then the value of the block is unused.
49004185 if (result != .none) {
4901 const operand_ty = f.typeOf(branch.operand);
49024186 const operand = try f.resolveInst(branch.operand);
49034187 try reap(f, inst, &.{branch.operand});
49044188
4905 const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete));
4906 try f.writeCValue(w, result, .Other);
4907 try a.assign(f, w);
4908 try f.writeCValue(w, operand, .Other);
4909 try a.end(f, w);
4189 try f.writeCValue(w, result, .other);
4190 try w.writeAll(" = ");
4191 try f.writeCValue(w, operand, .other);
4192 try w.writeByte(';');
4193 try f.newline();
49104194 }
49114195
49124196 try w.print("goto zig_block_{d};\n", .{block.block_id});
......@@ -4914,14 +4198,14 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
49144198
49154199fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {
49164200 const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
4917 try f.object.code.writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)});
4201 try f.code.writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)});
49184202}
49194203
49204204fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
4921 const pt = f.object.dg.pt;
4205 const pt = f.dg.pt;
49224206 const zcu = pt.zcu;
49234207 const br = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
4924 const w = &f.object.code.writer;
4208 const w = &f.code.writer;
49254209
49264210 if (try f.air.value(br.operand, pt)) |cond_val| {
49274211 // Comptime-known dispatch. Iterate the cases to find the correct
......@@ -4950,11 +4234,11 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
49504234 // Runtime-known dispatch. Set the switch condition, and branch back.
49514235 const cond = try f.resolveInst(br.operand);
49524236 const cond_local = f.loop_switch_conds.get(br.block_inst).?;
4953 try f.writeCValue(w, .{ .local = cond_local }, .Other);
4237 try f.writeCValue(w, .{ .local = cond_local }, .other);
49544238 try w.writeAll(" = ");
4955 try f.writeCValue(w, cond, .Other);
4239 try f.writeCValue(w, cond, .other);
49564240 try w.writeByte(';');
4957 try f.object.newline();
4241 try f.newline();
49584242 try w.print("goto zig_switch_{d}_loop;\n", .{@intFromEnum(br.block_inst)});
49594243}
49604244
......@@ -4971,11 +4255,10 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
49714255}
49724256
49734257fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CValue {
4974 const pt = f.object.dg.pt;
4258 const pt = f.dg.pt;
49754259 const zcu = pt.zcu;
4976 const target = &f.object.dg.mod.resolved_target.result;
4977 const ctype_pool = &f.object.dg.ctype_pool;
4978 const w = &f.object.code.writer;
4260 const target = &f.dg.mod.resolved_target.result;
4261 const w = &f.code.writer;
49794262
49804263 if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) {
49814264 const src_info = dest_ty.intInfo(zcu);
......@@ -4986,26 +4269,16 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
49864269
49874270 if (dest_ty.isPtrAtRuntime(zcu) or operand_ty.isPtrAtRuntime(zcu)) {
49884271 const local = try f.allocLocal(null, dest_ty);
4989 try f.writeCValue(w, local, .Other);
4272 try f.writeCValue(w, local, .other);
49904273 try w.writeAll(" = (");
49914274 try f.renderType(w, dest_ty);
49924275 try w.writeByte(')');
4993 try f.writeCValue(w, operand, .Other);
4276 try f.writeCValue(w, operand, .other);
49944277 try w.writeByte(';');
4995 try f.object.newline();
4278 try f.newline();
49964279 return local;
49974280 }
49984281
4999 const operand_lval = if (operand == .constant) blk: {
5000 const operand_local = try f.allocLocal(null, operand_ty);
5001 try f.writeCValue(w, operand_local, .Other);
5002 try w.writeAll(" = ");
5003 try f.writeCValue(w, operand, .Other);
5004 try w.writeByte(';');
5005 try f.object.newline();
5006 break :blk operand_local;
5007 } else operand;
5008
50094282 const local = try f.allocLocal(null, dest_ty);
50104283 // On big-endian targets, copying ABI integers with padding bits is awkward, because the padding bits are at the low bytes of the value.
50114284 // We need to offset the source or destination pointer appropriately and copy the right number of bytes.
......@@ -5013,141 +4286,134 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
50134286 // e.g. [10]u8 -> u80. We need to offset the destination so that we copy to the least significant bits of the integer.
50144287 const offset = dest_ty.abiSize(zcu) - operand_ty.abiSize(zcu);
50154288 try w.writeAll("memcpy((char *)&");
5016 try f.writeCValue(w, local, .Other);
4289 try f.writeCValue(w, local, .other);
50174290 try w.print(" + {d}, &", .{offset});
5018 try f.writeCValue(w, operand_lval, .Other);
4291 switch (operand) {
4292 .constant => |val| try f.dg.renderValueAsLvalue(w, val),
4293 else => try f.writeCValue(w, operand, .other),
4294 }
50194295 try w.print(", {d});", .{operand_ty.abiSize(zcu)});
50204296 } else if (target.cpu.arch.endian() == .big and operand_ty.isAbiInt(zcu) and !dest_ty.isAbiInt(zcu)) {
50214297 // e.g. u80 -> [10]u8. We need to offset the source so that we copy from the least significant bits of the integer.
50224298 const offset = operand_ty.abiSize(zcu) - dest_ty.abiSize(zcu);
50234299 try w.writeAll("memcpy(&");
5024 try f.writeCValue(w, local, .Other);
4300 try f.writeCValue(w, local, .other);
50254301 try w.writeAll(", (const char *)&");
5026 try f.writeCValue(w, operand_lval, .Other);
4302 switch (operand) {
4303 .constant => |val| try f.dg.renderValueAsLvalue(w, val),
4304 else => try f.writeCValue(w, operand, .other),
4305 }
50274306 try w.print(" + {d}, {d});", .{ offset, dest_ty.abiSize(zcu) });
50284307 } else {
50294308 try w.writeAll("memcpy(&");
5030 try f.writeCValue(w, local, .Other);
4309 try f.writeCValue(w, local, .other);
50314310 try w.writeAll(", &");
5032 try f.writeCValue(w, operand_lval, .Other);
4311 switch (operand) {
4312 .constant => |val| try f.dg.renderValueAsLvalue(w, val),
4313 else => try f.writeCValue(w, operand, .other),
4314 }
50334315 try w.print(", {d});", .{@min(dest_ty.abiSize(zcu), operand_ty.abiSize(zcu))});
50344316 }
50354317
5036 try f.object.newline();
4318 try f.newline();
50374319
50384320 // Ensure padding bits have the expected value.
50394321 if (dest_ty.isAbiInt(zcu)) {
5040 const dest_ctype = try f.ctypeFromType(dest_ty, .complete);
5041 const dest_info = dest_ty.intInfo(zcu);
5042 var bits: u16 = dest_info.bits;
5043 var wrap_ctype: ?CType = null;
5044 var need_bitcasts = false;
5045
5046 try f.writeCValue(w, local, .Other);
5047 switch (dest_ctype.info(ctype_pool)) {
5048 else => {},
5049 .array => |array_info| {
5050 try w.print("[{d}]", .{switch (target.cpu.arch.endian()) {
5051 .little => array_info.len - 1,
5052 .big => 0,
5053 }});
5054 wrap_ctype = array_info.elem_ctype.toSignedness(dest_info.signedness);
5055 need_bitcasts = wrap_ctype.?.index == .zig_i128;
5056 bits -= 1;
5057 bits %= @as(u16, @intCast(f.byteSize(array_info.elem_ctype) * 8));
5058 bits += 1;
4322 switch (CType.classifyInt(dest_ty, zcu)) {
4323 .void => unreachable, // opv
4324 .small => {
4325 try f.writeCValue(w, local, .other);
4326 try w.writeAll(" = zig_wrap_");
4327 try f.dg.renderTypeForBuiltinFnName(w, dest_ty);
4328 try w.writeByte('(');
4329 try f.writeCValue(w, local, .other);
4330 try f.dg.renderBuiltinInfo(w, dest_ty, .bits);
4331 try w.writeAll(");");
4332 try f.newline();
50594333 },
5060 }
5061 try w.writeAll(" = ");
5062 if (need_bitcasts) {
5063 try w.writeAll("zig_bitCast_");
5064 try f.object.dg.renderCTypeForBuiltinFnName(w, wrap_ctype.?.toUnsigned());
5065 try w.writeByte('(');
5066 }
5067 try w.writeAll("zig_wrap_");
5068 const info_ty = try pt.intType(dest_info.signedness, bits);
5069 if (wrap_ctype) |ctype|
5070 try f.object.dg.renderCTypeForBuiltinFnName(w, ctype)
5071 else
5072 try f.object.dg.renderTypeForBuiltinFnName(w, info_ty);
5073 try w.writeByte('(');
5074 if (need_bitcasts) {
5075 try w.writeAll("zig_bitCast_");
5076 try f.object.dg.renderCTypeForBuiltinFnName(w, wrap_ctype.?);
5077 try w.writeByte('(');
5078 }
5079 try f.writeCValue(w, local, .Other);
5080 switch (dest_ctype.info(ctype_pool)) {
5081 else => {},
5082 .array => |array_info| try w.print("[{d}]", .{
5083 switch (target.cpu.arch.endian()) {
5084 .little => array_info.len - 1,
4334 .big => |big| {
4335 const dest_info = dest_ty.intInfo(zcu);
4336 const padding_index: u16 = switch (target.cpu.arch.endian()) {
4337 .little => big.limbs_len - 1,
50854338 .big => 0,
5086 },
5087 }),
4339 };
4340 const wrap_bits = ((dest_info.bits - 1) % big.limb_size.bits()) + 1;
4341 if (big.limb_size != .@"128" or dest_info.signedness == .unsigned) {
4342 try f.writeCValueMember(w, local, .{ .identifier = "limbs" });
4343 try w.print("[{d}] = zig_wrap_{c}{d}(", .{
4344 padding_index,
4345 signAbbrev(dest_info.signedness),
4346 big.limb_size.bits(),
4347 });
4348 try f.writeCValueMember(w, local, .{ .identifier = "limbs" });
4349 try w.print("[{d}], {d});", .{ padding_index, wrap_bits });
4350 } else {
4351 try f.writeCValueMember(w, local, .{ .identifier = "limbs" });
4352 try w.print("[{d}] = zig_bitCast_u128(zig_wrap_i128(zig_bitCast_i128(", .{
4353 padding_index,
4354 });
4355 try f.writeCValueMember(w, local, .{ .identifier = "limbs" });
4356 try w.print("[{d}]), {d}));", .{ padding_index, wrap_bits });
4357 try f.newline();
4358 }
4359 },
50884360 }
5089 if (need_bitcasts) try w.writeByte(')');
5090 try f.object.dg.renderBuiltinInfo(w, info_ty, .bits);
5091 if (need_bitcasts) try w.writeByte(')');
5092 try w.writeAll(");");
5093 try f.object.newline();
50944361 }
50954362
5096 try f.freeCValue(null, operand_lval);
50974363 return local;
50984364}
50994365
5100fn airTrap(f: *Function, w: *Writer) !void {
4366fn airTrap(f: *Function) !void {
51014367 // Not even allowed to call trap in a naked function.
5102 if (f.object.dg.is_naked_fn) return;
5103 try w.writeAll("zig_trap();\n");
4368 if (f.dg.is_naked_fn) return;
4369 try f.code.writer.writeAll("zig_trap();\n");
51044370}
51054371
51064372fn airBreakpoint(f: *Function) !CValue {
5107 const w = &f.object.code.writer;
4373 const w = &f.code.writer;
51084374 try w.writeAll("zig_breakpoint();");
5109 try f.object.newline();
4375 try f.newline();
51104376 return .none;
51114377}
51124378
51134379fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
5114 const w = &f.object.code.writer;
4380 const w = &f.code.writer;
51154381 const local = try f.allocLocal(inst, .usize);
5116 try f.writeCValue(w, local, .Other);
4382 try f.writeCValue(w, local, .other);
51174383 try w.writeAll(" = (");
51184384 try f.renderType(w, .usize);
51194385 try w.writeAll(")zig_return_address();");
5120 try f.object.newline();
4386 try f.newline();
51214387 return local;
51224388}
51234389
51244390fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
5125 const w = &f.object.code.writer;
4391 const w = &f.code.writer;
51264392 const local = try f.allocLocal(inst, .usize);
5127 try f.writeCValue(w, local, .Other);
4393 try f.writeCValue(w, local, .other);
51284394 try w.writeAll(" = (");
51294395 try f.renderType(w, .usize);
51304396 try w.writeAll(")zig_frame_address();");
5131 try f.object.newline();
4397 try f.newline();
51324398 return local;
51334399}
51344400
5135fn airUnreach(o: *Object) !void {
4401fn airUnreach(f: *Function) !void {
51364402 // Not even allowed to call unreachable in a naked function.
5137 if (o.dg.is_naked_fn) return;
5138 try o.code.writer.writeAll("zig_unreachable();\n");
4403 if (f.dg.is_naked_fn) return;
4404 try f.code.writer.writeAll("zig_unreachable();\n");
51394405}
51404406
51414407fn airLoop(f: *Function, inst: Air.Inst.Index) !void {
51424408 const block = f.air.unwrapBlock(inst);
5143 const w = &f.object.code.writer;
4409 const w = &f.code.writer;
51444410
51454411 // `repeat` instructions matching this loop will branch to
51464412 // this label. Since we need a label for arbitrary `repeat`
51474413 // anyway, there's actually no need to use a "real" looping
51484414 // construct at all!
51494415 try w.print("zig_loop_{d}:", .{@intFromEnum(inst)});
5150 try f.object.newline();
4416 try f.newline();
51514417 try genBodyInner(f, block.body); // no need to restore state, we're noreturn
51524418}
51534419
......@@ -5158,15 +4424,15 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {
51584424 const then_body = cond_br.then_body;
51594425 const else_body = cond_br.else_body;
51604426 const liveness_condbr = f.liveness.getCondBr(inst);
5161 const w = &f.object.code.writer;
4427 const w = &f.code.writer;
51624428
51634429 try w.writeAll("if (");
5164 try f.writeCValue(w, cond, .Other);
4430 try f.writeCValue(w, cond, .other);
51654431 try w.writeAll(") ");
51664432
51674433 try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);
5168 try f.object.newline();
5169 if (else_body.len > 0) if (f.object.dg.expected_block) |_|
4434 try f.newline();
4435 if (else_body.len > 0) if (f.dg.expected_block) |_|
51704436 return f.fail("runtime code not allowed in naked function", .{});
51714437
51724438 // We don't need to use `genBodyResolveState` for the else block, because this instruction is
......@@ -5184,23 +4450,23 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {
51844450}
51854451
51864452fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void {
5187 const pt = f.object.dg.pt;
4453 const pt = f.dg.pt;
51884454 const zcu = pt.zcu;
5189 const gpa = f.object.dg.gpa;
4455 const gpa = f.dg.gpa;
51904456 const switch_br = f.air.unwrapSwitch(inst);
51914457 const init_condition = try f.resolveInst(switch_br.operand);
51924458 try reap(f, inst, &.{switch_br.operand});
51934459 const condition_ty = f.typeOf(switch_br.operand);
5194 const w = &f.object.code.writer;
4460 const w = &f.code.writer;
51954461
51964462 // For dispatches, we will create a local alloc to contain the condition value.
51974463 // This may not result in optimal codegen for switch loops, but it minimizes the
51984464 // amount of C code we generate, which is probably more desirable here (and is simpler).
51994465 const condition = if (is_dispatch_loop) cond: {
52004466 const new_local = try f.allocLocal(inst, condition_ty);
5201 try f.copyCValue(try f.ctypeFromType(condition_ty, .complete), new_local, init_condition);
4467 try f.copyCValue(new_local, init_condition);
52024468 try w.print("zig_switch_{d}_loop:", .{@intFromEnum(inst)});
5203 try f.object.newline();
4469 try f.newline();
52044470 try f.loop_switch_conds.put(gpa, inst, new_local.new_local);
52054471 break :cond new_local;
52064472 } else init_condition;
......@@ -5222,9 +4488,9 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
52224488 try f.renderType(w, lowered_condition_ty);
52234489 try w.writeByte(')');
52244490 }
5225 try f.writeCValue(w, condition, .Other);
4491 try f.writeCValue(w, condition, .other);
52264492 try w.writeAll(") {");
5227 f.object.indent();
4493 f.indent();
52284494
52294495 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1);
52304496 defer gpa.free(liveness.deaths);
......@@ -5237,7 +4503,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
52374503 continue;
52384504 }
52394505 for (case.items) |item| {
5240 try f.object.newline();
4506 try f.newline();
52414507 try w.writeAll("case ");
52424508 const item_value = try f.air.value(item, pt);
52434509 // If `item_value` is a pointer with a known integer address, print the address
......@@ -5254,28 +4520,28 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
52544520 try f.renderType(w, .usize);
52554521 try w.writeByte(')');
52564522 }
5257 try f.object.dg.renderValue(w, (try f.air.value(item, pt)).?, .Other);
4523 try f.dg.renderValue(w, (try f.air.value(item, pt)).?, .other);
52584524 }
52594525 try w.writeByte(':');
52604526 }
52614527 try w.writeAll(" {");
5262 f.object.indent();
5263 try f.object.newline();
4528 f.indent();
4529 try f.newline();
52644530 if (is_dispatch_loop) {
52654531 try w.print("zig_switch_{d}_dispatch_{d}:;", .{ @intFromEnum(inst), case.idx });
5266 try f.object.newline();
4532 try f.newline();
52674533 }
52684534 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
5269 try f.object.outdent();
4535 try f.outdent();
52704536 try w.writeByte('}');
5271 if (f.object.dg.expected_block) |_|
4537 if (f.dg.expected_block) |_|
52724538 return f.fail("runtime code not allowed in naked function", .{});
52734539
52744540 // The case body must be noreturn so we don't need to insert a break.
52754541 }
52764542
52774543 const else_body = it.elseBody();
5278 try f.object.newline();
4544 try f.newline();
52794545
52804546 try w.writeAll("default: ");
52814547 if (any_range_cases) {
......@@ -5288,33 +4554,33 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
52884554 try w.writeAll("if (");
52894555 for (case.items, 0..) |item, item_i| {
52904556 if (item_i != 0) try w.writeAll(" || ");
5291 try f.writeCValue(w, condition, .Other);
4557 try f.writeCValue(w, condition, .other);
52924558 try w.writeAll(" == ");
5293 try f.object.dg.renderValue(w, (try f.air.value(item, pt)).?, .Other);
4559 try f.dg.renderValue(w, (try f.air.value(item, pt)).?, .other);
52944560 }
52954561 for (case.ranges, 0..) |range, range_i| {
52964562 if (case.items.len != 0 or range_i != 0) try w.writeAll(" || ");
52974563 // "(x >= lower && x <= upper)"
52984564 try w.writeByte('(');
5299 try f.writeCValue(w, condition, .Other);
4565 try f.writeCValue(w, condition, .other);
53004566 try w.writeAll(" >= ");
5301 try f.object.dg.renderValue(w, (try f.air.value(range[0], pt)).?, .Other);
4567 try f.dg.renderValue(w, (try f.air.value(range[0], pt)).?, .other);
53024568 try w.writeAll(" && ");
5303 try f.writeCValue(w, condition, .Other);
4569 try f.writeCValue(w, condition, .other);
53044570 try w.writeAll(" <= ");
5305 try f.object.dg.renderValue(w, (try f.air.value(range[1], pt)).?, .Other);
4571 try f.dg.renderValue(w, (try f.air.value(range[1], pt)).?, .other);
53064572 try w.writeByte(')');
53074573 }
53084574 try w.writeAll(") {");
5309 f.object.indent();
5310 try f.object.newline();
4575 f.indent();
4576 try f.newline();
53114577 if (is_dispatch_loop) {
53124578 try w.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });
53134579 }
53144580 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
5315 try f.object.outdent();
4581 try f.outdent();
53164582 try w.writeByte('}');
5317 if (f.object.dg.expected_block) |_|
4583 if (f.dg.expected_block) |_|
53184584 return f.fail("runtime code not allowed in naked function", .{});
53194585 }
53204586 }
......@@ -5328,16 +4594,16 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
53284594 try die(f, inst, death.toRef());
53294595 }
53304596 try genBody(f, else_body);
5331 if (f.object.dg.expected_block) |_|
4597 if (f.dg.expected_block) |_|
53324598 return f.fail("runtime code not allowed in naked function", .{});
5333 } else try airUnreach(&f.object);
5334 try f.object.newline();
5335 try f.object.outdent();
4599 } else try airUnreach(f);
4600 try f.newline();
4601 try f.outdent();
53364602 try w.writeAll("}\n");
53374603}
53384604
53394605fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool {
5340 const dg = f.object.dg;
4606 const dg = f.dg;
53414607 const target = &dg.mod.resolved_target.result;
53424608 return switch (constraint[0]) {
53434609 '{' => true,
......@@ -5357,28 +4623,28 @@ fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool
53574623}
53584624
53594625fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5360 const pt = f.object.dg.pt;
4626 const pt = f.dg.pt;
53614627 const zcu = pt.zcu;
53624628 const unwrapped_asm = f.air.unwrapAsm(inst);
53634629 const is_volatile = unwrapped_asm.is_volatile;
5364 const gpa = f.object.dg.gpa;
4630 const gpa = f.dg.gpa;
53654631 const outputs = unwrapped_asm.outputs;
53664632 const inputs = unwrapped_asm.inputs;
53674633
53684634 const result = result: {
5369 const w = &f.object.code.writer;
4635 const w = &f.code.writer;
53704636 const inst_ty = f.typeOfIndex(inst);
53714637 const inst_local = if (inst_ty.hasRuntimeBits(zcu)) local: {
53724638 const inst_local = try f.allocLocalValue(.{
5373 .ctype = try f.ctypeFromType(inst_ty, .complete),
5374 .alignas = CType.AlignAs.fromAbiAlignment(inst_ty.abiAlignment(zcu)),
4639 .type = inst_ty,
4640 .alignment = .none,
53754641 });
53764642 if (f.wantSafety()) {
5377 try f.writeCValue(w, inst_local, .Other);
4643 try f.writeCValue(w, inst_local, .other);
53784644 try w.writeAll(" = ");
5379 try f.writeCValue(w, .{ .undef = inst_ty }, .Other);
4645 try f.writeCValue(w, .{ .undef = inst_ty }, .other);
53804646 try w.writeByte(';');
5381 try f.object.newline();
4647 try f.newline();
53824648 }
53834649 break :local inst_local;
53844650 } else .none;
......@@ -5399,20 +4665,20 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
53994665 const output_ty = if (output.operand == .none) inst_ty else f.typeOf(output.operand).childType(zcu);
54004666 try w.writeAll("register ");
54014667 const output_local = try f.allocLocalValue(.{
5402 .ctype = try f.ctypeFromType(output_ty, .complete),
5403 .alignas = CType.AlignAs.fromAbiAlignment(output_ty.abiAlignment(zcu)),
4668 .type = output_ty,
4669 .alignment = .none,
54044670 });
54054671 try f.allocs.put(gpa, output_local.new_local, false);
5406 try f.object.dg.renderTypeAndName(w, output_ty, output_local, .{}, .none, .complete);
4672 try f.dg.renderTypeAndName(w, output_ty, output_local, .{}, .none);
54074673 try w.writeAll(" __asm(\"");
54084674 try w.writeAll(constraint["={".len .. constraint.len - "}".len]);
54094675 try w.writeAll("\")");
54104676 if (f.wantSafety()) {
54114677 try w.writeAll(" = ");
5412 try f.writeCValue(w, .{ .undef = output_ty }, .Other);
4678 try f.writeCValue(w, .{ .undef = output_ty }, .other);
54134679 }
54144680 try w.writeByte(';');
5415 try f.object.newline();
4681 try f.newline();
54164682 }
54174683 }
54184684
......@@ -5432,29 +4698,29 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
54324698 const input_ty = f.typeOf(input.operand);
54334699 if (is_reg) try w.writeAll("register ");
54344700 const input_local = try f.allocLocalValue(.{
5435 .ctype = try f.ctypeFromType(input_ty, .complete),
5436 .alignas = CType.AlignAs.fromAbiAlignment(input_ty.abiAlignment(zcu)),
4701 .type = input_ty,
4702 .alignment = .none,
54374703 });
54384704 try f.allocs.put(gpa, input_local.new_local, false);
54394705 // Do not render the declaration as `const` qualified if we're generating an
54404706 // explicit `register` local, as GCC will ignore the constraint completely.
5441 try f.object.dg.renderTypeAndName(w, input_ty, input_local, if (is_reg) .{} else Const, .none, .complete);
4707 try f.dg.renderTypeAndName(w, input_ty, input_local, .{ .@"const" = is_reg }, .none);
54424708 if (is_reg) {
54434709 try w.writeAll(" __asm(\"");
54444710 try w.writeAll(constraint["{".len .. constraint.len - "}".len]);
54454711 try w.writeAll("\")");
54464712 }
54474713 try w.writeAll(" = ");
5448 try f.writeCValue(w, input_val, .Other);
4714 try f.writeCValue(w, input_val, .other);
54494715 try w.writeByte(';');
5450 try f.object.newline();
4716 try f.newline();
54514717 }
54524718 }
54534719
54544720 {
54554721 const asm_source = unwrapped_asm.source;
54564722
5457 var stack = std.heap.stackFallback(256, f.object.dg.gpa);
4723 var stack = std.heap.stackFallback(256, f.dg.gpa);
54584724 const allocator = stack.get();
54594725 const fixed_asm_source = try allocator.alloc(u8, asm_source.len);
54604726 defer allocator.free(fixed_asm_source);
......@@ -5520,10 +4786,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
55204786 const is_reg = constraint[1] == '{';
55214787 try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)});
55224788 if (is_reg) {
5523 try f.writeCValue(w, .{ .local = locals_index }, .Other);
4789 try f.writeCValue(w, .{ .local = locals_index }, .other);
55244790 locals_index += 1;
55254791 } else if (output.operand == .none) {
5526 try f.writeCValue(w, inst_local, .FunctionArgument);
4792 try f.writeCValue(w, inst_local, .other);
55274793 } else {
55284794 try f.writeCValueDeref(w, try f.resolveInst(output.operand));
55294795 }
......@@ -5547,7 +4813,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
55474813 const input_local_idx = locals_index;
55484814 locals_index += 1;
55494815 break :local .{ .local = input_local_idx };
5550 } else input_val, .Other);
4816 } else input_val, .other);
55514817 try w.writeByte(')');
55524818 }
55534819 try w.writeByte(':');
......@@ -5567,7 +4833,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
55674833 const field_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
55684834 assert(field_name.len != 0);
55694835
5570 const target = &f.object.dg.mod.resolved_target.result;
4836 const target = &f.dg.mod.resolved_target.result;
55714837 var c_name_buf: [16]u8 = undefined;
55724838 const name =
55734839 if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: {
......@@ -5594,7 +4860,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
55944860 }
55954861 w.undo(1); // erase the last comma
55964862 try w.writeAll(");");
5597 try f.object.newline();
4863 try f.newline();
55984864
55994865 locals_index = locals_begin;
56004866 it = unwrapped_asm.iterateOutputs();
......@@ -5608,10 +4874,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
56084874 else
56094875 try f.resolveInst(output.operand));
56104876 try w.writeAll(" = ");
5611 try f.writeCValue(w, .{ .local = locals_index }, .Other);
4877 try f.writeCValue(w, .{ .local = locals_index }, .other);
56124878 locals_index += 1;
56134879 try w.writeByte(';');
5614 try f.object.newline();
4880 try f.newline();
56154881 }
56164882 }
56174883
......@@ -5633,147 +4899,145 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
56334899fn airIsNull(
56344900 f: *Function,
56354901 inst: Air.Inst.Index,
5636 operator: std.math.CompareOperator,
4902 operator: enum { eq, neq },
56374903 is_ptr: bool,
56384904) !CValue {
5639 const pt = f.object.dg.pt;
4905 const pt = f.dg.pt;
56404906 const zcu = pt.zcu;
5641 const ctype_pool = &f.object.dg.ctype_pool;
56424907 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
56434908
5644 const w = &f.object.code.writer;
4909 const w = &f.code.writer;
56454910 const operand = try f.resolveInst(un_op);
56464911 try reap(f, inst, &.{un_op});
56474912
56484913 const local = try f.allocLocal(inst, .bool);
5649 const a = try Assignment.start(f, w, .bool);
5650 try f.writeCValue(w, local, .Other);
5651 try a.assign(f, w);
4914 try f.writeCValue(w, local, .other);
4915 try w.writeAll(" = ");
56524916
56534917 const operand_ty = f.typeOf(un_op);
56544918 const optional_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
5655 const opt_ctype = try f.ctypeFromType(optional_ty, .complete);
5656 const rhs = switch (opt_ctype.info(ctype_pool)) {
5657 .basic, .pointer => rhs: {
5658 if (is_ptr)
5659 try f.writeCValueDeref(w, operand)
5660 else
5661 try f.writeCValue(w, operand, .Other);
5662 break :rhs if (opt_ctype.isBool())
5663 "true"
5664 else if (opt_ctype.isInteger())
5665 "0"
5666 else
5667 "NULL";
4919
4920 const pre: []const u8, const maybe_field: ?[]const u8, const post: []const u8 = switch (operator) {
4921 // zig fmt: off
4922 .eq => switch (CType.classifyOptional(optional_ty, zcu)) {
4923 .npv_payload => unreachable, // opv optional
4924 .error_set => .{ "", null, " == 0" },
4925 .ptr_like => .{ "", null, " == NULL" },
4926 .slice_like => .{ "", "ptr", " == NULL" },
4927 .opv_payload => .{ "", "is_null", "" },
4928 .@"struct" => .{ "", "is_null", "" },
56684929 },
5669 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
5670 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
5671 .is_null, .payload => rhs: {
5672 if (is_ptr)
5673 try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" })
5674 else
5675 try f.writeCValueMember(w, operand, .{ .identifier = "is_null" });
5676 break :rhs "true";
5677 },
5678 .ptr, .len => rhs: {
5679 if (is_ptr)
5680 try f.writeCValueDerefMember(w, operand, .{ .identifier = "ptr" })
5681 else
5682 try f.writeCValueMember(w, operand, .{ .identifier = "ptr" });
5683 break :rhs "NULL";
5684 },
5685 else => unreachable,
4930 .neq => switch (CType.classifyOptional(optional_ty, zcu)) {
4931 .npv_payload => unreachable, // opv optional
4932 .error_set => .{ "", null, " != 0" },
4933 .ptr_like => .{ "", null, " != NULL" },
4934 .slice_like => .{ "", "ptr", " != NULL" },
4935 .opv_payload => .{ "!", "is_null", "" },
4936 .@"struct" => .{ "!", "is_null", "" },
56864937 },
4938 // zig fmt: on
56874939 };
5688 try w.writeAll(compareOperatorC(operator));
5689 try w.writeAll(rhs);
5690 try a.end(f, w);
4940
4941 try w.writeAll(pre);
4942 if (maybe_field) |field| {
4943 if (is_ptr) {
4944 try f.writeCValueDerefMember(w, operand, .{ .identifier = field });
4945 } else {
4946 try f.writeCValueMember(w, operand, .{ .identifier = field });
4947 }
4948 } else {
4949 if (is_ptr) {
4950 try f.writeCValueDeref(w, operand);
4951 } else {
4952 try f.writeCValue(w, operand, .other);
4953 }
4954 }
4955 try w.writeAll(post);
4956
4957 try w.writeByte(';');
4958 try f.newline();
56914959 return local;
56924960}
56934961
56944962fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
5695 const pt = f.object.dg.pt;
4963 const pt = f.dg.pt;
56964964 const zcu = pt.zcu;
5697 const ctype_pool = &f.object.dg.ctype_pool;
56984965 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
56994966
57004967 const inst_ty = f.typeOfIndex(inst);
57014968 const operand_ty = f.typeOf(ty_op.operand);
57024969 const opt_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
5703 const opt_ctype = try f.ctypeFromType(opt_ty, .complete);
5704 if (opt_ctype.isBool()) return if (is_ptr) .{ .undef = inst_ty } else .none;
57054970
57064971 const operand = try f.resolveInst(ty_op.operand);
5707 switch (opt_ctype.info(ctype_pool)) {
5708 .basic, .pointer => return f.moveCValue(inst, inst_ty, operand),
5709 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
5710 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
5711 .is_null, .payload => {
5712 const w = &f.object.code.writer;
5713 const local = try f.allocLocal(inst, inst_ty);
5714 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
5715 try f.writeCValue(w, local, .Other);
5716 try a.assign(f, w);
5717 if (is_ptr) {
5718 try w.writeByte('&');
5719 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });
5720 } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" });
5721 try a.end(f, w);
5722 return local;
5723 },
5724 .ptr, .len => return f.moveCValue(inst, inst_ty, operand),
5725 else => unreachable,
4972
4973 switch (CType.classifyOptional(opt_ty, zcu)) {
4974 .npv_payload => unreachable, // opv optional
4975
4976 .opv_payload => return if (is_ptr) .{ .undef = inst_ty } else .none,
4977
4978 .error_set,
4979 .ptr_like,
4980 .slice_like,
4981 => return f.moveCValue(inst, inst_ty, operand),
4982
4983 .@"struct" => {
4984 const w = &f.code.writer;
4985 const local = try f.allocLocal(inst, inst_ty);
4986 try f.writeCValue(w, local, .other);
4987 try w.writeAll(" = ");
4988 if (is_ptr) {
4989 try w.writeByte('&');
4990 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });
4991 } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" });
4992 try w.writeByte(';');
4993 try f.newline();
4994 return local;
57264995 },
57274996 }
57284997}
57294998
57304999fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
5731 const pt = f.object.dg.pt;
5000 const pt = f.dg.pt;
57325001 const zcu = pt.zcu;
57335002 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
5734 const w = &f.object.code.writer;
5003 const w = &f.code.writer;
57355004 const operand = try f.resolveInst(ty_op.operand);
57365005 try reap(f, inst, &.{ty_op.operand});
57375006 const operand_ty = f.typeOf(ty_op.operand);
5007 const opt_ty = operand_ty.childType(zcu);
57385008
57395009 const inst_ty = f.typeOfIndex(inst);
5740 const opt_ctype = try f.ctypeFromType(operand_ty.childType(zcu), .complete);
5741 switch (opt_ctype.info(&f.object.dg.ctype_pool)) {
5742 .basic => {
5743 const a = try Assignment.start(f, w, opt_ctype);
5744 try f.writeCValueDeref(w, operand);
5745 try a.assign(f, w);
5746 try f.object.dg.renderValue(w, Value.false, .Other);
5747 try a.end(f, w);
5748 return .none;
5749 },
5750 .pointer => {
5751 if (f.liveness.isUnused(inst)) return .none;
5752 const local = try f.allocLocal(inst, inst_ty);
5753 const a = try Assignment.start(f, w, opt_ctype);
5754 try f.writeCValue(w, local, .Other);
5755 try a.assign(f, w);
5756 try f.writeCValue(w, operand, .Other);
5757 try a.end(f, w);
5758 return local;
5010
5011 switch (CType.classifyOptional(opt_ty, zcu)) {
5012 .npv_payload => unreachable, // opv optional
5013
5014 .opv_payload => {
5015 try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" });
5016 try w.writeAll(" = ");
5017 try f.dg.renderValue(w, .false, .other);
5018 try w.writeByte(';');
5019 try f.newline();
5020 return .{ .undef = inst_ty };
57595021 },
5760 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
5761 .aggregate => {
5762 {
5763 const a = try Assignment.start(f, w, opt_ctype);
5764 try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" });
5765 try a.assign(f, w);
5766 try f.object.dg.renderValue(w, Value.false, .Other);
5767 try a.end(f, w);
5768 }
5022
5023 .error_set,
5024 .ptr_like,
5025 .slice_like,
5026 => return f.moveCValue(inst, inst_ty, operand),
5027
5028 .@"struct" => {
5029 try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" });
5030 try w.writeAll(" = ");
5031 try f.dg.renderValue(w, .false, .other);
5032 try w.writeByte(';');
5033 try f.newline();
57695034 if (f.liveness.isUnused(inst)) return .none;
57705035 const local = try f.allocLocal(inst, inst_ty);
5771 const a = try Assignment.start(f, w, opt_ctype);
5772 try f.writeCValue(w, local, .Other);
5773 try a.assign(f, w);
5774 try w.writeByte('&');
5036 try f.writeCValue(w, local, .other);
5037 try w.writeAll(" = &");
57755038 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });
5776 try a.end(f, w);
5039 try w.writeByte(';');
5040 try f.newline();
57775041 return local;
57785042 },
57795043 }
......@@ -5817,18 +5081,20 @@ fn fieldLocation(
58175081 .union_type => {
58185082 const loaded_union = ip.loadUnionType(container_ty.toIntern());
58195083 switch (loaded_union.layout) {
5820 .auto, .@"extern" => {
5084 .auto => {
58215085 const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
5822 if (!field_ty.hasRuntimeBits(zcu))
5823 return if (loaded_union.has_runtime_tag and !container_ty.unionHasAllZeroBitFieldTypes(zcu))
5824 .{ .field = .{ .identifier = "payload" } }
5825 else
5826 .begin;
5086 if (!field_ty.hasRuntimeBits(zcu)) {
5087 if (container_ty.unionHasAllZeroBitFieldTypes(zcu)) return .begin;
5088 return .{ .field = .{ .identifier = "payload" } };
5089 }
58275090 const field_name = ip.loadEnumType(loaded_union.enum_tag_type).field_names.get(ip)[field_index];
5828 return .{ .field = if (loaded_union.has_runtime_tag)
5829 .{ .payload_identifier = field_name.toSlice(ip) }
5830 else
5831 .{ .identifier = field_name.toSlice(ip) } };
5091 return .{ .field = .{ .payload_identifier = field_name.toSlice(ip) } };
5092 },
5093 .@"extern" => {
5094 const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
5095 if (!field_ty.hasRuntimeBits(zcu)) return .begin;
5096 const field_name = ip.loadEnumType(loaded_union.enum_tag_type).field_names.get(ip)[field_index];
5097 return .{ .field = .{ .identifier = field_name.toSlice(ip) } };
58325098 },
58335099 .@"packed" => return .begin,
58345100 }
......@@ -5865,7 +5131,7 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue
58655131}
58665132
58675133fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5868 const pt = f.object.dg.pt;
5134 const pt = f.dg.pt;
58695135 const zcu = pt.zcu;
58705136 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
58715137 const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
......@@ -5877,26 +5143,26 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
58775143 const field_ptr_val = try f.resolveInst(extra.field_ptr);
58785144 try reap(f, inst, &.{extra.field_ptr});
58795145
5880 const w = &f.object.code.writer;
5146 const w = &f.code.writer;
58815147 const local = try f.allocLocal(inst, container_ptr_ty);
5882 try f.writeCValue(w, local, .Other);
5148 try f.writeCValue(w, local, .other);
58835149 try w.writeAll(" = (");
58845150 try f.renderType(w, container_ptr_ty);
58855151 try w.writeByte(')');
58865152
58875153 switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, zcu)) {
5888 .begin => try f.writeCValue(w, field_ptr_val, .Other),
5154 .begin => try f.writeCValue(w, field_ptr_val, .other),
58895155 .field => |field| {
58905156 const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);
58915157
58925158 try w.writeAll("((");
58935159 try f.renderType(w, u8_ptr_ty);
58945160 try w.writeByte(')');
5895 try f.writeCValue(w, field_ptr_val, .Other);
5161 try f.writeCValue(w, field_ptr_val, .other);
58965162 try w.writeAll(" - offsetof(");
58975163 try f.renderType(w, container_ty);
58985164 try w.writeAll(", ");
5899 try f.writeCValue(w, field, .Other);
5165 try f.writeCValue(w, field, .other);
59005166 try w.writeAll("))");
59015167 },
59025168 .byte_offset => |byte_offset| {
......@@ -5905,7 +5171,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
59055171 try w.writeAll("((");
59065172 try f.renderType(w, u8_ptr_ty);
59075173 try w.writeByte(')');
5908 try f.writeCValue(w, field_ptr_val, .Other);
5174 try f.writeCValue(w, field_ptr_val, .other);
59095175 try w.print(" - {f})", .{
59105176 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
59115177 });
......@@ -5913,7 +5179,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
59135179 }
59145180
59155181 try w.writeByte(';');
5916 try f.object.newline();
5182 try f.newline();
59175183 return local;
59185184}
59195185
......@@ -5924,23 +5190,19 @@ fn fieldPtr(
59245190 container_ptr_val: CValue,
59255191 field_index: u32,
59265192) !CValue {
5927 const pt = f.object.dg.pt;
5193 const pt = f.dg.pt;
59285194 const zcu = pt.zcu;
5929 const container_ty = container_ptr_ty.childType(zcu);
59305195 const field_ptr_ty = f.typeOfIndex(inst);
59315196
5932 // Ensure complete type definition is visible before accessing fields.
5933 _ = try f.ctypeFromType(container_ty, .complete);
5934
5935 const w = &f.object.code.writer;
5197 const w = &f.code.writer;
59365198 const local = try f.allocLocal(inst, field_ptr_ty);
5937 try f.writeCValue(w, local, .Other);
5199 try f.writeCValue(w, local, .other);
59385200 try w.writeAll(" = (");
59395201 try f.renderType(w, field_ptr_ty);
59405202 try w.writeByte(')');
59415203
59425204 switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, zcu)) {
5943 .begin => try f.writeCValue(w, container_ptr_val, .Other),
5205 .begin => try f.writeCValue(w, container_ptr_val, .other),
59445206 .field => |field| {
59455207 try w.writeByte('&');
59465208 try f.writeCValueDerefMember(w, container_ptr_val, field);
......@@ -5951,7 +5213,7 @@ fn fieldPtr(
59515213 try w.writeAll("((");
59525214 try f.renderType(w, u8_ptr_ty);
59535215 try w.writeByte(')');
5954 try f.writeCValue(w, container_ptr_val, .Other);
5216 try f.writeCValue(w, container_ptr_val, .other);
59555217 try w.print(" + {f})", .{
59565218 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
59575219 });
......@@ -5959,12 +5221,12 @@ fn fieldPtr(
59595221 }
59605222
59615223 try w.writeByte(';');
5962 try f.object.newline();
5224 try f.newline();
59635225 return local;
59645226}
59655227
59665228fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
5967 const pt = f.object.dg.pt;
5229 const pt = f.dg.pt;
59685230 const zcu = pt.zcu;
59695231 const ip = &zcu.intern_pool;
59705232 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
......@@ -5976,10 +5238,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
59765238 const struct_byval = try f.resolveInst(extra.struct_operand);
59775239 try reap(f, inst, &.{extra.struct_operand});
59785240 const struct_ty = f.typeOf(extra.struct_operand);
5979 const w = &f.object.code.writer;
5980
5981 // Ensure complete type definition is visible before accessing fields.
5982 _ = try f.ctypeFromType(struct_ty, .complete);
5241 const w = &f.code.writer;
59835242
59845243 assert(struct_ty.containerLayout(zcu) != .@"packed"); // `Air.Legalize.Feature.expand_packed_struct_field_val` handles this case
59855244 const field_name: CValue = switch (ip.indexToKey(struct_ty.toIntern())) {
......@@ -5988,29 +5247,25 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
59885247 const union_type = ip.loadUnionType(struct_ty.toIntern());
59895248 const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type);
59905249 const field_name_str = enum_tag_ty.enumFieldName(extra.field_index, zcu).toSlice(ip);
5991 if (union_type.has_runtime_tag) {
5992 break :name .{ .payload_identifier = field_name_str };
5993 } else {
5994 break :name .{ .identifier = field_name_str };
5995 }
5250 break :name .{ .payload_identifier = field_name_str };
59965251 },
59975252 .tuple_type => .{ .field = extra.field_index },
59985253 else => unreachable,
59995254 };
60005255
60015256 const local = try f.allocLocal(inst, inst_ty);
6002 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
6003 try f.writeCValue(w, local, .Other);
6004 try a.assign(f, w);
5257 try f.writeCValue(w, local, .other);
5258 try w.writeAll(" = ");
60055259 try f.writeCValueMember(w, struct_byval, field_name);
6006 try a.end(f, w);
5260 try w.writeByte(';');
5261 try f.newline();
60075262 return local;
60085263}
60095264
60105265/// *(E!T) -> E
60115266/// Note that the result is never a pointer.
60125267fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6013 const pt = f.object.dg.pt;
5268 const pt = f.dg.pt;
60145269 const zcu = pt.zcu;
60155270 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
60165271
......@@ -6020,37 +5275,23 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
60205275 try reap(f, inst, &.{ty_op.operand});
60215276
60225277 const operand_is_ptr = operand_ty.zigTypeTag(zcu) == .pointer;
6023 const error_union_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty;
6024 const error_ty = error_union_ty.errorUnionSet(zcu);
6025 const payload_ty = error_union_ty.errorUnionPayload(zcu);
60265278 const local = try f.allocLocal(inst, inst_ty);
60275279
6028 if (!payload_ty.hasRuntimeBits(zcu) and operand == .local and operand.local == local.new_local) {
6029 // The store will be 'x = x'; elide it.
6030 return local;
6031 }
6032
6033 const w = &f.object.code.writer;
6034 try f.writeCValue(w, local, .Other);
5280 const w = &f.code.writer;
5281 try f.writeCValue(w, local, .other);
60355282 try w.writeAll(" = ");
60365283
6037 if (!payload_ty.hasRuntimeBits(zcu))
6038 try f.writeCValue(w, operand, .Other)
6039 else if (error_ty.errorSetIsEmpty(zcu))
6040 try w.print("{f}", .{
6041 try f.fmtIntLiteralDec(try pt.intValue(try pt.errorIntType(), 0)),
6042 })
6043 else if (operand_is_ptr)
5284 if (operand_is_ptr)
60445285 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })
60455286 else
60465287 try f.writeCValueMember(w, operand, .{ .identifier = "error" });
60475288 try w.writeByte(';');
6048 try f.object.newline();
5289 try f.newline();
60495290 return local;
60505291}
60515292
60525293fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
6053 const pt = f.object.dg.pt;
5294 const pt = f.dg.pt;
60545295 const zcu = pt.zcu;
60555296 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
60565297
......@@ -6060,154 +5301,124 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
60605301 const operand_ty = f.typeOf(ty_op.operand);
60615302 const error_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
60625303
6063 const w = &f.object.code.writer;
5304 const w = &f.code.writer;
60645305 if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {
6065 if (!is_ptr) return .none;
6066
5306 assert(is_ptr); // opv bug in sema
60675307 const local = try f.allocLocal(inst, inst_ty);
6068 try f.writeCValue(w, local, .Other);
5308 try f.writeCValue(w, local, .other);
60695309 try w.writeAll(" = (");
60705310 try f.renderType(w, inst_ty);
60715311 try w.writeByte(')');
6072 try f.writeCValue(w, operand, .Other);
5312 try f.writeCValue(w, operand, .other);
60735313 try w.writeByte(';');
6074 try f.object.newline();
5314 try f.newline();
60755315 return local;
60765316 }
60775317
60785318 const local = try f.allocLocal(inst, inst_ty);
6079 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
6080 try f.writeCValue(w, local, .Other);
6081 try a.assign(f, w);
5319 try f.writeCValue(w, local, .other);
5320 try w.writeAll(" = ");
60825321 if (is_ptr) {
60835322 try w.writeByte('&');
60845323 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });
60855324 } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" });
6086 try a.end(f, w);
5325 try w.writeByte(';');
5326 try f.newline();
60875327 return local;
60885328}
60895329
60905330fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
6091 const ctype_pool = &f.object.dg.ctype_pool;
5331 const zcu = f.dg.pt.zcu;
60925332 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
60935333
60945334 const inst_ty = f.typeOfIndex(inst);
6095 const inst_ctype = try f.ctypeFromType(inst_ty, .complete);
6096 if (inst_ctype.isBool()) return .{ .constant = Value.true };
60975335
60985336 const operand = try f.resolveInst(ty_op.operand);
6099 switch (inst_ctype.info(ctype_pool)) {
6100 .basic, .pointer => return f.moveCValue(inst, inst_ty, operand),
6101 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
6102 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
6103 .is_null, .payload => {
6104 const operand_ctype = try f.ctypeFromType(f.typeOf(ty_op.operand), .complete);
6105 const w = &f.object.code.writer;
6106 const local = try f.allocLocal(inst, inst_ty);
6107 {
6108 const a = try Assignment.start(f, w, .bool);
6109 try f.writeCValueMember(w, local, .{ .identifier = "is_null" });
6110 try a.assign(f, w);
6111 try w.writeAll("false");
6112 try a.end(f, w);
6113 }
6114 {
6115 const a = try Assignment.start(f, w, operand_ctype);
6116 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6117 try a.assign(f, w);
6118 try f.writeCValue(w, operand, .Other);
6119 try a.end(f, w);
6120 }
6121 return local;
6122 },
6123 .ptr, .len => return f.moveCValue(inst, inst_ty, operand),
6124 else => unreachable,
5337
5338 switch (CType.classifyOptional(inst_ty, zcu)) {
5339 .npv_payload => unreachable, // opv optional
5340
5341 .opv_payload => unreachable, // opv bug in Sema
5342
5343 .error_set,
5344 .ptr_like,
5345 .slice_like,
5346 => return f.moveCValue(inst, inst_ty, operand),
5347
5348 .@"struct" => {
5349 const w = &f.code.writer;
5350 const local = try f.allocLocal(inst, inst_ty);
5351
5352 try f.writeCValueMember(w, local, .{ .identifier = "is_null" });
5353 try w.writeAll(" = false;");
5354 try f.newline();
5355
5356 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
5357 try w.writeAll(" = ");
5358 try f.writeCValue(w, operand, .other);
5359 try w.writeByte(';');
5360 try f.newline();
5361
5362 return local;
61255363 },
61265364 }
61275365}
61285366
61295367fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6130 const pt = f.object.dg.pt;
5368 const pt = f.dg.pt;
61315369 const zcu = pt.zcu;
61325370 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
61335371
61345372 const inst_ty = f.typeOfIndex(inst);
61355373 const payload_ty = inst_ty.errorUnionPayload(zcu);
6136 const repr_is_err = !payload_ty.hasRuntimeBits(zcu);
6137 const err_ty = inst_ty.errorUnionSet(zcu);
61385374 const err = try f.resolveInst(ty_op.operand);
61395375 try reap(f, inst, &.{ty_op.operand});
61405376
6141 const w = &f.object.code.writer;
5377 const w = &f.code.writer;
61425378 const local = try f.allocLocal(inst, inst_ty);
61435379
6144 if (repr_is_err and err == .local and err.local == local.new_local) {
6145 // The store will be 'x = x'; elide it.
6146 return local;
6147 }
6148
6149 if (!repr_is_err) {
6150 const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete));
5380 if (payload_ty.hasRuntimeBits(zcu)) {
61515381 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6152 try a.assign(f, w);
6153 try f.object.dg.renderUndefValue(w, payload_ty, .Other);
6154 try a.end(f, w);
6155 }
6156 {
6157 const a = try Assignment.start(f, w, try f.ctypeFromType(err_ty, .complete));
6158 if (repr_is_err)
6159 try f.writeCValue(w, local, .Other)
6160 else
6161 try f.writeCValueMember(w, local, .{ .identifier = "error" });
6162 try a.assign(f, w);
6163 try f.writeCValue(w, err, .Other);
6164 try a.end(f, w);
5382 try w.writeAll(" = ");
5383 try f.dg.renderUndefValue(w, payload_ty, .other);
5384 try w.writeByte(';');
5385 try f.newline();
61655386 }
5387
5388 try f.writeCValueMember(w, local, .{ .identifier = "error" });
5389 try w.writeAll(" = ");
5390 try f.writeCValue(w, err, .other);
5391 try w.writeByte(';');
5392 try f.newline();
5393
61665394 return local;
61675395}
61685396
61695397fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
6170 const pt = f.object.dg.pt;
6171 const zcu = pt.zcu;
6172 const w = &f.object.code.writer;
5398 const pt = f.dg.pt;
5399 const w = &f.code.writer;
61735400 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
61745401 const inst_ty = f.typeOfIndex(inst);
61755402 const operand = try f.resolveInst(ty_op.operand);
6176 const operand_ty = f.typeOf(ty_op.operand);
6177 const error_union_ty = operand_ty.childType(zcu);
61785403
6179 const payload_ty = error_union_ty.errorUnionPayload(zcu);
61805404 const err_int_ty = try pt.errorIntType();
61815405 const no_err = try pt.intValue(err_int_ty, 0);
61825406 try reap(f, inst, &.{ty_op.operand});
61835407
61845408 // First, set the non-error value.
6185 if (!payload_ty.hasRuntimeBits(zcu)) {
6186 const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete));
6187 try f.writeCValueDeref(w, operand);
6188 try a.assign(f, w);
6189 try w.print("{f}", .{try f.fmtIntLiteralDec(no_err)});
6190 try a.end(f, w);
6191 return .none;
6192 }
6193 {
6194 const a = try Assignment.start(f, w, try f.ctypeFromType(err_int_ty, .complete));
6195 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" });
6196 try a.assign(f, w);
6197 try w.print("{f}", .{try f.fmtIntLiteralDec(no_err)});
6198 try a.end(f, w);
6199 }
5409 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" });
5410 try w.print(" = {f};", .{try f.fmtIntLiteralDec(no_err)});
5411 try f.newline();
62005412
62015413 // Then return the payload pointer (only if it is used)
62025414 if (f.liveness.isUnused(inst)) return .none;
62035415
62045416 const local = try f.allocLocal(inst, inst_ty);
6205 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
6206 try f.writeCValue(w, local, .Other);
6207 try a.assign(f, w);
6208 try w.writeByte('&');
5417 try f.writeCValue(w, local, .other);
5418 try w.writeAll(" = &");
62095419 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });
6210 try a.end(f, w);
5420 try w.writeByte(';');
5421 try f.newline();
62115422 return local;
62125423}
62135424
......@@ -6227,7 +5438,7 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {
62275438}
62285439
62295440fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
6230 const pt = f.object.dg.pt;
5441 const pt = f.dg.pt;
62315442 const zcu = pt.zcu;
62325443 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
62335444
......@@ -6235,120 +5446,88 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
62355446 const payload_ty = inst_ty.errorUnionPayload(zcu);
62365447 const payload = try f.resolveInst(ty_op.operand);
62375448 assert(payload_ty.hasRuntimeBits(zcu));
6238 const err_ty = inst_ty.errorUnionSet(zcu);
62395449 try reap(f, inst, &.{ty_op.operand});
62405450
6241 const w = &f.object.code.writer;
5451 const w = &f.code.writer;
62425452 const local = try f.allocLocal(inst, inst_ty);
6243 {
6244 const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete));
6245 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6246 try a.assign(f, w);
6247 try f.writeCValue(w, payload, .Other);
6248 try a.end(f, w);
6249 }
6250 {
6251 const a = try Assignment.start(f, w, try f.ctypeFromType(err_ty, .complete));
6252 try f.writeCValueMember(w, local, .{ .identifier = "error" });
6253 try a.assign(f, w);
6254 try f.object.dg.renderValue(w, try pt.intValue(try pt.errorIntType(), 0), .Other);
6255 try a.end(f, w);
6256 }
5453
5454 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
5455 try w.writeAll(" = ");
5456 try f.writeCValue(w, payload, .other);
5457 try w.writeByte(';');
5458 try f.newline();
5459
5460 try f.writeCValueMember(w, local, .{ .identifier = "error" });
5461 try w.writeAll(" = ");
5462 try f.dg.renderValue(w, try pt.intValue(try pt.errorIntType(), 0), .other);
5463 try w.writeByte(';');
5464 try f.newline();
5465
62575466 return local;
62585467}
62595468
62605469fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue {
6261 const pt = f.object.dg.pt;
6262 const zcu = pt.zcu;
5470 const pt = f.dg.pt;
62635471 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
62645472
6265 const w = &f.object.code.writer;
5473 const w = &f.code.writer;
62665474 const operand = try f.resolveInst(un_op);
62675475 try reap(f, inst, &.{un_op});
6268 const operand_ty = f.typeOf(un_op);
62695476 const local = try f.allocLocal(inst, .bool);
6270 const err_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
6271 const payload_ty = err_union_ty.errorUnionPayload(zcu);
6272 const error_ty = err_union_ty.errorUnionSet(zcu);
62735477
6274 const a = try Assignment.start(f, w, .bool);
6275 try f.writeCValue(w, local, .Other);
6276 try a.assign(f, w);
5478 try f.writeCValue(w, local, .other);
5479 try w.writeAll(" = ");
62775480 const err_int_ty = try pt.errorIntType();
6278 if (!error_ty.errorSetIsEmpty(zcu))
6279 if (payload_ty.hasRuntimeBits(zcu))
6280 if (is_ptr)
6281 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })
6282 else
6283 try f.writeCValueMember(w, operand, .{ .identifier = "error" })
6284 else
6285 try f.writeCValue(w, operand, .Other)
5481 if (is_ptr)
5482 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })
62865483 else
6287 try f.object.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .Other);
6288 try w.writeByte(' ');
6289 try w.writeAll(operator);
6290 try w.writeByte(' ');
6291 try f.object.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .Other);
6292 try a.end(f, w);
5484 try f.writeCValueMember(w, operand, .{ .identifier = "error" });
5485 try w.print(" {s} ", .{operator});
5486 try f.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .other);
5487 try w.writeByte(';');
5488 try f.newline();
62935489 return local;
62945490}
62955491
62965492fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
6297 const pt = f.object.dg.pt;
5493 const pt = f.dg.pt;
62985494 const zcu = pt.zcu;
6299 const ctype_pool = &f.object.dg.ctype_pool;
63005495 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
63015496
63025497 const operand = try f.resolveInst(ty_op.operand);
63035498 try reap(f, inst, &.{ty_op.operand});
63045499 const inst_ty = f.typeOfIndex(inst);
6305 const ptr_ty = inst_ty.slicePtrFieldType(zcu);
6306 const w = &f.object.code.writer;
5500 const w = &f.code.writer;
63075501 const local = try f.allocLocal(inst, inst_ty);
63085502 const operand_ty = f.typeOf(ty_op.operand);
63095503 const array_ty = operand_ty.childType(zcu);
63105504
6311 {
6312 const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete));
6313 try f.writeCValueMember(w, local, .{ .identifier = "ptr" });
6314 try a.assign(f, w);
6315 if (operand == .undef) {
6316 try f.writeCValue(w, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Other);
6317 } else {
6318 const ptr_ctype = try f.ctypeFromType(ptr_ty, .complete);
6319 const ptr_child_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;
6320 const elem_ty = array_ty.childType(zcu);
6321 const elem_ctype = try f.ctypeFromType(elem_ty, .complete);
6322 if (!ptr_child_ctype.eql(elem_ctype)) {
6323 try w.writeByte('(');
6324 try f.renderCType(w, ptr_ctype);
6325 try w.writeByte(')');
6326 }
6327 const operand_ctype = try f.ctypeFromType(operand_ty, .complete);
6328 const operand_child_ctype = operand_ctype.info(ctype_pool).pointer.elem_ctype;
6329 if (operand_child_ctype.info(ctype_pool) == .array) {
6330 try w.writeByte('&');
6331 try f.writeCValueDeref(w, operand);
6332 try w.print("[{f}]", .{try f.fmtIntLiteralDec(.zero_usize)});
6333 } else try f.writeCValue(w, operand, .Other);
6334 }
6335 try a.end(f, w);
6336 }
6337 {
6338 const a = try Assignment.start(f, w, .usize);
6339 try f.writeCValueMember(w, local, .{ .identifier = "len" });
6340 try a.assign(f, w);
6341 try w.print("{f}", .{
6342 try f.fmtIntLiteralDec(try pt.intValue(.usize, array_ty.arrayLen(zcu))),
6343 });
6344 try a.end(f, w);
6345 }
5505 // We have a `*[n]T`, which was turned into to a pointer to `struct { T array[n]; }`.
5506 // Ideally we would want to use 'operand->array' to convert to a `T *` (we get a `T []`
5507 // which decays to a pointer), but if the element type is zero-bit or the array length is
5508 // zero, there will not be an `array` member (the array type lowers to `void`). We cannot
5509 // check the type layout here because it may not be resolved, so in this instance, we must
5510 // use a pointer cast.
5511 try f.writeCValueMember(w, local, .{ .identifier = "ptr" });
5512 try w.writeAll(" = (");
5513 try f.dg.renderType(w, inst_ty.slicePtrFieldType(zcu));
5514 try w.writeByte(')');
5515 try f.writeCValue(w, operand, .other);
5516 try w.writeByte(';');
5517 try f.newline();
5518
5519 try f.writeCValueMember(w, local, .{ .identifier = "len" });
5520 try w.print(" = {f}", .{
5521 try f.fmtIntLiteralDec(try pt.intValue(.usize, array_ty.arrayLen(zcu))),
5522 });
5523 try w.writeByte(';');
5524 try f.newline();
63465525
63475526 return local;
63485527}
63495528
63505529fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
6351 const pt = f.object.dg.pt;
5530 const pt = f.dg.pt;
63525531 const zcu = pt.zcu;
63535532 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
63545533
......@@ -6358,7 +5537,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
63585537 try reap(f, inst, &.{ty_op.operand});
63595538 const operand_ty = f.typeOf(ty_op.operand);
63605539 const scalar_ty = operand_ty.scalarType(zcu);
6361 const target = &f.object.dg.mod.resolved_target.result;
5540 const target = &f.dg.mod.resolved_target.result;
63625541 const operation = if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isRuntimeFloat())
63635542 if (inst_scalar_ty.floatBits(target) < scalar_ty.floatBits(target)) "trunc" else "extend"
63645543 else if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat())
......@@ -6368,16 +5547,15 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
63685547 else
63695548 unreachable;
63705549
6371 const w = &f.object.code.writer;
5550 const w = &f.code.writer;
63725551 const local = try f.allocLocal(inst, inst_ty);
63735552 const v = try Vectorize.start(f, inst, w, operand_ty);
6374 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));
6375 try f.writeCValue(w, local, .Other);
5553 try f.writeCValue(w, local, .other);
63765554 try v.elem(f, w);
6377 try a.assign(f, w);
5555 try w.writeAll(" = ");
63785556 if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) {
63795557 try w.writeAll("zig_wrap_");
6380 try f.object.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty);
5558 try f.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty);
63815559 try w.writeByte('(');
63825560 }
63835561 try w.writeAll("zig_");
......@@ -6385,14 +5563,15 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
63855563 try w.writeAll(compilerRtAbbrev(scalar_ty, zcu, target));
63865564 try w.writeAll(compilerRtAbbrev(inst_scalar_ty, zcu, target));
63875565 try w.writeByte('(');
6388 try f.writeCValue(w, operand, .FunctionArgument);
5566 try f.writeCValue(w, operand, .other);
63895567 try v.elem(f, w);
63905568 try w.writeByte(')');
63915569 if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) {
6392 try f.object.dg.renderBuiltinInfo(w, inst_scalar_ty, .bits);
5570 try f.dg.renderBuiltinInfo(w, inst_scalar_ty, .bits);
63935571 try w.writeByte(')');
63945572 }
6395 try a.end(f, w);
5573 try w.writeByte(';');
5574 try f.newline();
63965575 try v.end(f, inst, w);
63975576
63985577 return local;
......@@ -6405,7 +5584,7 @@ fn airUnBuiltinCall(
64055584 operation: []const u8,
64065585 info: BuiltinInfo,
64075586) !CValue {
6408 const pt = f.object.dg.pt;
5587 const pt = f.dg.pt;
64095588 const zcu = pt.zcu;
64105589
64115590 const operand = try f.resolveInst(operand_ref);
......@@ -6415,30 +5594,32 @@ fn airUnBuiltinCall(
64155594 const operand_ty = f.typeOf(operand_ref);
64165595 const scalar_ty = operand_ty.scalarType(zcu);
64175596
6418 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
6419 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
5597 const ref_ret = lowersToBigInt(inst_scalar_ty, zcu);
5598 const ref_arg = lowersToBigInt(scalar_ty, zcu);
64205599
6421 const w = &f.object.code.writer;
5600 const w = &f.code.writer;
64225601 const local = try f.allocLocal(inst, inst_ty);
64235602 const v = try Vectorize.start(f, inst, w, operand_ty);
64245603 if (!ref_ret) {
6425 try f.writeCValue(w, local, .Other);
5604 try f.writeCValue(w, local, .other);
64265605 try v.elem(f, w);
64275606 try w.writeAll(" = ");
64285607 }
64295608 try w.print("zig_{s}_", .{operation});
6430 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
5609 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
64315610 try w.writeByte('(');
64325611 if (ref_ret) {
6433 try f.writeCValue(w, local, .FunctionArgument);
5612 try w.writeByte('&');
5613 try f.writeCValue(w, local, .other);
64345614 try v.elem(f, w);
64355615 try w.writeAll(", ");
64365616 }
6437 try f.writeCValue(w, operand, .FunctionArgument);
5617 if (ref_arg) try w.writeByte('&');
5618 try f.writeCValue(w, operand, .other);
64385619 try v.elem(f, w);
6439 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);
5620 try f.dg.renderBuiltinInfo(w, scalar_ty, info);
64405621 try w.writeAll(");");
6441 try f.object.newline();
5622 try f.newline();
64425623 try v.end(f, inst, w);
64435624
64445625 return local;
......@@ -6450,13 +5631,12 @@ fn airBinBuiltinCall(
64505631 operation: []const u8,
64515632 info: BuiltinInfo,
64525633) !CValue {
6453 const pt = f.object.dg.pt;
5634 const pt = f.dg.pt;
64545635 const zcu = pt.zcu;
64555636 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
64565637
64575638 const operand_ty = f.typeOf(bin_op.lhs);
6458 const operand_ctype = try f.ctypeFromType(operand_ty, .complete);
6459 const is_big = operand_ctype.info(&f.object.dg.ctype_pool) == .array;
5639 const is_big = lowersToBigInt(operand_ty, zcu);
64605640
64615641 const lhs = try f.resolveInst(bin_op.lhs);
64625642 const rhs = try f.resolveInst(bin_op.rhs);
......@@ -6466,32 +5646,35 @@ fn airBinBuiltinCall(
64665646 const inst_scalar_ty = inst_ty.scalarType(zcu);
64675647 const scalar_ty = operand_ty.scalarType(zcu);
64685648
6469 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
6470 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
5649 const ref_ret = lowersToBigInt(inst_scalar_ty, zcu);
5650 const ref_arg = lowersToBigInt(scalar_ty, zcu);
64715651
6472 const w = &f.object.code.writer;
5652 const w = &f.code.writer;
64735653 const local = try f.allocLocal(inst, inst_ty);
64745654 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
64755655 const v = try Vectorize.start(f, inst, w, operand_ty);
64765656 if (!ref_ret) {
6477 try f.writeCValue(w, local, .Other);
5657 try f.writeCValue(w, local, .other);
64785658 try v.elem(f, w);
64795659 try w.writeAll(" = ");
64805660 }
64815661 try w.print("zig_{s}_", .{operation});
6482 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
5662 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
64835663 try w.writeByte('(');
64845664 if (ref_ret) {
6485 try f.writeCValue(w, local, .FunctionArgument);
5665 try w.writeByte('&');
5666 try f.writeCValue(w, local, .other);
64865667 try v.elem(f, w);
64875668 try w.writeAll(", ");
64885669 }
6489 try f.writeCValue(w, lhs, .FunctionArgument);
5670 if (ref_arg) try w.writeByte('&');
5671 try f.writeCValue(w, lhs, .other);
64905672 try v.elem(f, w);
64915673 try w.writeAll(", ");
6492 try f.writeCValue(w, rhs, .FunctionArgument);
5674 if (ref_arg) try w.writeByte('&');
5675 try f.writeCValue(w, rhs, .other);
64935676 if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, w);
6494 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);
5677 try f.dg.renderBuiltinInfo(w, scalar_ty, info);
64955678 try w.writeAll(");\n");
64965679 try v.end(f, inst, w);
64975680
......@@ -6506,7 +5689,7 @@ fn airCmpBuiltinCall(
65065689 operation: enum { cmp, operator },
65075690 info: BuiltinInfo,
65085691) !CValue {
6509 const pt = f.object.dg.pt;
5692 const pt = f.dg.pt;
65105693 const zcu = pt.zcu;
65115694 const lhs = try f.resolveInst(data.lhs);
65125695 const rhs = try f.resolveInst(data.rhs);
......@@ -6517,14 +5700,14 @@ fn airCmpBuiltinCall(
65175700 const operand_ty = f.typeOf(data.lhs);
65185701 const scalar_ty = operand_ty.scalarType(zcu);
65195702
6520 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
6521 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
5703 const ref_ret = lowersToBigInt(inst_scalar_ty, zcu);
5704 const ref_arg = lowersToBigInt(scalar_ty, zcu);
65225705
6523 const w = &f.object.code.writer;
5706 const w = &f.code.writer;
65245707 const local = try f.allocLocal(inst, inst_ty);
65255708 const v = try Vectorize.start(f, inst, w, operand_ty);
65265709 if (!ref_ret) {
6527 try f.writeCValue(w, local, .Other);
5710 try f.writeCValue(w, local, .other);
65285711 try v.elem(f, w);
65295712 try w.writeAll(" = ");
65305713 }
......@@ -6532,33 +5715,36 @@ fn airCmpBuiltinCall(
65325715 else => @tagName(operation),
65335716 .operator => compareOperatorAbbrev(operator),
65345717 }});
6535 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
5718 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
65365719 try w.writeByte('(');
65375720 if (ref_ret) {
6538 try f.writeCValue(w, local, .FunctionArgument);
5721 try w.writeByte('&');
5722 try f.writeCValue(w, local, .other);
65395723 try v.elem(f, w);
65405724 try w.writeAll(", ");
65415725 }
6542 try f.writeCValue(w, lhs, .FunctionArgument);
5726 if (ref_arg) try w.writeByte('&');
5727 try f.writeCValue(w, lhs, .other);
65435728 try v.elem(f, w);
65445729 try w.writeAll(", ");
6545 try f.writeCValue(w, rhs, .FunctionArgument);
5730 if (ref_arg) try w.writeByte('&');
5731 try f.writeCValue(w, rhs, .other);
65465732 try v.elem(f, w);
6547 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);
5733 try f.dg.renderBuiltinInfo(w, scalar_ty, info);
65485734 try w.writeByte(')');
65495735 if (!ref_ret) try w.print("{s}{f}", .{
65505736 compareOperatorC(operator),
65515737 try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)),
65525738 });
65535739 try w.writeByte(';');
6554 try f.object.newline();
5740 try f.newline();
65555741 try v.end(f, inst, w);
65565742
65575743 return local;
65585744}
65595745
65605746fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
6561 const pt = f.object.dg.pt;
5747 const pt = f.dg.pt;
65625748 const zcu = pt.zcu;
65635749 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
65645750 const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
......@@ -6568,9 +5754,8 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
65685754 const new_value = try f.resolveInst(extra.new_value);
65695755 const ptr_ty = f.typeOf(extra.ptr);
65705756 const ty = ptr_ty.childType(zcu);
6571 const ctype = try f.ctypeFromType(ty, .complete);
65725757
6573 const w = &f.object.code.writer;
5758 const w = &f.code.writer;
65745759 const new_value_mat = try Materialize.start(f, inst, ty, new_value);
65755760 try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value });
65765761
......@@ -6581,13 +5766,11 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
65815766
65825767 const local = try f.allocLocal(inst, inst_ty);
65835768 if (inst_ty.isPtrLikeOptional(zcu)) {
6584 {
6585 const a = try Assignment.start(f, w, ctype);
6586 try f.writeCValue(w, local, .Other);
6587 try a.assign(f, w);
6588 try f.writeCValue(w, expected_value, .Other);
6589 try a.end(f, w);
6590 }
5769 try f.writeCValue(w, local, .other);
5770 try w.writeAll(" = ");
5771 try f.writeCValue(w, expected_value, .other);
5772 try w.writeByte(';');
5773 try f.newline();
65915774
65925775 try w.writeAll("if (");
65935776 try w.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor});
......@@ -6595,9 +5778,9 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
65955778 try w.writeByte(')');
65965779 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
65975780 try w.writeAll(" *)");
6598 try f.writeCValue(w, ptr, .Other);
5781 try f.writeCValue(w, ptr, .other);
65995782 try w.writeAll(", ");
6600 try f.writeCValue(w, local, .FunctionArgument);
5783 try f.writeCValue(w, local, .other);
66015784 try w.writeAll(", ");
66025785 try new_value_mat.mat(f, w);
66035786 try w.writeAll(", ");
......@@ -6605,56 +5788,49 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
66055788 try w.writeAll(", ");
66065789 try writeMemoryOrder(w, extra.failureOrder());
66075790 try w.writeAll(", ");
6608 try f.object.dg.renderTypeForBuiltinFnName(w, ty);
5791 try f.dg.renderTypeForBuiltinFnName(w, ty);
66095792 try w.writeAll(", ");
66105793 try f.renderType(w, repr_ty);
66115794 try w.writeByte(')');
66125795 try w.writeAll(") {");
6613 f.object.indent();
6614 try f.object.newline();
6615 {
6616 const a = try Assignment.start(f, w, ctype);
6617 try f.writeCValue(w, local, .Other);
6618 try a.assign(f, w);
6619 try w.writeAll("NULL");
6620 try a.end(f, w);
6621 }
6622 try f.object.outdent();
5796 f.indent();
5797 try f.newline();
5798
5799 try f.writeCValue(w, local, .other);
5800 try w.writeAll(" = NULL;");
5801 try f.newline();
5802
5803 try f.outdent();
66235804 try w.writeByte('}');
6624 try f.object.newline();
5805 try f.newline();
66255806 } else {
6626 {
6627 const a = try Assignment.start(f, w, ctype);
6628 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6629 try a.assign(f, w);
6630 try f.writeCValue(w, expected_value, .Other);
6631 try a.end(f, w);
6632 }
6633 {
6634 const a = try Assignment.start(f, w, .bool);
6635 try f.writeCValueMember(w, local, .{ .identifier = "is_null" });
6636 try a.assign(f, w);
6637 try w.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor});
6638 try f.renderType(w, ty);
6639 try w.writeByte(')');
6640 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
6641 try w.writeAll(" *)");
6642 try f.writeCValue(w, ptr, .Other);
6643 try w.writeAll(", ");
6644 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6645 try w.writeAll(", ");
6646 try new_value_mat.mat(f, w);
6647 try w.writeAll(", ");
6648 try writeMemoryOrder(w, extra.successOrder());
6649 try w.writeAll(", ");
6650 try writeMemoryOrder(w, extra.failureOrder());
6651 try w.writeAll(", ");
6652 try f.object.dg.renderTypeForBuiltinFnName(w, ty);
6653 try w.writeAll(", ");
6654 try f.renderType(w, repr_ty);
6655 try w.writeByte(')');
6656 try a.end(f, w);
6657 }
5807 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
5808 try w.writeAll(" = ");
5809 try f.writeCValue(w, expected_value, .other);
5810 try w.writeByte(';');
5811 try f.newline();
5812
5813 try f.writeCValueMember(w, local, .{ .identifier = "is_null" });
5814 try w.print(" = zig_cmpxchg_{s}((zig_atomic(", .{flavor});
5815 try f.renderType(w, ty);
5816 try w.writeByte(')');
5817 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
5818 try w.writeAll(" *)");
5819 try f.writeCValue(w, ptr, .other);
5820 try w.writeAll(", ");
5821 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
5822 try w.writeAll(", ");
5823 try new_value_mat.mat(f, w);
5824 try w.writeAll(", ");
5825 try writeMemoryOrder(w, extra.successOrder());
5826 try w.writeAll(", ");
5827 try writeMemoryOrder(w, extra.failureOrder());
5828 try w.writeAll(", ");
5829 try f.dg.renderTypeForBuiltinFnName(w, ty);
5830 try w.writeAll(", ");
5831 try f.renderType(w, repr_ty);
5832 try w.writeAll(");");
5833 try f.newline();
66585834 }
66595835 try new_value_mat.end(f, inst);
66605836
......@@ -6667,7 +5843,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
66675843}
66685844
66695845fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
6670 const pt = f.object.dg.pt;
5846 const pt = f.dg.pt;
66715847 const zcu = pt.zcu;
66725848 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
66735849 const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data;
......@@ -6677,7 +5853,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
66775853 const ptr = try f.resolveInst(pl_op.operand);
66785854 const operand = try f.resolveInst(extra.operand);
66795855
6680 const w = &f.object.code.writer;
5856 const w = &f.code.writer;
66815857 const operand_mat = try Materialize.start(f, inst, ty, operand);
66825858 try reap(f, inst, &.{ pl_op.operand, extra.operand });
66835859
......@@ -6690,7 +5866,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
66905866 try w.print("zig_atomicrmw_{s}", .{toAtomicRmwSuffix(extra.op())});
66915867 if (is_float) try w.writeAll("_float") else if (is_128) try w.writeAll("_int128");
66925868 try w.writeByte('(');
6693 try f.writeCValue(w, local, .Other);
5869 try f.writeCValue(w, local, .other);
66945870 try w.writeAll(", (");
66955871 const use_atomic = switch (extra.op()) {
66965872 else => true,
......@@ -6702,17 +5878,17 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
67025878 if (use_atomic) try w.writeByte(')');
67035879 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
67045880 try w.writeAll(" *)");
6705 try f.writeCValue(w, ptr, .Other);
5881 try f.writeCValue(w, ptr, .other);
67065882 try w.writeAll(", ");
67075883 try operand_mat.mat(f, w);
67085884 try w.writeAll(", ");
67095885 try writeMemoryOrder(w, extra.ordering());
67105886 try w.writeAll(", ");
6711 try f.object.dg.renderTypeForBuiltinFnName(w, ty);
5887 try f.dg.renderTypeForBuiltinFnName(w, ty);
67125888 try w.writeAll(", ");
67135889 try f.renderType(w, repr_ty);
67145890 try w.writeAll(");");
6715 try f.object.newline();
5891 try f.newline();
67165892 try operand_mat.end(f, inst);
67175893
67185894 if (f.liveness.isUnused(inst)) {
......@@ -6724,7 +5900,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
67245900}
67255901
67265902fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
6727 const pt = f.object.dg.pt;
5903 const pt = f.dg.pt;
67285904 const zcu = pt.zcu;
67295905 const atomic_load = f.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
67305906 const ptr = try f.resolveInst(atomic_load.ptr);
......@@ -6738,31 +5914,31 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
67385914 ty;
67395915
67405916 const inst_ty = f.typeOfIndex(inst);
6741 const w = &f.object.code.writer;
5917 const w = &f.code.writer;
67425918 const local = try f.allocLocal(inst, inst_ty);
67435919
67445920 try w.writeAll("zig_atomic_load(");
6745 try f.writeCValue(w, local, .Other);
5921 try f.writeCValue(w, local, .other);
67465922 try w.writeAll(", (zig_atomic(");
67475923 try f.renderType(w, ty);
67485924 try w.writeByte(')');
67495925 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
67505926 try w.writeAll(" *)");
6751 try f.writeCValue(w, ptr, .Other);
5927 try f.writeCValue(w, ptr, .other);
67525928 try w.writeAll(", ");
67535929 try writeMemoryOrder(w, atomic_load.order);
67545930 try w.writeAll(", ");
6755 try f.object.dg.renderTypeForBuiltinFnName(w, ty);
5931 try f.dg.renderTypeForBuiltinFnName(w, ty);
67565932 try w.writeAll(", ");
67575933 try f.renderType(w, repr_ty);
67585934 try w.writeAll(");");
6759 try f.object.newline();
5935 try f.newline();
67605936
67615937 return local;
67625938}
67635939
67645940fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CValue {
6765 const pt = f.object.dg.pt;
5941 const pt = f.dg.pt;
67665942 const zcu = pt.zcu;
67675943 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
67685944 const ptr_ty = f.typeOf(bin_op.lhs);
......@@ -6770,7 +5946,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
67705946 const ptr = try f.resolveInst(bin_op.lhs);
67715947 const element = try f.resolveInst(bin_op.rhs);
67725948
6773 const w = &f.object.code.writer;
5949 const w = &f.code.writer;
67745950 const element_mat = try Materialize.start(f, inst, ty, element);
67755951 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
67765952
......@@ -6784,32 +5960,22 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
67845960 try w.writeByte(')');
67855961 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
67865962 try w.writeAll(" *)");
6787 try f.writeCValue(w, ptr, .Other);
5963 try f.writeCValue(w, ptr, .other);
67885964 try w.writeAll(", ");
67895965 try element_mat.mat(f, w);
67905966 try w.print(", {s}, ", .{order});
6791 try f.object.dg.renderTypeForBuiltinFnName(w, ty);
5967 try f.dg.renderTypeForBuiltinFnName(w, ty);
67925968 try w.writeAll(", ");
67935969 try f.renderType(w, repr_ty);
67945970 try w.writeAll(");");
6795 try f.object.newline();
5971 try f.newline();
67965972 try element_mat.end(f, inst);
67975973
67985974 return .none;
67995975}
68005976
6801fn writeSliceOrPtr(f: *Function, w: *Writer, ptr: CValue, ptr_ty: Type) !void {
6802 const pt = f.object.dg.pt;
6803 const zcu = pt.zcu;
6804 if (ptr_ty.isSlice(zcu)) {
6805 try f.writeCValueMember(w, ptr, .{ .identifier = "ptr" });
6806 } else {
6807 try f.writeCValue(w, ptr, .FunctionArgument);
6808 }
6809}
6810
68115977fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
6812 const pt = f.object.dg.pt;
5978 const pt = f.dg.pt;
68135979 const zcu = pt.zcu;
68145980 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
68155981 const dest_ty = f.typeOf(bin_op.lhs);
......@@ -6818,7 +5984,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
68185984 const elem_ty = f.typeOf(bin_op.rhs);
68195985 const elem_abi_size = elem_ty.abiSize(zcu);
68205986 const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndef(zcu) else false;
6821 const w = &f.object.code.writer;
5987 const w = &f.code.writer;
68225988
68235989 if (val_is_undef) {
68245990 if (!safety) {
......@@ -6832,153 +5998,128 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
68325998 try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" });
68335999 try w.writeAll(", 0xaa, ");
68346000 try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" });
6835 if (elem_abi_size > 1) {
6836 try w.print(" * {d}", .{elem_abi_size});
6837 }
6838 try w.writeAll(");");
6839 try f.object.newline();
68406001 },
68416002 .one => {
6842 const array_ty = dest_ty.childType(zcu);
6843 const len = array_ty.arrayLen(zcu) * elem_abi_size;
6844
6845 try f.writeCValue(w, dest_slice, .FunctionArgument);
6846 try w.print(", 0xaa, {d});", .{len});
6847 try f.object.newline();
6003 try f.writeCValue(w, dest_slice, .other);
6004 try w.print(", 0xaa, {d}", .{dest_ty.childType(zcu).arrayLen(zcu)});
68486005 },
68496006 .many, .c => unreachable,
68506007 }
6008 if (elem_abi_size > 0) try w.print(" * {d}", .{elem_abi_size});
6009 try w.writeAll(");");
6010 try f.newline();
68516011 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
68526012 return .none;
68536013 }
68546014
6855 if (elem_abi_size > 1 or dest_ty.isVolatilePtr(zcu)) {
6856 // For the assignment in this loop, the array pointer needs to get
6857 // casted to a regular pointer, otherwise an error like this occurs:
6858 // error: array type 'uint32_t[20]' (aka 'unsigned int[20]') is not assignable
6859 const elem_ptr_ty = try pt.ptrType(.{
6860 .child = elem_ty.toIntern(),
6861 .flags = .{
6862 .size = .c,
6863 },
6864 });
6865
6866 const index = try f.allocLocal(inst, .usize);
6867
6868 try w.writeAll("for (");
6869 try f.writeCValue(w, index, .Other);
6870 try w.writeAll(" = ");
6871 try f.object.dg.renderValue(w, .zero_usize, .Other);
6872 try w.writeAll("; ");
6873 try f.writeCValue(w, index, .Other);
6874 try w.writeAll(" != ");
6015 if (elem_abi_size == 1 and !dest_ty.isVolatilePtr(zcu)) {
6016 const bitcasted = try bitcast(f, .u8, value, elem_ty);
6017 try w.writeAll("memset(");
68756018 switch (dest_ty.ptrSize(zcu)) {
68766019 .slice => {
6020 try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" });
6021 try w.writeAll(", ");
6022 try f.writeCValue(w, bitcasted, .other);
6023 try w.writeAll(", ");
68776024 try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" });
68786025 },
68796026 .one => {
6880 const array_ty = dest_ty.childType(zcu);
6881 try w.print("{d}", .{array_ty.arrayLen(zcu)});
6027 try f.writeCValue(w, dest_slice, .other);
6028 try w.writeAll(", ");
6029 try f.writeCValue(w, bitcasted, .other);
6030 try w.print(", {d}", .{dest_ty.childType(zcu).arrayLen(zcu)});
68826031 },
68836032 .many, .c => unreachable,
68846033 }
6885 try w.writeAll("; ++");
6886 try f.writeCValue(w, index, .Other);
6887 try w.writeAll(") ");
6888
6889 const a = try Assignment.start(f, w, try f.ctypeFromType(elem_ty, .complete));
6890 try w.writeAll("((");
6891 try f.renderType(w, elem_ptr_ty);
6892 try w.writeByte(')');
6893 try writeSliceOrPtr(f, w, dest_slice, dest_ty);
6894 try w.writeAll(")[");
6895 try f.writeCValue(w, index, .Other);
6896 try w.writeByte(']');
6897 try a.assign(f, w);
6898 try f.writeCValue(w, value, .Other);
6899 try a.end(f, w);
6900
6034 try w.writeAll(");");
6035 try f.newline();
6036 try f.freeCValue(inst, bitcasted);
69016037 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6902 try freeLocal(f, inst, index.new_local, null);
6903
69046038 return .none;
69056039 }
69066040
6907 const bitcasted = try bitcast(f, .u8, value, elem_ty);
6041 // Fallback path: use a `for` loop.
6042
6043 const index = try f.allocLocal(inst, .usize);
69086044
6909 try w.writeAll("memset(");
6045 try w.writeAll("for (");
6046 try f.writeCValue(w, index, .other);
6047 try w.writeAll(" = ");
6048 try f.dg.renderValue(w, .zero_usize, .other);
6049 try w.writeAll("; ");
6050 try f.writeCValue(w, index, .other);
6051 try w.writeAll(" != ");
69106052 switch (dest_ty.ptrSize(zcu)) {
6911 .slice => {
6912 try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" });
6913 try w.writeAll(", ");
6914 try f.writeCValue(w, bitcasted, .FunctionArgument);
6915 try w.writeAll(", ");
6916 try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" });
6917 try w.writeAll(");");
6918 try f.object.newline();
6919 },
6920 .one => {
6921 const array_ty = dest_ty.childType(zcu);
6922 const len = array_ty.arrayLen(zcu) * elem_abi_size;
6053 .slice => try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" }),
6054 .one => try w.print("{d}", .{dest_ty.childType(zcu).arrayLen(zcu)}),
6055 .many, .c => unreachable,
6056 }
6057 try w.writeAll("; ++");
6058 try f.writeCValue(w, index, .other);
6059 try w.writeAll(") ");
69236060
6924 try f.writeCValue(w, dest_slice, .FunctionArgument);
6925 try w.writeAll(", ");
6926 try f.writeCValue(w, bitcasted, .FunctionArgument);
6927 try w.print(", {d});", .{len});
6928 try f.object.newline();
6929 },
6061 switch (dest_ty.ptrSize(zcu)) {
6062 .slice => try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" }),
6063 .one => try f.writeCValueDerefMember(w, dest_slice, .{ .identifier = "array" }),
69306064 .many, .c => unreachable,
69316065 }
6932 try f.freeCValue(inst, bitcasted);
6066 try w.writeByte('[');
6067 try f.writeCValue(w, index, .other);
6068 try w.writeAll("] = ");
6069 try f.writeCValue(w, value, .other);
6070 try w.writeByte(';');
6071 try f.newline();
6072
69336073 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6074 try freeLocal(f, inst, index.new_local, null);
6075
69346076 return .none;
69356077}
69366078
69376079fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CValue {
6938 const pt = f.object.dg.pt;
6080 const pt = f.dg.pt;
69396081 const zcu = pt.zcu;
69406082 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
69416083 const dest_ptr = try f.resolveInst(bin_op.lhs);
69426084 const src_ptr = try f.resolveInst(bin_op.rhs);
69436085 const dest_ty = f.typeOf(bin_op.lhs);
69446086 const src_ty = f.typeOf(bin_op.rhs);
6945 const w = &f.object.code.writer;
6087 const w = &f.code.writer;
69466088
69476089 if (dest_ty.ptrSize(zcu) != .one) {
69486090 try w.writeAll("if (");
6949 try writeArrayLen(f, dest_ptr, dest_ty);
6091 try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" });
69506092 try w.writeAll(" != 0) ");
69516093 }
69526094 try w.writeAll(function_paren);
6953 try writeSliceOrPtr(f, w, dest_ptr, dest_ty);
6095 switch (dest_ty.ptrSize(zcu)) {
6096 .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "ptr" }),
6097 .one => try f.writeCValueDerefMember(w, dest_ptr, .{ .identifier = "array" }),
6098 .many, .c => unreachable,
6099 }
69546100 try w.writeAll(", ");
6955 try writeSliceOrPtr(f, w, src_ptr, src_ty);
6101 switch (src_ty.ptrSize(zcu)) {
6102 .slice => try f.writeCValueMember(w, src_ptr, .{ .identifier = "ptr" }),
6103 .one => try f.writeCValueDerefMember(w, src_ptr, .{ .identifier = "array" }),
6104 .many, .c => try f.writeCValue(w, src_ptr, .other),
6105 }
69566106 try w.writeAll(", ");
6957 try writeArrayLen(f, dest_ptr, dest_ty);
6107 switch (dest_ty.ptrSize(zcu)) {
6108 .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }),
6109 .one => try w.print("{d}", .{dest_ty.childType(zcu).arrayLen(zcu)}),
6110 .many, .c => unreachable,
6111 }
69586112 try w.writeAll(" * sizeof(");
69596113 try f.renderType(w, dest_ty.indexableElem(zcu));
69606114 try w.writeAll("));");
6961 try f.object.newline();
6115 try f.newline();
69626116
69636117 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
69646118 return .none;
69656119}
69666120
6967fn writeArrayLen(f: *Function, dest_ptr: CValue, dest_ty: Type) !void {
6968 const pt = f.object.dg.pt;
6969 const zcu = pt.zcu;
6970 const w = &f.object.code.writer;
6971 switch (dest_ty.ptrSize(zcu)) {
6972 .one => try w.print("{f}", .{
6973 try f.fmtIntLiteralDec(try pt.intValue(.usize, dest_ty.childType(zcu).arrayLen(zcu))),
6974 }),
6975 .many, .c => unreachable,
6976 .slice => try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" }),
6977 }
6978}
6979
69806121fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
6981 const pt = f.object.dg.pt;
6122 const pt = f.dg.pt;
69826123 const zcu = pt.zcu;
69836124 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
69846125 const union_ptr = try f.resolveInst(bin_op.lhs);
......@@ -6988,19 +6129,18 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
69886129 const union_ty = f.typeOf(bin_op.lhs).childType(zcu);
69896130 const layout = union_ty.unionGetLayout(zcu);
69906131 if (layout.tag_size == 0) return .none;
6991 const tag_ty = union_ty.unionTagTypeRuntime(zcu).?;
69926132
6993 const w = &f.object.code.writer;
6994 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));
6133 const w = &f.code.writer;
69956134 try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" });
6996 try a.assign(f, w);
6997 try f.writeCValue(w, new_tag, .Other);
6998 try a.end(f, w);
6135 try w.writeAll(" = ");
6136 try f.writeCValue(w, new_tag, .other);
6137 try w.writeByte(';');
6138 try f.newline();
69996139 return .none;
70006140}
70016141
70026142fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
7003 const pt = f.object.dg.pt;
6143 const pt = f.dg.pt;
70046144 const zcu = pt.zcu;
70056145 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
70066146
......@@ -7012,17 +6152,20 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
70126152 if (layout.tag_size == 0) return .none;
70136153
70146154 const inst_ty = f.typeOfIndex(inst);
7015 const w = &f.object.code.writer;
6155 const w = &f.code.writer;
70166156 const local = try f.allocLocal(inst, inst_ty);
7017 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));
7018 try f.writeCValue(w, local, .Other);
7019 try a.assign(f, w);
6157 try f.writeCValue(w, local, .other);
6158 try w.writeAll(" = ");
70206159 try f.writeCValueMember(w, operand, .{ .identifier = "tag" });
7021 try a.end(f, w);
6160 try w.writeByte(';');
6161 try f.newline();
70226162 return local;
70236163}
70246164
70256165fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
6166 const zcu = f.dg.pt.zcu;
6167 const ip = &zcu.intern_pool;
6168 const gpa = zcu.comp.gpa;
70266169 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
70276170
70286171 const inst_ty = f.typeOfIndex(inst);
......@@ -7030,15 +6173,17 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
70306173 const operand = try f.resolveInst(un_op);
70316174 try reap(f, inst, &.{un_op});
70326175
7033 const w = &f.object.code.writer;
6176 const w = &f.code.writer;
70346177 const local = try f.allocLocal(inst, inst_ty);
7035 try f.writeCValue(w, local, .Other);
7036 try w.print(" = {s}(", .{
7037 try f.getLazyFnName(.{ .tag_name = enum_ty.toIntern() }),
6178 try f.writeCValue(w, local, .other);
6179 try f.need_tag_name_funcs.put(gpa, enum_ty.toIntern(), {});
6180 try w.print(" = zig_tagName_{f}__{d}(", .{
6181 fmtIdentUnsolo(enum_ty.containerTypeName(ip).toSlice(ip)),
6182 @intFromEnum(enum_ty.toIntern()),
70386183 });
7039 try f.writeCValue(w, operand, .Other);
6184 try f.writeCValue(w, operand, .other);
70406185 try w.writeAll(");");
7041 try f.object.newline();
6186 try f.newline();
70426187
70436188 return local;
70446189}
......@@ -7046,40 +6191,37 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
70466191fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
70476192 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
70486193
7049 const w = &f.object.code.writer;
6194 const w = &f.code.writer;
70506195 const inst_ty = f.typeOfIndex(inst);
70516196 const operand = try f.resolveInst(un_op);
70526197 try reap(f, inst, &.{un_op});
70536198 const local = try f.allocLocal(inst, inst_ty);
7054 try f.writeCValue(w, local, .Other);
6199 try f.writeCValue(w, local, .other);
70556200
70566201 try w.writeAll(" = zig_errorName[");
7057 try f.writeCValue(w, operand, .Other);
6202 try f.writeCValue(w, operand, .other);
70586203 try w.writeAll(" - 1];");
7059 try f.object.newline();
6204 try f.newline();
70606205 return local;
70616206}
70626207
70636208fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
7064 const pt = f.object.dg.pt;
7065 const zcu = pt.zcu;
70666209 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
70676210
70686211 const operand = try f.resolveInst(ty_op.operand);
70696212 try reap(f, inst, &.{ty_op.operand});
70706213
70716214 const inst_ty = f.typeOfIndex(inst);
7072 const inst_scalar_ty = inst_ty.scalarType(zcu);
70736215
7074 const w = &f.object.code.writer;
6216 const w = &f.code.writer;
70756217 const local = try f.allocLocal(inst, inst_ty);
70766218 const v = try Vectorize.start(f, inst, w, inst_ty);
7077 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_scalar_ty, .complete));
7078 try f.writeCValue(w, local, .Other);
6219 try f.writeCValue(w, local, .other);
70796220 try v.elem(f, w);
7080 try a.assign(f, w);
7081 try f.writeCValue(w, operand, .Other);
7082 try a.end(f, w);
6221 try w.writeAll(" = ");
6222 try f.writeCValue(w, operand, .other);
6223 try w.writeByte(';');
6224 try f.newline();
70836225 try v.end(f, inst, w);
70846226
70856227 return local;
......@@ -7096,29 +6238,29 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
70966238
70976239 const inst_ty = f.typeOfIndex(inst);
70986240
7099 const w = &f.object.code.writer;
6241 const w = &f.code.writer;
71006242 const local = try f.allocLocal(inst, inst_ty);
71016243 const v = try Vectorize.start(f, inst, w, inst_ty);
7102 try f.writeCValue(w, local, .Other);
6244 try f.writeCValue(w, local, .other);
71036245 try v.elem(f, w);
71046246 try w.writeAll(" = ");
7105 try f.writeCValue(w, pred, .Other);
6247 try f.writeCValue(w, pred, .other);
71066248 try v.elem(f, w);
71076249 try w.writeAll(" ? ");
7108 try f.writeCValue(w, lhs, .Other);
6250 try f.writeCValue(w, lhs, .other);
71096251 try v.elem(f, w);
71106252 try w.writeAll(" : ");
7111 try f.writeCValue(w, rhs, .Other);
6253 try f.writeCValue(w, rhs, .other);
71126254 try v.elem(f, w);
71136255 try w.writeByte(';');
7114 try f.object.newline();
6256 try f.newline();
71156257 try v.end(f, inst, w);
71166258
71176259 return local;
71186260}
71196261
71206262fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
7121 const pt = f.object.dg.pt;
6263 const pt = f.dg.pt;
71226264 const zcu = pt.zcu;
71236265
71246266 const unwrapped = f.air.unwrapShuffleOne(zcu, inst);
......@@ -7126,22 +6268,22 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
71266268 const operand = try f.resolveInst(unwrapped.operand);
71276269 const inst_ty = unwrapped.result_ty;
71286270
7129 const w = &f.object.code.writer;
6271 const w = &f.code.writer;
71306272 const local = try f.allocLocal(inst, inst_ty);
71316273 try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand
71326274 for (mask, 0..) |mask_elem, out_idx| {
7133 try f.writeCValue(w, local, .Other);
6275 try f.writeCValueMember(w, local, .{ .identifier = "array" });
71346276 try w.writeByte('[');
7135 try f.object.dg.renderValue(w, try pt.intValue(.usize, out_idx), .Other);
6277 try f.dg.renderValue(w, try pt.intValue(.usize, out_idx), .other);
71366278 try w.writeAll("] = ");
71376279 switch (mask_elem.unwrap()) {
71386280 .elem => |src_idx| {
7139 try f.writeCValue(w, operand, .Other);
6281 try f.writeCValueMember(w, operand, .{ .identifier = "array" });
71406282 try w.writeByte('[');
7141 try f.object.dg.renderValue(w, try pt.intValue(.usize, src_idx), .Other);
6283 try f.dg.renderValue(w, try pt.intValue(.usize, src_idx), .other);
71426284 try w.writeByte(']');
71436285 },
7144 .value => |val| try f.object.dg.renderValue(w, .fromInterned(val), .Other),
6286 .value => |val| try f.dg.renderValue(w, .fromInterned(val), .other),
71456287 }
71466288 try w.writeAll(";\n");
71476289 }
......@@ -7150,7 +6292,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
71506292}
71516293
71526294fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {
7153 const pt = f.object.dg.pt;
6295 const pt = f.dg.pt;
71546296 const zcu = pt.zcu;
71556297
71566298 const unwrapped = f.air.unwrapShuffleTwo(zcu, inst);
......@@ -7160,38 +6302,38 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {
71606302 const inst_ty = unwrapped.result_ty;
71616303 const elem_ty = inst_ty.childType(zcu);
71626304
7163 const w = &f.object.code.writer;
6305 const w = &f.code.writer;
71646306 const local = try f.allocLocal(inst, inst_ty);
71656307 try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands
71666308 for (mask, 0..) |mask_elem, out_idx| {
7167 try f.writeCValue(w, local, .Other);
6309 try f.writeCValueMember(w, local, .{ .identifier = "array" });
71686310 try w.writeByte('[');
7169 try f.object.dg.renderValue(w, try pt.intValue(.usize, out_idx), .Other);
6311 try f.dg.renderValue(w, try pt.intValue(.usize, out_idx), .other);
71706312 try w.writeAll("] = ");
71716313 switch (mask_elem.unwrap()) {
71726314 .a_elem => |src_idx| {
7173 try f.writeCValue(w, operand_a, .Other);
6315 try f.writeCValueMember(w, operand_a, .{ .identifier = "array" });
71746316 try w.writeByte('[');
7175 try f.object.dg.renderValue(w, try pt.intValue(.usize, src_idx), .Other);
6317 try f.dg.renderValue(w, try pt.intValue(.usize, src_idx), .other);
71766318 try w.writeByte(']');
71776319 },
71786320 .b_elem => |src_idx| {
7179 try f.writeCValue(w, operand_b, .Other);
6321 try f.writeCValueMember(w, operand_b, .{ .identifier = "array" });
71806322 try w.writeByte('[');
7181 try f.object.dg.renderValue(w, try pt.intValue(.usize, src_idx), .Other);
6323 try f.dg.renderValue(w, try pt.intValue(.usize, src_idx), .other);
71826324 try w.writeByte(']');
71836325 },
7184 .undef => try f.object.dg.renderUndefValue(w, elem_ty, .Other),
6326 .undef => try f.dg.renderUndefValue(w, elem_ty, .other),
71856327 }
71866328 try w.writeByte(';');
7187 try f.object.newline();
6329 try f.newline();
71886330 }
71896331
71906332 return local;
71916333}
71926334
71936335fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
7194 const pt = f.object.dg.pt;
6336 const pt = f.dg.pt;
71956337 const zcu = pt.zcu;
71966338 const reduce = f.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
71976339
......@@ -7199,7 +6341,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
71996341 const operand = try f.resolveInst(reduce.operand);
72006342 try reap(f, inst, &.{reduce.operand});
72016343 const operand_ty = f.typeOf(reduce.operand);
7202 const w = &f.object.code.writer;
6344 const w = &f.code.writer;
72036345
72046346 const use_operator = scalar_ty.bitSize(zcu) <= 64;
72056347 const op: union(enum) {
......@@ -7246,10 +6388,10 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
72466388 // }
72476389
72486390 const accum = try f.allocLocal(inst, scalar_ty);
7249 try f.writeCValue(w, accum, .Other);
6391 try f.writeCValue(w, accum, .other);
72506392 try w.writeAll(" = ");
72516393
7252 try f.object.dg.renderValue(w, switch (reduce.operation) {
6394 try f.dg.renderValue(w, switch (reduce.operation) {
72536395 .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) {
72546396 .bool => Value.false,
72556397 .int => try pt.intValue(scalar_ty, 0),
......@@ -7285,58 +6427,58 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
72856427 .float => try pt.floatValue(scalar_ty, std.math.nan(f128)),
72866428 else => unreachable,
72876429 },
7288 }, .Other);
6430 }, .other);
72896431 try w.writeByte(';');
7290 try f.object.newline();
6432 try f.newline();
72916433
72926434 const v = try Vectorize.start(f, inst, w, operand_ty);
7293 try f.writeCValue(w, accum, .Other);
6435 try f.writeCValue(w, accum, .other);
72946436 switch (op) {
72956437 .builtin => |func| {
72966438 try w.print(" = zig_{s}_", .{func.operation});
7297 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
6439 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
72986440 try w.writeByte('(');
7299 try f.writeCValue(w, accum, .FunctionArgument);
6441 try f.writeCValue(w, accum, .other);
73006442 try w.writeAll(", ");
7301 try f.writeCValue(w, operand, .Other);
6443 try f.writeCValue(w, operand, .other);
73026444 try v.elem(f, w);
7303 try f.object.dg.renderBuiltinInfo(w, scalar_ty, func.info);
6445 try f.dg.renderBuiltinInfo(w, scalar_ty, func.info);
73046446 try w.writeByte(')');
73056447 },
73066448 .infix => |ass| {
73076449 try w.writeAll(ass);
7308 try f.writeCValue(w, operand, .Other);
6450 try f.writeCValue(w, operand, .other);
73096451 try v.elem(f, w);
73106452 },
73116453 .ternary => |cmp| {
73126454 try w.writeAll(" = ");
7313 try f.writeCValue(w, accum, .Other);
6455 try f.writeCValue(w, accum, .other);
73146456 try w.writeAll(cmp);
7315 try f.writeCValue(w, operand, .Other);
6457 try f.writeCValue(w, operand, .other);
73166458 try v.elem(f, w);
73176459 try w.writeAll(" ? ");
7318 try f.writeCValue(w, accum, .Other);
6460 try f.writeCValue(w, accum, .other);
73196461 try w.writeAll(" : ");
7320 try f.writeCValue(w, operand, .Other);
6462 try f.writeCValue(w, operand, .other);
73216463 try v.elem(f, w);
73226464 },
73236465 }
73246466 try w.writeByte(';');
7325 try f.object.newline();
6467 try f.newline();
73266468 try v.end(f, inst, w);
73276469
73286470 return accum;
73296471}
73306472
73316473fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
7332 const pt = f.object.dg.pt;
6474 const pt = f.dg.pt;
73336475 const zcu = pt.zcu;
73346476 const ip = &zcu.intern_pool;
73356477 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
73366478 const inst_ty = f.typeOfIndex(inst);
73376479 const len: usize = @intCast(inst_ty.arrayLen(zcu));
73386480 const elements: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[ty_pl.payload..][0..len]);
7339 const gpa = f.object.dg.gpa;
6481 const gpa = f.dg.gpa;
73406482 const resolved_elements = try gpa.alloc(CValue, elements.len);
73416483 defer gpa.free(resolved_elements);
73426484 for (resolved_elements, elements) |*resolved_element, element| {
......@@ -7349,28 +6491,23 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
73496491 }
73506492 }
73516493
7352 const w = &f.object.code.writer;
6494 const w = &f.code.writer;
73536495 const local = try f.allocLocal(inst, inst_ty);
73546496 switch (ip.indexToKey(inst_ty.toIntern())) {
73556497 inline .array_type, .vector_type => |info, tag| {
7356 const a: Assignment = .{
7357 .ctype = try f.ctypeFromType(.fromInterned(info.child), .complete),
7358 };
73596498 for (resolved_elements, 0..) |element, i| {
7360 try a.restart(f, w);
7361 try f.writeCValue(w, local, .Other);
7362 try w.print("[{d}]", .{i});
7363 try a.assign(f, w);
7364 try f.writeCValue(w, element, .Other);
7365 try a.end(f, w);
6499 try f.writeCValueMember(w, local, .{ .identifier = "array" });
6500 try w.print("[{d}] = ", .{i});
6501 try f.writeCValue(w, element, .other);
6502 try w.writeByte(';');
6503 try f.newline();
73666504 }
73676505 if (tag == .array_type and info.sentinel != .none) {
7368 try a.restart(f, w);
7369 try f.writeCValue(w, local, .Other);
7370 try w.print("[{d}]", .{info.len});
7371 try a.assign(f, w);
7372 try f.object.dg.renderValue(w, Value.fromInterned(info.sentinel), .Other);
7373 try a.end(f, w);
6506 try f.writeCValueMember(w, local, .{ .identifier = "array" });
6507 try w.print("[{d}] = ", .{info.len});
6508 try f.dg.renderValue(w, Value.fromInterned(info.sentinel), .other);
6509 try w.writeByte(';');
6510 try f.newline();
73746511 }
73756512 },
73766513 .struct_type => {
......@@ -7382,11 +6519,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
73826519 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
73836520 if (!field_ty.hasRuntimeBits(zcu)) continue;
73846521
7385 const a = try Assignment.start(f, w, try f.ctypeFromType(field_ty, .complete));
73866522 try f.writeCValueMember(w, local, .{ .identifier = loaded_struct.field_names.get(ip)[field_index].toSlice(ip) });
7387 try a.assign(f, w);
7388 try f.writeCValue(w, resolved_elements[field_index], .Other);
7389 try a.end(f, w);
6523 try w.writeAll(" = ");
6524 try f.writeCValue(w, resolved_elements[field_index], .other);
6525 try w.writeByte(';');
6526 try f.newline();
73906527 }
73916528 },
73926529 .@"packed" => unreachable, // `Air.Legalize.Feature.expand_packed_struct_init` handles this case
......@@ -7397,11 +6534,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
73976534 const field_ty: Type = .fromInterned(tuple_info.types.get(ip)[field_index]);
73986535 if (!field_ty.hasRuntimeBits(zcu)) continue;
73996536
7400 const a = try Assignment.start(f, w, try f.ctypeFromType(field_ty, .complete));
74016537 try f.writeCValueMember(w, local, .{ .field = field_index });
7402 try a.assign(f, w);
7403 try f.writeCValue(w, resolved_elements[field_index], .Other);
7404 try a.end(f, w);
6538 try w.writeAll(" = ");
6539 try f.writeCValue(w, resolved_elements[field_index], .other);
6540 try w.writeByte(';');
6541 try f.newline();
74056542 },
74066543 else => unreachable,
74076544 }
......@@ -7410,46 +6547,52 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
74106547}
74116548
74126549fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
7413 const pt = f.object.dg.pt;
6550 const pt = f.dg.pt;
74146551 const zcu = pt.zcu;
74156552 const ip = &zcu.intern_pool;
74166553 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
74176554 const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data;
6555 const field_index = extra.field_index;
74186556
74196557 const union_ty = f.typeOfIndex(inst);
74206558 const loaded_union = ip.loadUnionType(union_ty.toIntern());
7421 const field_name = ip.loadEnumType(loaded_union.enum_tag_type).field_names.get(ip)[extra.field_index];
7422 const payload_ty = f.typeOf(extra.init);
6559 const loaded_enum = ip.loadEnumType(loaded_union.enum_tag_type);
6560
74236561 const payload = try f.resolveInst(extra.init);
74246562 try reap(f, inst, &.{extra.init});
74256563
7426 const w = &f.object.code.writer;
6564 const w = &f.code.writer;
74276565 if (loaded_union.layout == .@"packed") return f.moveCValue(inst, union_ty, payload);
74286566
74296567 const local = try f.allocLocal(inst, union_ty);
74306568
7431 const field: CValue = if (union_ty.unionTagTypeRuntime(zcu)) |tag_ty| field: {
7432 assert(union_ty.unionGetLayout(zcu).tag_size != 0);
7433 const field_index = tag_ty.enumFieldIndex(field_name, zcu).?;
7434 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
7435 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));
6569 if (loaded_union.has_runtime_tag) {
74366570 try f.writeCValueMember(w, local, .{ .identifier = "tag" });
7437 try a.assign(f, w);
7438 try w.print("{f}", .{try f.fmtIntLiteralDec(tag_val.intFromEnum(zcu))});
7439 try a.end(f, w);
7440 break :field .{ .payload_identifier = field_name.toSlice(ip) };
7441 } else .{ .identifier = field_name.toSlice(ip) };
7442
7443 const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete));
7444 try f.writeCValueMember(w, local, field);
7445 try a.assign(f, w);
7446 try f.writeCValue(w, payload, .Other);
7447 try a.end(f, w);
6571 if (loaded_enum.field_values.len == 0) {
6572 // auto-numbered
6573 try w.print(" = {d};", .{field_index});
6574 } else {
6575 const tag_int_val: Value = .fromInterned(loaded_enum.field_values.get(ip)[field_index]);
6576 try w.print(" = {f};", .{try f.fmtIntLiteralDec(tag_int_val)});
6577 }
6578 try f.newline();
6579 }
6580
6581 const field_name_slice = loaded_enum.field_names.get(ip)[field_index].toSlice(ip);
6582 switch (loaded_union.layout) {
6583 .auto => try f.writeCValueMember(w, local, .{ .payload_identifier = field_name_slice }),
6584 .@"extern" => try f.writeCValueMember(w, local, .{ .identifier = field_name_slice }),
6585 .@"packed" => unreachable,
6586 }
6587 try w.writeAll(" = ");
6588 try f.writeCValue(w, payload, .other);
6589 try w.writeByte(';');
6590 try f.newline();
74486591 return local;
74496592}
74506593
74516594fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
7452 const pt = f.object.dg.pt;
6595 const pt = f.dg.pt;
74536596 const zcu = pt.zcu;
74546597 const prefetch = f.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
74556598
......@@ -7457,16 +6600,16 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
74576600 const ptr = try f.resolveInst(prefetch.ptr);
74586601 try reap(f, inst, &.{prefetch.ptr});
74596602
7460 const w = &f.object.code.writer;
6603 const w = &f.code.writer;
74616604 switch (prefetch.cache) {
74626605 .data => {
74636606 try w.writeAll("zig_prefetch(");
74646607 if (ptr_ty.isSlice(zcu))
74656608 try f.writeCValueMember(w, ptr, .{ .identifier = "ptr" })
74666609 else
7467 try f.writeCValue(w, ptr, .FunctionArgument);
6610 try f.writeCValue(w, ptr, .other);
74686611 try w.print(", {d}, {d});", .{ @intFromEnum(prefetch.rw), prefetch.locality });
7469 try f.object.newline();
6612 try f.newline();
74706613 },
74716614 // The available prefetch intrinsics do not accept a cache argument; only
74726615 // address, rw, and locality.
......@@ -7479,14 +6622,14 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
74796622fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
74806623 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
74816624
7482 const w = &f.object.code.writer;
6625 const w = &f.code.writer;
74836626 const inst_ty = f.typeOfIndex(inst);
74846627 const local = try f.allocLocal(inst, inst_ty);
7485 try f.writeCValue(w, local, .Other);
6628 try f.writeCValue(w, local, .other);
74866629
74876630 try w.writeAll(" = ");
74886631 try w.print("zig_wasm_memory_size({d});", .{pl_op.payload});
7489 try f.object.newline();
6632 try f.newline();
74906633
74916634 return local;
74926635}
......@@ -7494,23 +6637,23 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
74946637fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
74956638 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
74966639
7497 const w = &f.object.code.writer;
6640 const w = &f.code.writer;
74986641 const inst_ty = f.typeOfIndex(inst);
74996642 const operand = try f.resolveInst(pl_op.operand);
75006643 try reap(f, inst, &.{pl_op.operand});
75016644 const local = try f.allocLocal(inst, inst_ty);
7502 try f.writeCValue(w, local, .Other);
6645 try f.writeCValue(w, local, .other);
75036646
75046647 try w.writeAll(" = ");
75056648 try w.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload});
7506 try f.writeCValue(w, operand, .FunctionArgument);
6649 try f.writeCValue(w, operand, .other);
75076650 try w.writeAll(");");
7508 try f.object.newline();
6651 try f.newline();
75096652 return local;
75106653}
75116654
75126655fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
7513 const pt = f.object.dg.pt;
6656 const pt = f.dg.pt;
75146657 const zcu = pt.zcu;
75156658 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
75166659 const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data;
......@@ -7523,24 +6666,24 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
75236666 const inst_ty = f.typeOfIndex(inst);
75246667 const inst_scalar_ty = inst_ty.scalarType(zcu);
75256668
7526 const w = &f.object.code.writer;
6669 const w = &f.code.writer;
75276670 const local = try f.allocLocal(inst, inst_ty);
75286671 const v = try Vectorize.start(f, inst, w, inst_ty);
7529 try f.writeCValue(w, local, .Other);
6672 try f.writeCValue(w, local, .other);
75306673 try v.elem(f, w);
75316674 try w.writeAll(" = zig_fma_");
7532 try f.object.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty);
6675 try f.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty);
75336676 try w.writeByte('(');
7534 try f.writeCValue(w, mulend1, .FunctionArgument);
6677 try f.writeCValue(w, mulend1, .other);
75356678 try v.elem(f, w);
75366679 try w.writeAll(", ");
7537 try f.writeCValue(w, mulend2, .FunctionArgument);
6680 try f.writeCValue(w, mulend2, .other);
75386681 try v.elem(f, w);
75396682 try w.writeAll(", ");
7540 try f.writeCValue(w, addend, .FunctionArgument);
6683 try f.writeCValue(w, addend, .other);
75416684 try v.elem(f, w);
75426685 try w.writeAll(");");
7543 try f.object.newline();
6686 try f.newline();
75446687 try v.end(f, inst, w);
75456688
75466689 return local;
......@@ -7548,34 +6691,33 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
75486691
75496692fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue {
75506693 const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
7551 const w = &f.object.code.writer;
6694 const w = &f.code.writer;
75526695 const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty));
7553 try f.writeCValue(w, local, .Other);
6696 try f.writeCValue(w, local, .other);
75546697 try w.writeAll(" = ");
7555 try f.object.dg.renderNav(w, ty_nav.nav, .Other);
6698 try f.dg.renderNav(w, ty_nav.nav, .other);
75566699 try w.writeByte(';');
7557 try f.object.newline();
6700 try f.newline();
75586701 return local;
75596702}
75606703
75616704fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
7562 const pt = f.object.dg.pt;
6705 const pt = f.dg.pt;
75636706 const zcu = pt.zcu;
75646707 const inst_ty = f.typeOfIndex(inst);
7565 const function_ty = zcu.navValue(f.object.dg.pass.nav).typeOf(zcu);
7566 const function_info = (try f.ctypeFromType(function_ty, .complete)).info(&f.object.dg.ctype_pool).function;
7567 assert(function_info.varargs);
75686708
7569 const w = &f.object.code.writer;
6709 assert(Value.fromInterned(f.func_index).typeOf(zcu).fnIsVarArgs(zcu));
6710
6711 const w = &f.code.writer;
75706712 const local = try f.allocLocal(inst, inst_ty);
75716713 try w.writeAll("va_start(*(va_list *)&");
7572 try f.writeCValue(w, local, .Other);
7573 if (function_info.param_ctypes.len > 0) {
6714 try f.writeCValue(w, local, .other);
6715 if (f.next_arg_index > 0) {
75746716 try w.writeAll(", ");
7575 try f.writeCValue(w, .{ .arg = function_info.param_ctypes.len - 1 }, .FunctionArgument);
6717 try f.writeCValue(w, .{ .arg = f.next_arg_index - 1 }, .other);
75766718 }
75776719 try w.writeAll(");");
7578 try f.object.newline();
6720 try f.newline();
75796721 return local;
75806722}
75816723
......@@ -7586,15 +6728,15 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {
75866728 const va_list = try f.resolveInst(ty_op.operand);
75876729 try reap(f, inst, &.{ty_op.operand});
75886730
7589 const w = &f.object.code.writer;
6731 const w = &f.code.writer;
75906732 const local = try f.allocLocal(inst, inst_ty);
7591 try f.writeCValue(w, local, .Other);
6733 try f.writeCValue(w, local, .other);
75926734 try w.writeAll(" = va_arg(*(va_list *)");
7593 try f.writeCValue(w, va_list, .Other);
6735 try f.writeCValue(w, va_list, .other);
75946736 try w.writeAll(", ");
75956737 try f.renderType(w, ty_op.ty.toType());
75966738 try w.writeAll(");");
7597 try f.object.newline();
6739 try f.newline();
75986740 return local;
75996741}
76006742
......@@ -7604,11 +6746,11 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {
76046746 const va_list = try f.resolveInst(un_op);
76056747 try reap(f, inst, &.{un_op});
76066748
7607 const w = &f.object.code.writer;
6749 const w = &f.code.writer;
76086750 try w.writeAll("va_end(*(va_list *)");
7609 try f.writeCValue(w, va_list, .Other);
6751 try f.writeCValue(w, va_list, .other);
76106752 try w.writeAll(");");
7611 try f.object.newline();
6753 try f.newline();
76126754 return .none;
76136755}
76146756
......@@ -7619,14 +6761,14 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {
76196761 const va_list = try f.resolveInst(ty_op.operand);
76206762 try reap(f, inst, &.{ty_op.operand});
76216763
7622 const w = &f.object.code.writer;
6764 const w = &f.code.writer;
76236765 const local = try f.allocLocal(inst, inst_ty);
76246766 try w.writeAll("va_copy(*(va_list *)&");
7625 try f.writeCValue(w, local, .Other);
6767 try f.writeCValue(w, local, .other);
76266768 try w.writeAll(", *(va_list *)");
7627 try f.writeCValue(w, va_list, .Other);
6769 try f.writeCValue(w, va_list, .other);
76286770 try w.writeAll(");");
7629 try f.object.newline();
6771 try f.newline();
76306772 return local;
76316773}
76326774
......@@ -7943,103 +7085,193 @@ fn undefPattern(comptime IntType: type) IntType {
79437085
79447086const FormatIntLiteralContext = struct {
79457087 dg: *DeclGen,
7946 int_info: InternPool.Key.IntType,
7947 kind: CType.Kind,
7948 ctype: CType,
7088 loc: ValueRenderLocation,
79497089 val: Value,
7090 cty: CType,
79507091 base: u8,
79517092 case: std.fmt.Case,
79527093};
79537094fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void {
7954 const pt = data.dg.pt;
7955 const zcu = pt.zcu;
7956 const target = &data.dg.mod.resolved_target.result;
7957 const ctype_pool = &data.dg.ctype_pool;
7958
7959 const ExpectedContents = struct {
7960 const base = 10;
7961 const bits = 128;
7962 const limbs_count = BigInt.calcTwosCompLimbCount(bits);
7963
7964 undef_limbs: [limbs_count]BigIntLimb,
7965 wrap_limbs: [limbs_count]BigIntLimb,
7966 to_string_buf: [bits]u8,
7967 to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb,
7968 };
7969 var stack align(@alignOf(ExpectedContents)) =
7970 std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa);
7971 const allocator = stack.get();
7972
7973 var undef_limbs: []BigIntLimb = &.{};
7974 defer allocator.free(undef_limbs);
7975
7976 var int_buf: Value.BigIntSpace = undefined;
7977 const int = if (data.val.isUndef(zcu)) blk: {
7978 undef_limbs = allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits)) catch return error.WriteFailed;
7979 @memset(undef_limbs, undefPattern(BigIntLimb));
7980
7981 var undef_int = BigInt.Mutable{
7982 .limbs = undef_limbs,
7983 .len = undef_limbs.len,
7984 .positive = true,
7985 };
7986 undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits);
7987 break :blk undef_int.toConst();
7988 } else data.val.toBigInt(&int_buf, zcu);
7989 assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits));
7990
7991 const c_bits: usize = @intCast(data.ctype.byteSize(ctype_pool, data.dg.mod) * 8);
7992 var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined;
7993 const one = BigInt.Mutable.init(&one_limbs, 1).toConst();
7994
7995 var wrap = BigInt.Mutable{
7996 .limbs = allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)) catch return error.WriteFailed,
7997 .len = undefined,
7998 .positive = undefined,
7999 };
8000 defer allocator.free(wrap.limbs);
8001
8002 const c_limb_info: struct {
8003 ctype: CType,
8004 count: usize,
8005 endian: std.builtin.Endian,
8006 homogeneous: bool,
8007 } = switch (data.ctype.info(ctype_pool)) {
8008 .basic => |basic_info| switch (basic_info) {
8009 else => .{
8010 .ctype = .void,
8011 .count = 1,
8012 .endian = .little,
8013 .homogeneous = true,
7095 const dg = data.dg;
7096 const zcu = dg.pt.zcu;
7097 const target = &dg.mod.resolved_target.result;
7098
7099 const val = data.val;
7100 const ty = val.typeOf(zcu);
7101
7102 assert(!val.isUndef(zcu));
7103
7104 var space: Value.BigIntSpace = undefined;
7105 const val_bigint = val.toBigInt(&space, zcu);
7106
7107 switch (CType.classifyInt(ty, zcu)) {
7108 .void => unreachable, // opv
7109 .small => |int_cty| return FormatInt128.format(.{
7110 .target = zcu.getTarget(),
7111 .int_cty = int_cty,
7112 .val = val_bigint,
7113 .is_global = data.loc == .static_initializer,
7114 .base = data.base,
7115 .case = data.case,
7116 }, w),
7117 .big => |big| {
7118 if (!data.loc.isInitializer()) {
7119 // Use `CType.fmtTypeName` directly to avoid the possibility of `error.OutOfMemory`.
7120 try w.print("({f})", .{data.cty.fmtTypeName(zcu)});
7121 }
7122
7123 try w.writeAll("{{");
7124
7125 var limb_buf: [std.math.big.int.calcTwosCompLimbCount(65535)]std.math.big.Limb = undefined;
7126 for (0..big.limbs_len) |limb_index| {
7127 if (limb_index != 0) try w.writeAll(", ");
7128 const limb_bit_offset: u64 = switch (target.cpu.arch.endian()) {
7129 .little => limb_index * big.limb_size.bits(),
7130 .big => (big.limbs_len - limb_index - 1) * big.limb_size.bits(),
7131 };
7132 var limb_bigint: std.math.big.int.Mutable = .{
7133 .limbs = &limb_buf,
7134 .len = undefined,
7135 .positive = undefined,
7136 };
7137 limb_bigint.shiftRight(val_bigint, limb_bit_offset);
7138 limb_bigint.truncate(limb_bigint.toConst(), .unsigned, big.limb_size.bits());
7139 try FormatInt128.format(.{
7140 .target = zcu.getTarget(),
7141 .int_cty = big.limb_size.unsigned(),
7142 .val = limb_bigint.toConst(),
7143 .is_global = data.loc == .static_initializer,
7144 .base = data.base,
7145 .case = data.case,
7146 }, w);
7147 }
7148
7149 try w.writeAll("}}");
7150 },
7151 }
7152}
7153const FormatInt128 = struct {
7154 target: *const std.Target,
7155 int_cty: CType.Int,
7156 val: std.math.big.int.Const,
7157 is_global: bool,
7158 base: u8,
7159 case: std.fmt.Case,
7160 pub fn format(data: FormatInt128, w: *Writer) Writer.Error!void {
7161 const target = data.target;
7162
7163 const val = data.val;
7164 const is_global = data.is_global;
7165 const base = data.base;
7166 const case = data.case;
7167
7168 switch (data.int_cty) {
7169 .uint8_t,
7170 .uint16_t,
7171 .uint32_t,
7172 .uint64_t,
7173 .@"unsigned short",
7174 .@"unsigned int",
7175 .@"unsigned long",
7176 .@"unsigned long long",
7177 .uintptr_t,
7178 => |t| try w.print("{f}", .{
7179 fmtUnsignedIntLiteralSmall(target, t, val.toInt(u64) catch unreachable, is_global, base, case),
7180 }),
7181
7182 .int8_t,
7183 .int16_t,
7184 .int32_t,
7185 .int64_t,
7186 .char,
7187 .@"signed short",
7188 .@"signed int",
7189 .@"signed long",
7190 .@"signed long long",
7191 .intptr_t,
7192 => |t| try w.print("{f}", .{
7193 fmtSignedIntLiteralSmall(target, t, val.toInt(i64) catch unreachable, is_global, base, case),
7194 }),
7195
7196 .zig_u128 => {
7197 const raw = val.toInt(u128) catch unreachable;
7198 const lo: u64 = @truncate(raw);
7199 const hi: u64 = @intCast(raw >> 64);
7200 const macro_name: []const u8 = if (is_global) "zig_init_u128" else "zig_make_u128";
7201 try w.print("{s}({f}, {f})", .{
7202 macro_name,
7203 fmtUnsignedIntLiteralSmall(target, .uint64_t, hi, is_global, base, case),
7204 fmtUnsignedIntLiteralSmall(target, .uint64_t, lo, is_global, base, case),
7205 });
80147206 },
8015 .zig_u128, .zig_i128 => .{
8016 .ctype = .u64,
8017 .count = 2,
8018 .endian = .big,
8019 .homogeneous = false,
7207
7208 .zig_i128 => {
7209 const raw = val.toInt(i128) catch unreachable;
7210 const lo: u64 = @truncate(@as(u128, @bitCast(raw)));
7211 const hi: i64 = @intCast(raw >> 64);
7212 const macro_name: []const u8 = if (is_global) "zig_init_i128" else "zig_make_i128";
7213 try w.print("{s}({f}, {f})", .{
7214 macro_name,
7215 fmtSignedIntLiteralSmall(target, .int64_t, hi, is_global, base, case),
7216 fmtUnsignedIntLiteralSmall(target, .uint64_t, lo, is_global, base, case),
7217 });
80207218 },
8021 },
8022 .array => |array_info| .{
8023 .ctype = array_info.elem_ctype,
8024 .count = @intCast(array_info.len),
8025 .endian = target.cpu.arch.endian(),
8026 .homogeneous = true,
8027 },
8028 else => unreachable,
7219 }
7220 }
7221};
7222fn fmtUnsignedIntLiteralSmall(
7223 target: *const std.Target,
7224 int_cty: CType.Int,
7225 val: u64,
7226 is_global: bool,
7227 base: u8,
7228 case: std.fmt.Case,
7229) FormatUnsignedIntLiteralSmall {
7230 return .{
7231 .target = target,
7232 .int_cty = int_cty,
7233 .val = val,
7234 .is_global = is_global,
7235 .base = base,
7236 .case = case,
80297237 };
8030 if (c_limb_info.count == 1) {
8031 if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or
8032 data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits))
8033 return w.print("{s}_{s}", .{
8034 data.ctype.getStandardDefineAbbrev() orelse return w.print("zig_{s}Int_{c}{d}", .{
8035 if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits,
8036 }),
8037 if (int.positive) "MAX" else "MIN",
8038 });
8039
8040 if (!int.positive) try w.writeByte('-');
8041 try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool);
7238}
7239fn fmtSignedIntLiteralSmall(
7240 target: *const std.Target,
7241 int_cty: CType.Int,
7242 val: i64,
7243 is_global: bool,
7244 base: u8,
7245 case: std.fmt.Case,
7246) FormatSignedIntLiteralSmall {
7247 return .{
7248 .target = target,
7249 .int_cty = int_cty,
7250 .val = val,
7251 .is_global = is_global,
7252 .base = base,
7253 .case = case,
7254 };
7255}
80427256
7257const FormatSignedIntLiteralSmall = struct {
7258 target: *const std.Target,
7259 int_cty: CType.Int,
7260 val: i64,
7261 is_global: bool,
7262 base: u8,
7263 case: std.fmt.Case,
7264 pub fn format(data: FormatSignedIntLiteralSmall, w: *Writer) Writer.Error!void {
7265 const bits = data.int_cty.bits(data.target);
7266 const max_int: i64 = @bitCast((@as(u64, 1) << @intCast(bits - 1)) - 1);
7267 const min_int: i64 = @bitCast(@as(u64, 1) << @intCast(bits - 1));
7268 if (data.val == max_int) {
7269 return w.print("{s}_MAX", .{minMaxMacroPrefix(data.int_cty)});
7270 } else if (data.val == min_int) {
7271 return w.print("{s}_MIN", .{minMaxMacroPrefix(data.int_cty)});
7272 }
7273 if (data.val < 0) try w.writeByte('-');
7274 try w.writeAll(intLiteralPrefix(data.int_cty, data.is_global));
80437275 switch (data.base) {
80447276 2 => try w.writeAll("0b"),
80457277 8 => try w.writeByte('0'),
......@@ -8047,68 +7279,131 @@ fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void
80477279 16 => try w.writeAll("0x"),
80487280 else => unreachable,
80497281 }
8050 const string = int.abs().toStringAlloc(allocator, data.base, data.case) catch
8051 return error.WriteFailed;
8052 defer allocator.free(string);
8053 try w.writeAll(string);
8054 } else {
8055 try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool);
8056 wrap.truncate(int, .unsigned, c_bits);
8057 @memset(wrap.limbs[wrap.len..], 0);
8058 wrap.len = wrap.limbs.len;
8059 const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);
8060
8061 var c_limb_int_info: std.builtin.Type.Int = .{
8062 .signedness = undefined,
8063 .bits = @intCast(@divExact(c_bits, c_limb_info.count)),
8064 };
8065 var c_limb_ctype: CType = undefined;
8066
8067 var limb_offset: usize = 0;
8068 const most_significant_limb_i = wrap.len - limbs_per_c_limb;
8069 while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) {
8070 const limb_i = switch (c_limb_info.endian) {
8071 .little => limb_offset,
8072 .big => most_significant_limb_i - limb_offset,
8073 };
8074 var c_limb_mut = BigInt.Mutable{
8075 .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb],
8076 .len = undefined,
8077 .positive = true,
8078 };
8079 c_limb_mut.normalize(limbs_per_c_limb);
8080
8081 if (limb_i == most_significant_limb_i and
8082 !c_limb_info.homogeneous and data.int_info.signedness == .signed)
8083 {
8084 // most significant limb is actually signed
8085 c_limb_int_info.signedness = .signed;
8086 c_limb_ctype = c_limb_info.ctype.toSigned();
8087
8088 c_limb_mut.truncate(
8089 c_limb_mut.toConst(),
8090 .signed,
8091 data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb),
8092 );
8093 } else {
8094 c_limb_int_info.signedness = .unsigned;
8095 c_limb_ctype = c_limb_info.ctype;
8096 }
8097
8098 if (limb_offset > 0) try w.writeAll(", ");
8099 try formatIntLiteral(.{
8100 .dg = data.dg,
8101 .int_info = c_limb_int_info,
8102 .kind = data.kind,
8103 .ctype = c_limb_ctype,
8104 .val = pt.intValue_big(.comptime_int, c_limb_mut.toConst()) catch
8105 return error.WriteFailed,
8106 .base = data.base,
8107 .case = data.case,
8108 }, w);
7282 // This `@abs` is safe thanks to the `min_int` case above.
7283 try w.printInt(@abs(data.val), data.base, data.case, .{});
7284 try w.writeAll(intLiteralSuffix(data.int_cty));
7285 }
7286};
7287const FormatUnsignedIntLiteralSmall = struct {
7288 target: *const std.Target,
7289 int_cty: CType.Int,
7290 val: u64,
7291 is_global: bool,
7292 base: u8,
7293 case: std.fmt.Case,
7294 pub fn format(data: FormatUnsignedIntLiteralSmall, w: *Writer) Writer.Error!void {
7295 const bits = data.int_cty.bits(data.target);
7296 const max_int: u64 = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - bits);
7297 if (data.val == max_int) {
7298 return w.print("{s}_MAX", .{minMaxMacroPrefix(data.int_cty)});
7299 }
7300 try w.writeAll(intLiteralPrefix(data.int_cty, data.is_global));
7301 switch (data.base) {
7302 2 => try w.writeAll("0b"),
7303 8 => try w.writeByte('0'),
7304 10 => {},
7305 16 => try w.writeAll("0x"),
7306 else => unreachable,
81097307 }
7308 try w.printInt(data.val, data.base, data.case, .{});
7309 try w.writeAll(intLiteralSuffix(data.int_cty));
81107310 }
8111 try data.ctype.renderLiteralSuffix(w, ctype_pool);
7311};
7312fn minMaxMacroPrefix(int_cty: CType.Int) []const u8 {
7313 return switch (int_cty) {
7314 // zig fmt: off
7315 .char => "CHAR",
7316
7317 .@"unsigned short" => "USHRT",
7318 .@"unsigned int" => "UINT",
7319 .@"unsigned long" => "ULONG",
7320 .@"unsigned long long" => "ULLONG",
7321
7322 .@"signed short" => "SHRT",
7323 .@"signed int" => "INT",
7324 .@"signed long" => "LONG",
7325 .@"signed long long" => "LLONG",
7326
7327 .uint8_t => "UINT8",
7328 .uint16_t => "UINT16",
7329 .uint32_t => "UINT32",
7330 .uint64_t => "UINT64",
7331 .zig_u128 => unreachable,
7332
7333 .int8_t => "INT8",
7334 .int16_t => "INT16",
7335 .int32_t => "INT32",
7336 .int64_t => "INT64",
7337 .zig_i128 => unreachable,
7338
7339 .uintptr_t => "UINTPTR",
7340 .intptr_t => "INTPTR",
7341 // zig fmt: on
7342 };
7343}
7344fn intLiteralPrefix(cty: CType.Int, is_global: bool) []const u8 {
7345 return switch (cty) {
7346 // zig fmt: off
7347 .char => if (is_global) "" else "(char)",
7348
7349 .@"unsigned short" => if (is_global) "" else "(unsigned short)",
7350 .@"unsigned int" => "",
7351 .@"unsigned long" => "",
7352 .@"unsigned long long" => "",
7353
7354 .@"signed short" => if (is_global) "" else "(signed short)",
7355 .@"signed int" => "",
7356 .@"signed long" => "",
7357 .@"signed long long" => "",
7358
7359 .uint8_t => "UINT8_C(",
7360 .uint16_t => "UINT16_C(",
7361 .uint32_t => "UINT32_C(",
7362 .uint64_t => "UINT64_C(",
7363 .zig_u128 => unreachable,
7364
7365 .int8_t => "INT8_C(",
7366 .int16_t => "INT16_C(",
7367 .int32_t => "INT32_C(",
7368 .int64_t => "INT64_C(",
7369 .zig_i128 => unreachable,
7370
7371 .uintptr_t => if (is_global) "" else "(uintptr_t)",
7372 .intptr_t => if (is_global) "" else "(intptr_t)",
7373 // zig fmt: on
7374 };
7375}
7376fn intLiteralSuffix(cty: CType.Int) []const u8 {
7377 return switch (cty) {
7378 // zig fmt: off
7379 .char => "",
7380
7381 .@"unsigned short" => "u",
7382 .@"unsigned int" => "u",
7383 .@"unsigned long" => "ul",
7384 .@"unsigned long long" => "ull",
7385
7386 .@"signed short" => "",
7387 .@"signed int" => "",
7388 .@"signed long" => "l",
7389 .@"signed long long" => "ll",
7390
7391 .uint8_t => ")",
7392 .uint16_t => ")",
7393 .uint32_t => ")",
7394 .uint64_t => ")",
7395 .zig_u128 => unreachable,
7396
7397 .int8_t => ")",
7398 .int16_t => ")",
7399 .int32_t => ")",
7400 .int64_t => ")",
7401 .zig_i128 => unreachable,
7402
7403 .uintptr_t => "ul",
7404 .intptr_t => "",
7405 // zig fmt: on
7406 };
81127407}
81137408
81147409const Materialize = struct {
......@@ -8123,7 +7418,7 @@ const Materialize = struct {
81237418 }
81247419
81257420 pub fn mat(self: Materialize, f: *Function, w: *Writer) !void {
8126 try f.writeCValue(w, self.local, .Other);
7421 try f.writeCValue(w, self.local, .other);
81277422 }
81287423
81297424 pub fn end(self: Materialize, f: *Function, inst: Air.Inst.Index) !void {
......@@ -8131,95 +7426,52 @@ const Materialize = struct {
81317426 }
81327427};
81337428
8134const Assignment = struct {
8135 ctype: CType,
8136
8137 pub fn start(f: *Function, w: *Writer, ctype: CType) !Assignment {
8138 const self: Assignment = .{ .ctype = ctype };
8139 try self.restart(f, w);
8140 return self;
8141 }
8142
8143 pub fn restart(self: Assignment, f: *Function, w: *Writer) !void {
8144 switch (self.strategy(f)) {
8145 .assign => {},
8146 .memcpy => try w.writeAll("memcpy("),
8147 }
8148 }
8149
8150 pub fn assign(self: Assignment, f: *Function, w: *Writer) !void {
8151 switch (self.strategy(f)) {
8152 .assign => try w.writeAll(" = "),
8153 .memcpy => try w.writeAll(", "),
8154 }
8155 }
8156
8157 pub fn end(self: Assignment, f: *Function, w: *Writer) !void {
8158 switch (self.strategy(f)) {
8159 .assign => {},
8160 .memcpy => {
8161 try w.writeAll(", sizeof(");
8162 try f.renderCType(w, self.ctype);
8163 try w.writeAll("))");
8164 },
8165 }
8166 try w.writeByte(';');
8167 try f.object.newline();
8168 }
8169
8170 fn strategy(self: Assignment, f: *Function) enum { assign, memcpy } {
8171 return switch (self.ctype.info(&f.object.dg.ctype_pool)) {
8172 else => .assign,
8173 .array, .vector => .memcpy,
8174 };
8175 }
8176};
8177
81787429const Vectorize = struct {
81797430 index: CValue = .none,
81807431
81817432 pub fn start(f: *Function, inst: Air.Inst.Index, w: *Writer, ty: Type) !Vectorize {
8182 const pt = f.object.dg.pt;
7433 const pt = f.dg.pt;
81837434 const zcu = pt.zcu;
8184 return if (ty.zigTypeTag(zcu) == .vector) index: {
8185 const local = try f.allocLocal(inst, .usize);
8186
8187 try w.writeAll("for (");
8188 try f.writeCValue(w, local, .Other);
8189 try w.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)});
8190 try f.writeCValue(w, local, .Other);
8191 try w.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))});
8192 try f.writeCValue(w, local, .Other);
8193 try w.print(" += {f}) {{\n", .{try f.fmtIntLiteralDec(.one_usize)});
8194 f.object.indent();
8195 try f.object.newline();
8196
8197 break :index .{ .index = local };
8198 } else .{};
7435 switch (ty.zigTypeTag(zcu)) {
7436 else => return .{ .index = .none },
7437 .vector => {
7438 const local = try f.allocLocal(inst, .usize);
7439 try w.writeAll("for (");
7440 try f.writeCValue(w, local, .other);
7441 try w.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)});
7442 try f.writeCValue(w, local, .other);
7443 try w.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))});
7444 try f.writeCValue(w, local, .other);
7445 try w.print(" += {f}) {{", .{try f.fmtIntLiteralDec(.one_usize)});
7446 f.indent();
7447 try f.newline();
7448 return .{ .index = local };
7449 },
7450 }
81997451 }
82007452
82017453 pub fn elem(self: Vectorize, f: *Function, w: *Writer) !void {
82027454 if (self.index != .none) {
8203 try w.writeByte('[');
8204 try f.writeCValue(w, self.index, .Other);
7455 try w.writeAll(".array[");
7456 try f.writeCValue(w, self.index, .other);
82057457 try w.writeByte(']');
82067458 }
82077459 }
82087460
82097461 pub fn end(self: Vectorize, f: *Function, inst: Air.Inst.Index, w: *Writer) !void {
82107462 if (self.index != .none) {
8211 try f.object.outdent();
7463 try f.outdent();
82127464 try w.writeByte('}');
8213 try f.object.newline();
7465 try f.newline();
82147466 try freeLocal(f, inst, self.index.new_local, null);
82157467 }
82167468 }
82177469};
82187470
8219fn lowersToArray(ty: Type, zcu: *Zcu) bool {
7471fn lowersToBigInt(ty: Type, zcu: *const Zcu) bool {
82207472 return switch (ty.zigTypeTag(zcu)) {
8221 .array, .vector => return true,
8222 else => return ty.isAbiInt(zcu) and toCIntBits(@as(u32, @intCast(ty.bitSize(zcu)))) == null,
7473 .int, .@"enum", .@"struct", .@"union" => CType.classifyInt(ty, zcu) == .big,
7474 else => false,
82237475 };
82247476}
82257477
......@@ -8245,8 +7497,8 @@ fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void {
82457497}
82467498
82477499fn freeLocal(f: *Function, inst: ?Air.Inst.Index, local_index: LocalIndex, ref_inst: ?Air.Inst.Index) !void {
8248 const gpa = f.object.dg.gpa;
8249 const local = &f.locals.items[local_index];
7500 const gpa = f.dg.gpa;
7501 const local = f.locals.items[local_index];
82507502 if (inst) |i| {
82517503 if (ref_inst) |operand| {
82527504 log.debug("%{d}: freeing t{d} (operand %{d})", .{ @intFromEnum(i), local_index, operand });
......@@ -8260,7 +7512,7 @@ fn freeLocal(f: *Function, inst: ?Air.Inst.Index, local_index: LocalIndex, ref_i
82607512 log.debug("freeing t{d}", .{local_index});
82617513 }
82627514 }
8263 const gop = try f.free_locals_map.getOrPut(gpa, local.getType());
7515 const gop = try f.free_locals_map.getOrPut(gpa, local);
82647516 if (!gop.found_existing) gop.value_ptr.* = .{};
82657517 if (std.debug.runtime_safety) {
82667518 // If this trips, an unfreeable allocation was attempted to be freed.
......@@ -8317,3 +7569,28 @@ fn deinitFreeLocalsMap(gpa: Allocator, map: *LocalsMap) void {
83177569 }
83187570 map.deinit(gpa);
83197571}
7572
7573fn renderErrorName(w: *Writer, err_name: []const u8) Writer.Error!void {
7574 try w.print("zig_error_{f}", .{fmtIdentUnsolo(err_name)});
7575}
7576
7577fn renderNavName(w: *Writer, nav_index: InternPool.Nav.Index, ip: *const InternPool) !void {
7578 const nav = ip.getNav(nav_index);
7579 if (nav.getExtern(ip)) |@"extern"| {
7580 try w.print("{f}", .{
7581 fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
7582 });
7583 } else {
7584 // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
7585 // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
7586 const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
7587 try w.print("{f}__{d}", .{
7588 fmtIdentUnsolo(fqn_slice[0..@min(fqn_slice.len, 100)]),
7589 @intFromEnum(nav_index),
7590 });
7591 }
7592}
7593
7594fn renderUavName(w: *Writer, uav: Value) !void {
7595 try w.print("__anon_{d}", .{@intFromEnum(uav.toIntern())});
7596}
src/codegen/c/Type.zig deleted-3471
......@@ -1,3471 +0,0 @@
1index: CType.Index,
2
3pub const @"void": CType = .{ .index = .void };
4pub const @"bool": CType = .{ .index = .bool };
5pub const @"i8": CType = .{ .index = .int8_t };
6pub const @"u8": CType = .{ .index = .uint8_t };
7pub const @"i16": CType = .{ .index = .int16_t };
8pub const @"u16": CType = .{ .index = .uint16_t };
9pub const @"i32": CType = .{ .index = .int32_t };
10pub const @"u32": CType = .{ .index = .uint32_t };
11pub const @"i64": CType = .{ .index = .int64_t };
12pub const @"u64": CType = .{ .index = .uint64_t };
13pub const @"i128": CType = .{ .index = .zig_i128 };
14pub const @"u128": CType = .{ .index = .zig_u128 };
15pub const @"isize": CType = .{ .index = .intptr_t };
16pub const @"usize": CType = .{ .index = .uintptr_t };
17pub const @"f16": CType = .{ .index = .zig_f16 };
18pub const @"f32": CType = .{ .index = .zig_f32 };
19pub const @"f64": CType = .{ .index = .zig_f64 };
20pub const @"f80": CType = .{ .index = .zig_f80 };
21pub const @"f128": CType = .{ .index = .zig_f128 };
22
23pub fn fromPoolIndex(pool_index: usize) CType {
24 return .{ .index = @enumFromInt(CType.Index.first_pool_index + pool_index) };
25}
26
27pub fn toPoolIndex(ctype: CType) ?u32 {
28 const pool_index, const is_null =
29 @subWithOverflow(@intFromEnum(ctype.index), CType.Index.first_pool_index);
30 return switch (is_null) {
31 0 => pool_index,
32 1 => null,
33 };
34}
35
36pub fn eql(lhs: CType, rhs: CType) bool {
37 return lhs.index == rhs.index;
38}
39
40pub fn isBool(ctype: CType) bool {
41 return switch (ctype.index) {
42 ._Bool, .bool => true,
43 else => false,
44 };
45}
46
47pub fn isInteger(ctype: CType) bool {
48 return switch (ctype.index) {
49 .char,
50 .@"signed char",
51 .short,
52 .int,
53 .long,
54 .@"long long",
55 .@"unsigned char",
56 .@"unsigned short",
57 .@"unsigned int",
58 .@"unsigned long",
59 .@"unsigned long long",
60 .size_t,
61 .ptrdiff_t,
62 .uint8_t,
63 .int8_t,
64 .uint16_t,
65 .int16_t,
66 .uint32_t,
67 .int32_t,
68 .uint64_t,
69 .int64_t,
70 .uintptr_t,
71 .intptr_t,
72 .zig_u128,
73 .zig_i128,
74 => true,
75 else => false,
76 };
77}
78
79pub fn signedness(ctype: CType, mod: *Module) std.builtin.Signedness {
80 return switch (ctype.index) {
81 .char => mod.resolved_target.result.cCharSignedness(),
82 .@"signed char",
83 .short,
84 .int,
85 .long,
86 .@"long long",
87 .ptrdiff_t,
88 .int8_t,
89 .int16_t,
90 .int32_t,
91 .int64_t,
92 .intptr_t,
93 .zig_i128,
94 => .signed,
95 .@"unsigned char",
96 .@"unsigned short",
97 .@"unsigned int",
98 .@"unsigned long",
99 .@"unsigned long long",
100 .size_t,
101 .uint8_t,
102 .uint16_t,
103 .uint32_t,
104 .uint64_t,
105 .uintptr_t,
106 .zig_u128,
107 => .unsigned,
108 else => unreachable,
109 };
110}
111
112pub fn isFloat(ctype: CType) bool {
113 return switch (ctype.index) {
114 .float,
115 .double,
116 .@"long double",
117 .zig_f16,
118 .zig_f32,
119 .zig_f64,
120 .zig_f80,
121 .zig_f128,
122 .zig_c_longdouble,
123 => true,
124 else => false,
125 };
126}
127
128pub fn toSigned(ctype: CType) CType {
129 return switch (ctype.index) {
130 .char, .@"signed char", .@"unsigned char" => .{ .index = .@"signed char" },
131 .short, .@"unsigned short" => .{ .index = .short },
132 .int, .@"unsigned int" => .{ .index = .int },
133 .long, .@"unsigned long" => .{ .index = .long },
134 .@"long long", .@"unsigned long long" => .{ .index = .@"long long" },
135 .size_t, .ptrdiff_t => .{ .index = .ptrdiff_t },
136 .uint8_t, .int8_t => .{ .index = .int8_t },
137 .uint16_t, .int16_t => .{ .index = .int16_t },
138 .uint32_t, .int32_t => .{ .index = .int32_t },
139 .uint64_t, .int64_t => .{ .index = .int64_t },
140 .uintptr_t, .intptr_t => .{ .index = .intptr_t },
141 .zig_u128, .zig_i128 => .{ .index = .zig_i128 },
142 .float,
143 .double,
144 .@"long double",
145 .zig_f16,
146 .zig_f32,
147 .zig_f80,
148 .zig_f128,
149 .zig_c_longdouble,
150 => ctype,
151 else => unreachable,
152 };
153}
154
155pub fn toUnsigned(ctype: CType) CType {
156 return switch (ctype.index) {
157 .char, .@"signed char", .@"unsigned char" => .{ .index = .@"unsigned char" },
158 .short, .@"unsigned short" => .{ .index = .@"unsigned short" },
159 .int, .@"unsigned int" => .{ .index = .@"unsigned int" },
160 .long, .@"unsigned long" => .{ .index = .@"unsigned long" },
161 .@"long long", .@"unsigned long long" => .{ .index = .@"unsigned long long" },
162 .size_t, .ptrdiff_t => .{ .index = .size_t },
163 .uint8_t, .int8_t => .{ .index = .uint8_t },
164 .uint16_t, .int16_t => .{ .index = .uint16_t },
165 .uint32_t, .int32_t => .{ .index = .uint32_t },
166 .uint64_t, .int64_t => .{ .index = .uint64_t },
167 .uintptr_t, .intptr_t => .{ .index = .uintptr_t },
168 .zig_u128, .zig_i128 => .{ .index = .zig_u128 },
169 else => unreachable,
170 };
171}
172
173pub fn toSignedness(ctype: CType, s: std.builtin.Signedness) CType {
174 return switch (s) {
175 .unsigned => ctype.toUnsigned(),
176 .signed => ctype.toSigned(),
177 };
178}
179
180pub fn isAnyChar(ctype: CType) bool {
181 return switch (ctype.index) {
182 else => false,
183 .char, .@"signed char", .@"unsigned char", .uint8_t, .int8_t => true,
184 };
185}
186
187pub fn isString(ctype: CType, pool: *const Pool) bool {
188 return info: switch (ctype.info(pool)) {
189 .basic, .fwd_decl, .aggregate, .function => false,
190 .pointer => |pointer_info| pointer_info.elem_ctype.isAnyChar(),
191 .aligned => |aligned_info| continue :info aligned_info.ctype.info(pool),
192 .array, .vector => |sequence_info| sequence_info.elem_type.isAnyChar(),
193 };
194}
195
196pub fn isNonString(ctype: CType, pool: *const Pool) bool {
197 var allow_pointer = true;
198 return info: switch (ctype.info(pool)) {
199 .basic, .fwd_decl, .aggregate, .function => false,
200 .pointer => |pointer_info| allow_pointer and pointer_info.nonstring,
201 .aligned => |aligned_info| continue :info aligned_info.ctype.info(pool),
202 .array, .vector => |sequence_info| sequence_info.nonstring or {
203 allow_pointer = false;
204 continue :info sequence_info.elem_ctype.info(pool);
205 },
206 };
207}
208
209pub fn getStandardDefineAbbrev(ctype: CType) ?[]const u8 {
210 return switch (ctype.index) {
211 .char => "CHAR",
212 .@"signed char" => "SCHAR",
213 .short => "SHRT",
214 .int => "INT",
215 .long => "LONG",
216 .@"long long" => "LLONG",
217 .@"unsigned char" => "UCHAR",
218 .@"unsigned short" => "USHRT",
219 .@"unsigned int" => "UINT",
220 .@"unsigned long" => "ULONG",
221 .@"unsigned long long" => "ULLONG",
222 .float => "FLT",
223 .double => "DBL",
224 .@"long double" => "LDBL",
225 .size_t => "SIZE",
226 .ptrdiff_t => "PTRDIFF",
227 .uint8_t => "UINT8",
228 .int8_t => "INT8",
229 .uint16_t => "UINT16",
230 .int16_t => "INT16",
231 .uint32_t => "UINT32",
232 .int32_t => "INT32",
233 .uint64_t => "UINT64",
234 .int64_t => "INT64",
235 .uintptr_t => "UINTPTR",
236 .intptr_t => "INTPTR",
237 else => null,
238 };
239}
240
241pub fn renderLiteralPrefix(ctype: CType, w: *Writer, kind: Kind, pool: *const Pool) Writer.Error!void {
242 switch (ctype.info(pool)) {
243 .basic => |basic_info| switch (basic_info) {
244 .void => unreachable,
245 ._Bool,
246 .char,
247 .@"signed char",
248 .short,
249 .@"unsigned short",
250 .bool,
251 .size_t,
252 .ptrdiff_t,
253 .uintptr_t,
254 .intptr_t,
255 => switch (kind) {
256 else => try w.print("({s})", .{@tagName(basic_info)}),
257 .global => {},
258 },
259 .int,
260 .long,
261 .@"long long",
262 .@"unsigned char",
263 .@"unsigned int",
264 .@"unsigned long",
265 .@"unsigned long long",
266 .float,
267 .double,
268 .@"long double",
269 => {},
270 .uint8_t,
271 .int8_t,
272 .uint16_t,
273 .int16_t,
274 .uint32_t,
275 .int32_t,
276 .uint64_t,
277 .int64_t,
278 => try w.print("{s}_C(", .{ctype.getStandardDefineAbbrev().?}),
279 .zig_u128,
280 .zig_i128,
281 .zig_f16,
282 .zig_f32,
283 .zig_f64,
284 .zig_f80,
285 .zig_f128,
286 .zig_c_longdouble,
287 => try w.print("zig_{s}_{s}(", .{
288 switch (kind) {
289 else => "make",
290 .global => "init",
291 },
292 @tagName(basic_info)["zig_".len..],
293 }),
294 .va_list => unreachable,
295 _ => unreachable,
296 },
297 .array, .vector => try w.writeByte('{'),
298 else => unreachable,
299 }
300}
301
302pub fn renderLiteralSuffix(ctype: CType, w: *Writer, pool: *const Pool) Writer.Error!void {
303 switch (ctype.info(pool)) {
304 .basic => |basic_info| switch (basic_info) {
305 .void => unreachable,
306 ._Bool => {},
307 .char,
308 .@"signed char",
309 .short,
310 .int,
311 => {},
312 .long => try w.writeByte('l'),
313 .@"long long" => try w.writeAll("ll"),
314 .@"unsigned char",
315 .@"unsigned short",
316 .@"unsigned int",
317 => try w.writeByte('u'),
318 .@"unsigned long",
319 .size_t,
320 .uintptr_t,
321 => try w.writeAll("ul"),
322 .@"unsigned long long" => try w.writeAll("ull"),
323 .float => try w.writeByte('f'),
324 .double => {},
325 .@"long double" => try w.writeByte('l'),
326 .bool,
327 .ptrdiff_t,
328 .intptr_t,
329 => {},
330 .uint8_t,
331 .int8_t,
332 .uint16_t,
333 .int16_t,
334 .uint32_t,
335 .int32_t,
336 .uint64_t,
337 .int64_t,
338 .zig_u128,
339 .zig_i128,
340 .zig_f16,
341 .zig_f32,
342 .zig_f64,
343 .zig_f80,
344 .zig_f128,
345 .zig_c_longdouble,
346 => try w.writeByte(')'),
347 .va_list => unreachable,
348 _ => unreachable,
349 },
350 .array, .vector => try w.writeByte('}'),
351 else => unreachable,
352 }
353}
354
355pub fn floatActiveBits(ctype: CType, mod: *Module) u16 {
356 const target = &mod.resolved_target.result;
357 return switch (ctype.index) {
358 .float => target.cTypeBitSize(.float),
359 .double => target.cTypeBitSize(.double),
360 .@"long double", .zig_c_longdouble => target.cTypeBitSize(.longdouble),
361 .zig_f16 => 16,
362 .zig_f32 => 32,
363 .zig_f64 => 64,
364 .zig_f80 => 80,
365 .zig_f128 => 128,
366 else => unreachable,
367 };
368}
369
370pub fn byteSize(ctype: CType, pool: *const Pool, mod: *Module) u64 {
371 const target = &mod.resolved_target.result;
372 return switch (ctype.info(pool)) {
373 .basic => |basic_info| switch (basic_info) {
374 .void => 0,
375 .char, .@"signed char", ._Bool, .@"unsigned char", .bool, .uint8_t, .int8_t => 1,
376 .short => target.cTypeByteSize(.short),
377 .int => target.cTypeByteSize(.int),
378 .long => target.cTypeByteSize(.long),
379 .@"long long" => target.cTypeByteSize(.longlong),
380 .@"unsigned short" => target.cTypeByteSize(.ushort),
381 .@"unsigned int" => target.cTypeByteSize(.uint),
382 .@"unsigned long" => target.cTypeByteSize(.ulong),
383 .@"unsigned long long" => target.cTypeByteSize(.ulonglong),
384 .float => target.cTypeByteSize(.float),
385 .double => target.cTypeByteSize(.double),
386 .@"long double" => target.cTypeByteSize(.longdouble),
387 .size_t,
388 .ptrdiff_t,
389 .uintptr_t,
390 .intptr_t,
391 => @divExact(target.ptrBitWidth(), 8),
392 .uint16_t, .int16_t, .zig_f16 => 2,
393 .uint32_t, .int32_t, .zig_f32 => 4,
394 .uint64_t, .int64_t, .zig_f64 => 8,
395 .zig_u128, .zig_i128, .zig_f128 => 16,
396 .zig_f80 => if (target.cTypeBitSize(.longdouble) == 80)
397 target.cTypeByteSize(.longdouble)
398 else
399 16,
400 .zig_c_longdouble => target.cTypeByteSize(.longdouble),
401 .va_list => unreachable,
402 _ => unreachable,
403 },
404 .pointer => @divExact(target.ptrBitWidth(), 8),
405 .array, .vector => |sequence_info| sequence_info.elem_ctype.byteSize(pool, mod) * sequence_info.len,
406 else => unreachable,
407 };
408}
409
410pub fn info(ctype: CType, pool: *const Pool) Info {
411 const pool_index = ctype.toPoolIndex() orelse return .{ .basic = ctype.index };
412 const item = pool.items.get(pool_index);
413 switch (item.tag) {
414 .basic => unreachable,
415 .pointer => return .{ .pointer = .{
416 .elem_ctype = .{ .index = @enumFromInt(item.data) },
417 } },
418 .pointer_const => return .{ .pointer = .{
419 .elem_ctype = .{ .index = @enumFromInt(item.data) },
420 .@"const" = true,
421 } },
422 .pointer_volatile => return .{ .pointer = .{
423 .elem_ctype = .{ .index = @enumFromInt(item.data) },
424 .@"volatile" = true,
425 } },
426 .pointer_const_volatile => return .{ .pointer = .{
427 .elem_ctype = .{ .index = @enumFromInt(item.data) },
428 .@"const" = true,
429 .@"volatile" = true,
430 } },
431 .aligned => {
432 const extra = pool.getExtra(Pool.Aligned, item.data);
433 return .{ .aligned = .{
434 .ctype = .{ .index = extra.ctype },
435 .alignas = extra.flags.alignas,
436 } };
437 },
438 .array_small => {
439 const extra = pool.getExtra(Pool.SequenceSmall, item.data);
440 return .{ .array = .{
441 .elem_ctype = .{ .index = extra.elem_ctype },
442 .len = extra.len,
443 } };
444 },
445 .array_large => {
446 const extra = pool.getExtra(Pool.SequenceLarge, item.data);
447 return .{ .array = .{
448 .elem_ctype = .{ .index = extra.elem_ctype },
449 .len = extra.len(),
450 } };
451 },
452 .vector => {
453 const extra = pool.getExtra(Pool.SequenceSmall, item.data);
454 return .{ .vector = .{
455 .elem_ctype = .{ .index = extra.elem_ctype },
456 .len = extra.len,
457 } };
458 },
459 .nonstring => {
460 var child_info = info(.{ .index = @enumFromInt(item.data) }, pool);
461 switch (child_info) {
462 else => unreachable,
463 .pointer => |*pointer_info| pointer_info.nonstring = true,
464 .array, .vector => |*sequence_info| sequence_info.nonstring = true,
465 }
466 return child_info;
467 },
468 .fwd_decl_struct_anon => {
469 const extra_trail = pool.getExtraTrail(Pool.FwdDeclAnon, item.data);
470 return .{ .fwd_decl = .{
471 .tag = .@"struct",
472 .name = .{ .anon = .{
473 .extra_index = extra_trail.trail.extra_index,
474 .len = extra_trail.extra.fields_len,
475 } },
476 } };
477 },
478 .fwd_decl_union_anon => {
479 const extra_trail = pool.getExtraTrail(Pool.FwdDeclAnon, item.data);
480 return .{ .fwd_decl = .{
481 .tag = .@"union",
482 .name = .{ .anon = .{
483 .extra_index = extra_trail.trail.extra_index,
484 .len = extra_trail.extra.fields_len,
485 } },
486 } };
487 },
488 .fwd_decl_struct => return .{ .fwd_decl = .{
489 .tag = .@"struct",
490 .name = .{ .index = @enumFromInt(item.data) },
491 } },
492 .fwd_decl_union => return .{ .fwd_decl = .{
493 .tag = .@"union",
494 .name = .{ .index = @enumFromInt(item.data) },
495 } },
496 .aggregate_struct_anon => {
497 const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data);
498 return .{ .aggregate = .{
499 .tag = .@"struct",
500 .name = .{ .anon = .{
501 .index = extra_trail.extra.index,
502 .id = extra_trail.extra.id,
503 } },
504 .fields = .{
505 .extra_index = extra_trail.trail.extra_index,
506 .len = extra_trail.extra.fields_len,
507 },
508 } };
509 },
510 .aggregate_union_anon => {
511 const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data);
512 return .{ .aggregate = .{
513 .tag = .@"union",
514 .name = .{ .anon = .{
515 .index = extra_trail.extra.index,
516 .id = extra_trail.extra.id,
517 } },
518 .fields = .{
519 .extra_index = extra_trail.trail.extra_index,
520 .len = extra_trail.extra.fields_len,
521 },
522 } };
523 },
524 .aggregate_struct_packed_anon => {
525 const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data);
526 return .{ .aggregate = .{
527 .tag = .@"struct",
528 .@"packed" = true,
529 .name = .{ .anon = .{
530 .index = extra_trail.extra.index,
531 .id = extra_trail.extra.id,
532 } },
533 .fields = .{
534 .extra_index = extra_trail.trail.extra_index,
535 .len = extra_trail.extra.fields_len,
536 },
537 } };
538 },
539 .aggregate_union_packed_anon => {
540 const extra_trail = pool.getExtraTrail(Pool.AggregateAnon, item.data);
541 return .{ .aggregate = .{
542 .tag = .@"union",
543 .@"packed" = true,
544 .name = .{ .anon = .{
545 .index = extra_trail.extra.index,
546 .id = extra_trail.extra.id,
547 } },
548 .fields = .{
549 .extra_index = extra_trail.trail.extra_index,
550 .len = extra_trail.extra.fields_len,
551 },
552 } };
553 },
554 .aggregate_struct => {
555 const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data);
556 return .{ .aggregate = .{
557 .tag = .@"struct",
558 .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } },
559 .fields = .{
560 .extra_index = extra_trail.trail.extra_index,
561 .len = extra_trail.extra.fields_len,
562 },
563 } };
564 },
565 .aggregate_union => {
566 const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data);
567 return .{ .aggregate = .{
568 .tag = .@"union",
569 .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } },
570 .fields = .{
571 .extra_index = extra_trail.trail.extra_index,
572 .len = extra_trail.extra.fields_len,
573 },
574 } };
575 },
576 .aggregate_struct_packed => {
577 const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data);
578 return .{ .aggregate = .{
579 .tag = .@"struct",
580 .@"packed" = true,
581 .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } },
582 .fields = .{
583 .extra_index = extra_trail.trail.extra_index,
584 .len = extra_trail.extra.fields_len,
585 },
586 } };
587 },
588 .aggregate_union_packed => {
589 const extra_trail = pool.getExtraTrail(Pool.Aggregate, item.data);
590 return .{ .aggregate = .{
591 .tag = .@"union",
592 .@"packed" = true,
593 .name = .{ .fwd_decl = .{ .index = extra_trail.extra.fwd_decl } },
594 .fields = .{
595 .extra_index = extra_trail.trail.extra_index,
596 .len = extra_trail.extra.fields_len,
597 },
598 } };
599 },
600 .function => {
601 const extra_trail = pool.getExtraTrail(Pool.Function, item.data);
602 return .{ .function = .{
603 .return_ctype = .{ .index = extra_trail.extra.return_ctype },
604 .param_ctypes = .{
605 .extra_index = extra_trail.trail.extra_index,
606 .len = extra_trail.extra.param_ctypes_len,
607 },
608 .varargs = false,
609 } };
610 },
611 .function_varargs => {
612 const extra_trail = pool.getExtraTrail(Pool.Function, item.data);
613 return .{ .function = .{
614 .return_ctype = .{ .index = extra_trail.extra.return_ctype },
615 .param_ctypes = .{
616 .extra_index = extra_trail.trail.extra_index,
617 .len = extra_trail.extra.param_ctypes_len,
618 },
619 .varargs = true,
620 } };
621 },
622 }
623}
624
625pub fn hash(ctype: CType, pool: *const Pool) Pool.Map.Hash {
626 return if (ctype.toPoolIndex()) |pool_index|
627 pool.map.entries.items(.hash)[pool_index]
628 else
629 CType.Index.basic_hashes[@intFromEnum(ctype.index)];
630}
631
632fn toForward(ctype: CType, pool: *Pool, allocator: std.mem.Allocator) !CType {
633 return switch (ctype.info(pool)) {
634 .basic, .pointer, .fwd_decl => ctype,
635 .aligned => |aligned_info| pool.getAligned(allocator, .{
636 .ctype = try aligned_info.ctype.toForward(pool, allocator),
637 .alignas = aligned_info.alignas,
638 }),
639 .array => |array_info| pool.getArray(allocator, .{
640 .elem_ctype = try array_info.elem_ctype.toForward(pool, allocator),
641 .len = array_info.len,
642 .nonstring = array_info.nonstring,
643 }),
644 .vector => |vector_info| pool.getVector(allocator, .{
645 .elem_ctype = try vector_info.elem_ctype.toForward(pool, allocator),
646 .len = vector_info.len,
647 .nonstring = vector_info.nonstring,
648 }),
649 .aggregate => |aggregate_info| switch (aggregate_info.name) {
650 .anon => ctype,
651 .fwd_decl => |fwd_decl| fwd_decl,
652 },
653 .function => unreachable,
654 };
655}
656
657const Index = enum(u32) {
658 void,
659
660 // C basic types
661 char,
662
663 @"signed char",
664 short,
665 int,
666 long,
667 @"long long",
668
669 _Bool,
670 @"unsigned char",
671 @"unsigned short",
672 @"unsigned int",
673 @"unsigned long",
674 @"unsigned long long",
675
676 float,
677 double,
678 @"long double",
679
680 // C header types
681 // - stdbool.h
682 bool,
683 // - stddef.h
684 size_t,
685 ptrdiff_t,
686 // - stdint.h
687 uint8_t,
688 int8_t,
689 uint16_t,
690 int16_t,
691 uint32_t,
692 int32_t,
693 uint64_t,
694 int64_t,
695 uintptr_t,
696 intptr_t,
697 // - stdarg.h
698 va_list,
699
700 // zig.h types
701 zig_u128,
702 zig_i128,
703 zig_f16,
704 zig_f32,
705 zig_f64,
706 zig_f80,
707 zig_f128,
708 zig_c_longdouble,
709
710 _,
711
712 const first_pool_index: u32 = @typeInfo(CType.Index).@"enum".fields.len;
713 const basic_hashes = init: {
714 @setEvalBranchQuota(1_600);
715 var basic_hashes_init: [first_pool_index]Pool.Map.Hash = undefined;
716 for (&basic_hashes_init, 0..) |*basic_hash, index| {
717 const ctype_index: CType.Index = @enumFromInt(index);
718 var hasher = Pool.Hasher.init;
719 hasher.update(@intFromEnum(ctype_index));
720 basic_hash.* = hasher.final(.basic);
721 }
722 break :init basic_hashes_init;
723 };
724};
725
726const Slice = struct {
727 extra_index: Pool.ExtraIndex,
728 len: u32,
729
730 pub fn at(slice: CType.Slice, index: usize, pool: *const Pool) CType {
731 var extra: Pool.ExtraTrail = .{ .extra_index = slice.extra_index };
732 return .{ .index = extra.next(slice.len, CType.Index, pool)[index] };
733 }
734};
735
736pub const Kind = enum {
737 forward,
738 forward_parameter,
739 complete,
740 global,
741 parameter,
742
743 pub fn isForward(kind: Kind) bool {
744 return switch (kind) {
745 .forward, .forward_parameter => true,
746 .complete, .global, .parameter => false,
747 };
748 }
749
750 pub fn isParameter(kind: Kind) bool {
751 return switch (kind) {
752 .forward_parameter, .parameter => true,
753 .forward, .complete, .global => false,
754 };
755 }
756
757 pub fn asParameter(kind: Kind) Kind {
758 return switch (kind) {
759 .forward, .forward_parameter => .forward_parameter,
760 .complete, .parameter, .global => .parameter,
761 };
762 }
763
764 pub fn noParameter(kind: Kind) Kind {
765 return switch (kind) {
766 .forward, .forward_parameter => .forward,
767 .complete, .parameter => .complete,
768 .global => .global,
769 };
770 }
771
772 pub fn asComplete(kind: Kind) Kind {
773 return switch (kind) {
774 .forward, .complete => .complete,
775 .forward_parameter, .parameter => .parameter,
776 .global => .global,
777 };
778 }
779};
780
781pub const Info = union(enum) {
782 basic: CType.Index,
783 pointer: Pointer,
784 aligned: Aligned,
785 array: Sequence,
786 vector: Sequence,
787 fwd_decl: FwdDecl,
788 aggregate: Aggregate,
789 function: Function,
790
791 const Tag = @typeInfo(Info).@"union".tag_type.?;
792
793 pub const Pointer = struct {
794 elem_ctype: CType,
795 @"const": bool = false,
796 @"volatile": bool = false,
797 nonstring: bool = false,
798
799 fn tag(pointer_info: Pointer) Pool.Tag {
800 return @enumFromInt(@intFromEnum(Pool.Tag.pointer) +
801 @as(u2, @bitCast(packed struct(u2) {
802 @"const": bool,
803 @"volatile": bool,
804 }{
805 .@"const" = pointer_info.@"const",
806 .@"volatile" = pointer_info.@"volatile",
807 })));
808 }
809 };
810
811 pub const Aligned = struct {
812 ctype: CType,
813 alignas: AlignAs,
814 };
815
816 pub const Sequence = struct {
817 elem_ctype: CType,
818 len: u64,
819 nonstring: bool = false,
820 };
821
822 pub const AggregateTag = enum { @"enum", @"struct", @"union" };
823
824 pub const Field = struct {
825 name: Pool.String,
826 ctype: CType,
827 alignas: AlignAs,
828
829 pub const Slice = struct {
830 extra_index: Pool.ExtraIndex,
831 len: u32,
832
833 pub fn at(slice: Field.Slice, index: usize, pool: *const Pool) Field {
834 assert(index < slice.len);
835 const extra = pool.getExtra(Pool.Field, @intCast(slice.extra_index +
836 index * @typeInfo(Pool.Field).@"struct".fields.len));
837 return .{
838 .name = .{ .index = extra.name },
839 .ctype = .{ .index = extra.ctype },
840 .alignas = extra.flags.alignas,
841 };
842 }
843
844 fn eqlAdapted(
845 lhs_slice: Field.Slice,
846 lhs_pool: *const Pool,
847 rhs_slice: Field.Slice,
848 rhs_pool: *const Pool,
849 pool_adapter: anytype,
850 ) bool {
851 if (lhs_slice.len != rhs_slice.len) return false;
852 for (0..lhs_slice.len) |index| {
853 if (!lhs_slice.at(index, lhs_pool).eqlAdapted(
854 lhs_pool,
855 rhs_slice.at(index, rhs_pool),
856 rhs_pool,
857 pool_adapter,
858 )) return false;
859 }
860 return true;
861 }
862 };
863
864 fn eqlAdapted(
865 lhs_field: Field,
866 lhs_pool: *const Pool,
867 rhs_field: Field,
868 rhs_pool: *const Pool,
869 pool_adapter: anytype,
870 ) bool {
871 if (!std.meta.eql(lhs_field.alignas, rhs_field.alignas)) return false;
872 if (!pool_adapter.eql(lhs_field.ctype, rhs_field.ctype)) return false;
873 return if (lhs_field.name.toPoolSlice(lhs_pool)) |lhs_name|
874 if (rhs_field.name.toPoolSlice(rhs_pool)) |rhs_name|
875 std.mem.eql(u8, lhs_name, rhs_name)
876 else
877 false
878 else
879 lhs_field.name.index == rhs_field.name.index;
880 }
881 };
882
883 pub const FwdDecl = struct {
884 tag: AggregateTag,
885 name: union(enum) {
886 anon: Field.Slice,
887 index: InternPool.Index,
888 },
889 };
890
891 pub const Aggregate = struct {
892 tag: AggregateTag,
893 @"packed": bool = false,
894 name: union(enum) {
895 anon: struct {
896 index: InternPool.Index,
897 id: u32,
898 },
899 fwd_decl: CType,
900 },
901 fields: Field.Slice,
902 };
903
904 pub const Function = struct {
905 return_ctype: CType,
906 param_ctypes: CType.Slice,
907 varargs: bool = false,
908 };
909
910 pub fn eqlAdapted(
911 lhs_info: Info,
912 lhs_pool: *const Pool,
913 rhs_ctype: CType,
914 rhs_pool: *const Pool,
915 pool_adapter: anytype,
916 ) bool {
917 const rhs_info = rhs_ctype.info(rhs_pool);
918 if (@as(Info.Tag, lhs_info) != @as(Info.Tag, rhs_info)) return false;
919 return switch (lhs_info) {
920 .basic => |lhs_basic_info| lhs_basic_info == rhs_info.basic,
921 .pointer => |lhs_pointer_info| lhs_pointer_info.@"const" == rhs_info.pointer.@"const" and
922 lhs_pointer_info.@"volatile" == rhs_info.pointer.@"volatile" and
923 lhs_pointer_info.nonstring == rhs_info.pointer.nonstring and
924 pool_adapter.eql(lhs_pointer_info.elem_ctype, rhs_info.pointer.elem_ctype),
925 .aligned => |lhs_aligned_info| std.meta.eql(lhs_aligned_info.alignas, rhs_info.aligned.alignas) and
926 pool_adapter.eql(lhs_aligned_info.ctype, rhs_info.aligned.ctype),
927 .array => |lhs_array_info| lhs_array_info.len == rhs_info.array.len and
928 lhs_array_info.nonstring == rhs_info.array.nonstring and
929 pool_adapter.eql(lhs_array_info.elem_ctype, rhs_info.array.elem_ctype),
930 .vector => |lhs_vector_info| lhs_vector_info.len == rhs_info.vector.len and
931 lhs_vector_info.nonstring == rhs_info.vector.nonstring and
932 pool_adapter.eql(lhs_vector_info.elem_ctype, rhs_info.vector.elem_ctype),
933 .fwd_decl => |lhs_fwd_decl_info| lhs_fwd_decl_info.tag == rhs_info.fwd_decl.tag and
934 switch (lhs_fwd_decl_info.name) {
935 .anon => |lhs_anon| rhs_info.fwd_decl.name == .anon and lhs_anon.eqlAdapted(
936 lhs_pool,
937 rhs_info.fwd_decl.name.anon,
938 rhs_pool,
939 pool_adapter,
940 ),
941 .index => |lhs_index| rhs_info.fwd_decl.name == .index and
942 lhs_index == rhs_info.fwd_decl.name.index,
943 },
944 .aggregate => |lhs_aggregate_info| lhs_aggregate_info.tag == rhs_info.aggregate.tag and
945 lhs_aggregate_info.@"packed" == rhs_info.aggregate.@"packed" and
946 switch (lhs_aggregate_info.name) {
947 .anon => |lhs_anon| rhs_info.aggregate.name == .anon and
948 lhs_anon.index == rhs_info.aggregate.name.anon.index and
949 lhs_anon.id == rhs_info.aggregate.name.anon.id,
950 .fwd_decl => |lhs_fwd_decl| rhs_info.aggregate.name == .fwd_decl and
951 pool_adapter.eql(lhs_fwd_decl, rhs_info.aggregate.name.fwd_decl),
952 } and lhs_aggregate_info.fields.eqlAdapted(
953 lhs_pool,
954 rhs_info.aggregate.fields,
955 rhs_pool,
956 pool_adapter,
957 ),
958 .function => |lhs_function_info| lhs_function_info.param_ctypes.len ==
959 rhs_info.function.param_ctypes.len and
960 pool_adapter.eql(lhs_function_info.return_ctype, rhs_info.function.return_ctype) and
961 for (0..lhs_function_info.param_ctypes.len) |param_index| {
962 if (!pool_adapter.eql(
963 lhs_function_info.param_ctypes.at(param_index, lhs_pool),
964 rhs_info.function.param_ctypes.at(param_index, rhs_pool),
965 )) break false;
966 } else true,
967 };
968 }
969};
970
971pub const Pool = struct {
972 map: Map,
973 items: std.MultiArrayList(Item),
974 extra: std.ArrayList(u32),
975
976 string_map: Map,
977 string_indices: std.ArrayList(u32),
978 string_bytes: std.ArrayList(u8),
979
980 const Map = std.AutoArrayHashMapUnmanaged(void, void);
981
982 pub const String = struct {
983 index: String.Index,
984
985 const FormatData = struct { string: String, pool: *const Pool };
986 fn format(data: FormatData, writer: *Writer) Writer.Error!void {
987 if (data.string.toSlice(data.pool)) |slice|
988 try writer.writeAll(slice)
989 else
990 try writer.print("f{d}", .{@intFromEnum(data.string.index)});
991 }
992 pub fn fmt(str: String, pool: *const Pool) std.fmt.Alt(FormatData, format) {
993 return .{ .data = .{ .string = str, .pool = pool } };
994 }
995
996 fn fromUnnamed(index: u31) String {
997 return .{ .index = @enumFromInt(index) };
998 }
999
1000 fn isNamed(str: String) bool {
1001 return @intFromEnum(str.index) >= String.Index.first_named_index;
1002 }
1003
1004 pub fn toSlice(str: String, pool: *const Pool) ?[]const u8 {
1005 return str.toPoolSlice(pool) orelse if (str.isNamed()) @tagName(str.index) else null;
1006 }
1007
1008 fn toPoolSlice(str: String, pool: *const Pool) ?[]const u8 {
1009 if (str.toPoolIndex()) |pool_index| {
1010 const start = pool.string_indices.items[pool_index + 0];
1011 const end = pool.string_indices.items[pool_index + 1];
1012 return pool.string_bytes.items[start..end];
1013 } else return null;
1014 }
1015
1016 fn fromPoolIndex(pool_index: usize) String {
1017 return .{ .index = @enumFromInt(String.Index.first_pool_index + pool_index) };
1018 }
1019
1020 fn toPoolIndex(str: String) ?u32 {
1021 const pool_index, const is_null =
1022 @subWithOverflow(@intFromEnum(str.index), String.Index.first_pool_index);
1023 return switch (is_null) {
1024 0 => pool_index,
1025 1 => null,
1026 };
1027 }
1028
1029 const Index = enum(u32) {
1030 array = first_named_index,
1031 @"error",
1032 is_null,
1033 len,
1034 payload,
1035 ptr,
1036 tag,
1037 _,
1038
1039 const first_named_index: u32 = 1 << 31;
1040 const first_pool_index: u32 = first_named_index + @typeInfo(String.Index).@"enum".fields.len;
1041 };
1042
1043 const Adapter = struct {
1044 pool: *const Pool,
1045 pub fn hash(_: @This(), slice: []const u8) Map.Hash {
1046 return @truncate(Hasher.Impl.hash(1, slice));
1047 }
1048 pub fn eql(string_adapter: @This(), lhs_slice: []const u8, _: void, rhs_index: usize) bool {
1049 const rhs_string = String.fromPoolIndex(rhs_index);
1050 const rhs_slice = rhs_string.toPoolSlice(string_adapter.pool).?;
1051 return std.mem.eql(u8, lhs_slice, rhs_slice);
1052 }
1053 };
1054 };
1055
1056 pub const empty: Pool = .{
1057 .map = .empty,
1058 .items = .empty,
1059 .extra = .empty,
1060
1061 .string_map = .empty,
1062 .string_indices = .empty,
1063 .string_bytes = .empty,
1064 };
1065
1066 pub fn init(pool: *Pool, allocator: std.mem.Allocator) !void {
1067 if (pool.string_indices.items.len == 0)
1068 try pool.string_indices.append(allocator, 0);
1069 }
1070
1071 pub fn deinit(pool: *Pool, allocator: std.mem.Allocator) void {
1072 pool.map.deinit(allocator);
1073 pool.items.deinit(allocator);
1074 pool.extra.deinit(allocator);
1075
1076 pool.string_map.deinit(allocator);
1077 pool.string_indices.deinit(allocator);
1078 pool.string_bytes.deinit(allocator);
1079
1080 pool.* = undefined;
1081 }
1082
1083 pub fn move(pool: *Pool) Pool {
1084 defer pool.* = empty;
1085 return pool.*;
1086 }
1087
1088 pub fn clearRetainingCapacity(pool: *Pool) void {
1089 pool.map.clearRetainingCapacity();
1090 pool.items.shrinkRetainingCapacity(0);
1091 pool.extra.clearRetainingCapacity();
1092
1093 pool.string_map.clearRetainingCapacity();
1094 pool.string_indices.shrinkRetainingCapacity(1);
1095 pool.string_bytes.clearRetainingCapacity();
1096 }
1097
1098 pub fn freeUnusedCapacity(pool: *Pool, allocator: std.mem.Allocator) void {
1099 pool.map.shrinkAndFree(allocator, pool.map.count());
1100 pool.items.shrinkAndFree(allocator, pool.items.len);
1101 pool.extra.shrinkAndFree(allocator, pool.extra.items.len);
1102
1103 pool.string_map.shrinkAndFree(allocator, pool.string_map.count());
1104 pool.string_indices.shrinkAndFree(allocator, pool.string_indices.items.len);
1105 pool.string_bytes.shrinkAndFree(allocator, pool.string_bytes.items.len);
1106 }
1107
1108 pub fn getPointer(pool: *Pool, allocator: std.mem.Allocator, pointer_info: Info.Pointer) !CType {
1109 var hasher = Hasher.init;
1110 hasher.update(pointer_info.elem_ctype.hash(pool));
1111 return pool.getNonString(allocator, try pool.tagData(
1112 allocator,
1113 hasher,
1114 pointer_info.tag(),
1115 @intFromEnum(pointer_info.elem_ctype.index),
1116 ), pointer_info.nonstring);
1117 }
1118
1119 pub fn getAligned(pool: *Pool, allocator: std.mem.Allocator, aligned_info: Info.Aligned) !CType {
1120 return pool.tagExtra(allocator, .aligned, Aligned, .{
1121 .ctype = aligned_info.ctype.index,
1122 .flags = .{ .alignas = aligned_info.alignas },
1123 });
1124 }
1125
1126 pub fn getArray(pool: *Pool, allocator: std.mem.Allocator, array_info: Info.Sequence) !CType {
1127 return pool.getNonString(allocator, if (std.math.cast(u32, array_info.len)) |small_len|
1128 try pool.tagExtra(allocator, .array_small, SequenceSmall, .{
1129 .elem_ctype = array_info.elem_ctype.index,
1130 .len = small_len,
1131 })
1132 else
1133 try pool.tagExtra(allocator, .array_large, SequenceLarge, .{
1134 .elem_ctype = array_info.elem_ctype.index,
1135 .len_lo = @truncate(array_info.len >> 0),
1136 .len_hi = @truncate(array_info.len >> 32),
1137 }), array_info.nonstring);
1138 }
1139
1140 pub fn getVector(pool: *Pool, allocator: std.mem.Allocator, vector_info: Info.Sequence) !CType {
1141 return pool.getNonString(allocator, try pool.tagExtra(allocator, .vector, SequenceSmall, .{
1142 .elem_ctype = vector_info.elem_ctype.index,
1143 .len = @intCast(vector_info.len),
1144 }), vector_info.nonstring);
1145 }
1146
1147 pub fn getNonString(
1148 pool: *Pool,
1149 allocator: std.mem.Allocator,
1150 child_ctype: CType,
1151 nonstring: bool,
1152 ) !CType {
1153 if (!nonstring) return child_ctype;
1154 var hasher = Hasher.init;
1155 hasher.update(child_ctype.hash(pool));
1156 return pool.tagData(allocator, hasher, .nonstring, @intFromEnum(child_ctype.index));
1157 }
1158
1159 pub fn getFwdDecl(
1160 pool: *Pool,
1161 allocator: std.mem.Allocator,
1162 fwd_decl_info: struct {
1163 tag: Info.AggregateTag,
1164 name: union(enum) {
1165 anon: []const Info.Field,
1166 index: InternPool.Index,
1167 },
1168 },
1169 ) !CType {
1170 var hasher = Hasher.init;
1171 switch (fwd_decl_info.name) {
1172 .anon => |fields| {
1173 const ExpectedContents = [32]CType;
1174 var stack align(@max(
1175 @alignOf(std.heap.StackFallbackAllocator(0)),
1176 @alignOf(ExpectedContents),
1177 )) = std.heap.stackFallback(@sizeOf(ExpectedContents), allocator);
1178 const stack_allocator = stack.get();
1179 const field_ctypes = try stack_allocator.alloc(CType, fields.len);
1180 defer stack_allocator.free(field_ctypes);
1181 for (field_ctypes, fields) |*field_ctype, field|
1182 field_ctype.* = try field.ctype.toForward(pool, allocator);
1183 const extra: FwdDeclAnon = .{ .fields_len = @intCast(fields.len) };
1184 const extra_index = try pool.addExtra(
1185 allocator,
1186 FwdDeclAnon,
1187 extra,
1188 fields.len * @typeInfo(Field).@"struct".fields.len,
1189 );
1190 for (fields, field_ctypes) |field, field_ctype| pool.addHashedExtraAssumeCapacity(
1191 &hasher,
1192 Field,
1193 .{
1194 .name = field.name.index,
1195 .ctype = field_ctype.index,
1196 .flags = .{ .alignas = field.alignas },
1197 },
1198 );
1199 hasher.updateExtra(FwdDeclAnon, extra, pool);
1200 return pool.tagTrailingExtra(allocator, hasher, switch (fwd_decl_info.tag) {
1201 .@"struct" => .fwd_decl_struct_anon,
1202 .@"union" => .fwd_decl_union_anon,
1203 .@"enum" => unreachable,
1204 }, extra_index);
1205 },
1206 .index => |index| {
1207 hasher.update(index);
1208 return pool.tagData(allocator, hasher, switch (fwd_decl_info.tag) {
1209 .@"struct" => .fwd_decl_struct,
1210 .@"union" => .fwd_decl_union,
1211 .@"enum" => unreachable,
1212 }, @intFromEnum(index));
1213 },
1214 }
1215 }
1216
1217 pub fn getAggregate(
1218 pool: *Pool,
1219 allocator: std.mem.Allocator,
1220 aggregate_info: struct {
1221 tag: Info.AggregateTag,
1222 @"packed": bool = false,
1223 name: union(enum) {
1224 anon: struct {
1225 index: InternPool.Index,
1226 id: u32,
1227 },
1228 fwd_decl: CType,
1229 },
1230 fields: []const Info.Field,
1231 },
1232 ) !CType {
1233 var hasher = Hasher.init;
1234 switch (aggregate_info.name) {
1235 .anon => |anon| {
1236 const extra: AggregateAnon = .{
1237 .index = anon.index,
1238 .id = anon.id,
1239 .fields_len = @intCast(aggregate_info.fields.len),
1240 };
1241 const extra_index = try pool.addExtra(
1242 allocator,
1243 AggregateAnon,
1244 extra,
1245 aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len,
1246 );
1247 for (aggregate_info.fields) |field| pool.addHashedExtraAssumeCapacity(&hasher, Field, .{
1248 .name = field.name.index,
1249 .ctype = field.ctype.index,
1250 .flags = .{ .alignas = field.alignas },
1251 });
1252 hasher.updateExtra(AggregateAnon, extra, pool);
1253 return pool.tagTrailingExtra(allocator, hasher, switch (aggregate_info.tag) {
1254 .@"struct" => switch (aggregate_info.@"packed") {
1255 false => .aggregate_struct_anon,
1256 true => .aggregate_struct_packed_anon,
1257 },
1258 .@"union" => switch (aggregate_info.@"packed") {
1259 false => .aggregate_union_anon,
1260 true => .aggregate_union_packed_anon,
1261 },
1262 .@"enum" => unreachable,
1263 }, extra_index);
1264 },
1265 .fwd_decl => |fwd_decl| {
1266 const extra: Aggregate = .{
1267 .fwd_decl = fwd_decl.index,
1268 .fields_len = @intCast(aggregate_info.fields.len),
1269 };
1270 const extra_index = try pool.addExtra(
1271 allocator,
1272 Aggregate,
1273 extra,
1274 aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len,
1275 );
1276 for (aggregate_info.fields) |field| pool.addHashedExtraAssumeCapacity(&hasher, Field, .{
1277 .name = field.name.index,
1278 .ctype = field.ctype.index,
1279 .flags = .{ .alignas = field.alignas },
1280 });
1281 hasher.updateExtra(Aggregate, extra, pool);
1282 return pool.tagTrailingExtra(allocator, hasher, switch (aggregate_info.tag) {
1283 .@"struct" => switch (aggregate_info.@"packed") {
1284 false => .aggregate_struct,
1285 true => .aggregate_struct_packed,
1286 },
1287 .@"union" => switch (aggregate_info.@"packed") {
1288 false => .aggregate_union,
1289 true => .aggregate_union_packed,
1290 },
1291 .@"enum" => unreachable,
1292 }, extra_index);
1293 },
1294 }
1295 }
1296
1297 pub fn getFunction(
1298 pool: *Pool,
1299 allocator: std.mem.Allocator,
1300 function_info: struct {
1301 return_ctype: CType,
1302 param_ctypes: []const CType,
1303 varargs: bool = false,
1304 },
1305 ) !CType {
1306 var hasher = Hasher.init;
1307 const extra: Function = .{
1308 .return_ctype = function_info.return_ctype.index,
1309 .param_ctypes_len = @intCast(function_info.param_ctypes.len),
1310 };
1311 const extra_index = try pool.addExtra(allocator, Function, extra, function_info.param_ctypes.len);
1312 for (function_info.param_ctypes) |param_ctype| {
1313 hasher.update(param_ctype.hash(pool));
1314 pool.extra.appendAssumeCapacity(@intFromEnum(param_ctype.index));
1315 }
1316 hasher.updateExtra(Function, extra, pool);
1317 return pool.tagTrailingExtra(allocator, hasher, switch (function_info.varargs) {
1318 false => .function,
1319 true => .function_varargs,
1320 }, extra_index);
1321 }
1322
1323 pub fn fromFields(
1324 pool: *Pool,
1325 allocator: std.mem.Allocator,
1326 tag: Info.AggregateTag,
1327 fields: []Info.Field,
1328 kind: Kind,
1329 ) !CType {
1330 sortFields(fields);
1331 const fwd_decl = try pool.getFwdDecl(allocator, .{
1332 .tag = tag,
1333 .name = .{ .anon = fields },
1334 });
1335 return if (kind.isForward()) fwd_decl else pool.getAggregate(allocator, .{
1336 .tag = tag,
1337 .name = .{ .fwd_decl = fwd_decl },
1338 .fields = fields,
1339 });
1340 }
1341
1342 pub fn fromIntInfo(
1343 pool: *Pool,
1344 allocator: std.mem.Allocator,
1345 int_info: std.builtin.Type.Int,
1346 mod: *Module,
1347 kind: Kind,
1348 ) !CType {
1349 switch (int_info.bits) {
1350 0 => return .void,
1351 1...8 => switch (int_info.signedness) {
1352 .signed => return .i8,
1353 .unsigned => return .u8,
1354 },
1355 9...16 => switch (int_info.signedness) {
1356 .signed => return .i16,
1357 .unsigned => return .u16,
1358 },
1359 17...32 => switch (int_info.signedness) {
1360 .signed => return .i32,
1361 .unsigned => return .u32,
1362 },
1363 33...64 => switch (int_info.signedness) {
1364 .signed => return .i64,
1365 .unsigned => return .u64,
1366 },
1367 65...128 => switch (int_info.signedness) {
1368 .signed => return .i128,
1369 .unsigned => return .u128,
1370 },
1371 else => {
1372 const target = &mod.resolved_target.result;
1373 const abi_align_bytes = std.zig.target.intAlignment(target, int_info.bits);
1374 const limb_ctype = try pool.fromIntInfo(allocator, .{
1375 .signedness = .unsigned,
1376 .bits = @intCast(abi_align_bytes * 8),
1377 }, mod, kind.noParameter());
1378 const array_ctype = try pool.getArray(allocator, .{
1379 .len = @divExact(std.zig.target.intByteSize(target, int_info.bits), abi_align_bytes),
1380 .elem_ctype = limb_ctype,
1381 .nonstring = limb_ctype.isAnyChar(),
1382 });
1383 if (!kind.isParameter()) return array_ctype;
1384 var fields = [_]Info.Field{
1385 .{
1386 .name = .{ .index = .array },
1387 .ctype = array_ctype,
1388 .alignas = AlignAs.fromAbiAlignment(.fromByteUnits(abi_align_bytes)),
1389 },
1390 };
1391 return pool.fromFields(allocator, .@"struct", &fields, kind);
1392 },
1393 }
1394 }
1395
1396 pub fn fromType(
1397 pool: *Pool,
1398 allocator: std.mem.Allocator,
1399 scratch: *std.ArrayList(u32),
1400 ty: Type,
1401 pt: Zcu.PerThread,
1402 mod: *Module,
1403 kind: Kind,
1404 ) !CType {
1405 const ip = &pt.zcu.intern_pool;
1406 const zcu = pt.zcu;
1407 switch (ty.toIntern()) {
1408 .u0_type,
1409 .i0_type,
1410 .anyopaque_type,
1411 .void_type,
1412 .empty_tuple_type,
1413 .type_type,
1414 .comptime_int_type,
1415 .comptime_float_type,
1416 .null_type,
1417 .undefined_type,
1418 .enum_literal_type,
1419 .optional_type_type,
1420 .manyptr_const_type_type,
1421 .slice_const_type_type,
1422 => return .void,
1423 .u1_type, .u8_type => return .u8,
1424 .i8_type => return .i8,
1425 .u16_type => return .u16,
1426 .i16_type => return .i16,
1427 .u29_type, .u32_type => return .u32,
1428 .i32_type => return .i32,
1429 .u64_type => return .u64,
1430 .i64_type => return .i64,
1431 .u80_type, .u128_type => return .u128,
1432 .i128_type => return .i128,
1433 .u256_type => return pool.fromIntInfo(allocator, .{
1434 .signedness = .unsigned,
1435 .bits = 256,
1436 }, mod, kind),
1437 .usize_type => return .usize,
1438 .isize_type => return .isize,
1439 .c_char_type => return .{ .index = .char },
1440 .c_short_type => return .{ .index = .short },
1441 .c_ushort_type => return .{ .index = .@"unsigned short" },
1442 .c_int_type => return .{ .index = .int },
1443 .c_uint_type => return .{ .index = .@"unsigned int" },
1444 .c_long_type => return .{ .index = .long },
1445 .c_ulong_type => return .{ .index = .@"unsigned long" },
1446 .c_longlong_type => return .{ .index = .@"long long" },
1447 .c_ulonglong_type => return .{ .index = .@"unsigned long long" },
1448 .c_longdouble_type => return .{ .index = .@"long double" },
1449 .f16_type => return .f16,
1450 .f32_type => return .f32,
1451 .f64_type => return .f64,
1452 .f80_type => return .f80,
1453 .f128_type => return .f128,
1454 .bool_type, .optional_noreturn_type => return .bool,
1455 .noreturn_type,
1456 .anyframe_type,
1457 .generic_poison_type,
1458 => unreachable,
1459 .anyerror_type,
1460 .anyerror_void_error_union_type,
1461 .adhoc_inferred_error_set_type,
1462 => return pool.fromIntInfo(allocator, .{
1463 .signedness = .unsigned,
1464 .bits = pt.zcu.errorSetBits(),
1465 }, mod, kind),
1466
1467 .ptr_usize_type => return pool.getPointer(allocator, .{
1468 .elem_ctype = .usize,
1469 }),
1470 .ptr_const_comptime_int_type => return pool.getPointer(allocator, .{
1471 .elem_ctype = .void,
1472 .@"const" = true,
1473 }),
1474 .manyptr_u8_type => return pool.getPointer(allocator, .{
1475 .elem_ctype = .u8,
1476 .nonstring = true,
1477 }),
1478 .manyptr_const_u8_type => return pool.getPointer(allocator, .{
1479 .elem_ctype = .u8,
1480 .@"const" = true,
1481 .nonstring = true,
1482 }),
1483 .manyptr_const_u8_sentinel_0_type => return pool.getPointer(allocator, .{
1484 .elem_ctype = .u8,
1485 .@"const" = true,
1486 }),
1487 .slice_const_u8_type => {
1488 const target = &mod.resolved_target.result;
1489 var fields = [_]Info.Field{
1490 .{
1491 .name = .{ .index = .ptr },
1492 .ctype = try pool.getPointer(allocator, .{
1493 .elem_ctype = .u8,
1494 .@"const" = true,
1495 .nonstring = true,
1496 }),
1497 .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1498 },
1499 .{
1500 .name = .{ .index = .len },
1501 .ctype = .usize,
1502 .alignas = AlignAs.fromAbiAlignment(
1503 .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1504 ),
1505 },
1506 };
1507 return pool.fromFields(allocator, .@"struct", &fields, kind);
1508 },
1509 .slice_const_u8_sentinel_0_type => {
1510 const target = &mod.resolved_target.result;
1511 var fields = [_]Info.Field{
1512 .{
1513 .name = .{ .index = .ptr },
1514 .ctype = try pool.getPointer(allocator, .{
1515 .elem_ctype = .u8,
1516 .@"const" = true,
1517 }),
1518 .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1519 },
1520 .{
1521 .name = .{ .index = .len },
1522 .ctype = .usize,
1523 .alignas = AlignAs.fromAbiAlignment(
1524 .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1525 ),
1526 },
1527 };
1528 return pool.fromFields(allocator, .@"struct", &fields, kind);
1529 },
1530
1531 .manyptr_const_slice_const_u8_type => {
1532 const target = &mod.resolved_target.result;
1533 var fields: [2]Info.Field = .{
1534 .{
1535 .name = .{ .index = .ptr },
1536 .ctype = try pool.getPointer(allocator, .{
1537 .elem_ctype = .u8,
1538 .@"const" = true,
1539 .nonstring = true,
1540 }),
1541 .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1542 },
1543 .{
1544 .name = .{ .index = .len },
1545 .ctype = .usize,
1546 .alignas = AlignAs.fromAbiAlignment(
1547 .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1548 ),
1549 },
1550 };
1551 const slice_const_u8 = try pool.fromFields(allocator, .@"struct", &fields, kind);
1552 return pool.getPointer(allocator, .{
1553 .elem_ctype = slice_const_u8,
1554 .@"const" = true,
1555 });
1556 },
1557 .slice_const_slice_const_u8_type => {
1558 const target = &mod.resolved_target.result;
1559 var fields: [2]Info.Field = .{
1560 .{
1561 .name = .{ .index = .ptr },
1562 .ctype = try pool.getPointer(allocator, .{
1563 .elem_ctype = .u8,
1564 .@"const" = true,
1565 .nonstring = true,
1566 }),
1567 .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1568 },
1569 .{
1570 .name = .{ .index = .len },
1571 .ctype = .usize,
1572 .alignas = AlignAs.fromAbiAlignment(
1573 .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1574 ),
1575 },
1576 };
1577 const slice_const_u8 = try pool.fromFields(allocator, .@"struct", &fields, .forward);
1578 fields = .{
1579 .{
1580 .name = .{ .index = .ptr },
1581 .ctype = try pool.getPointer(allocator, .{
1582 .elem_ctype = slice_const_u8,
1583 .@"const" = true,
1584 }),
1585 .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
1586 },
1587 .{
1588 .name = .{ .index = .len },
1589 .ctype = .usize,
1590 .alignas = AlignAs.fromAbiAlignment(
1591 .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
1592 ),
1593 },
1594 };
1595 return pool.fromFields(allocator, .@"struct", &fields, kind);
1596 },
1597
1598 .vector_8_i8_type => {
1599 const vector_ctype = try pool.getVector(allocator, .{
1600 .elem_ctype = .i8,
1601 .len = 8,
1602 .nonstring = true,
1603 });
1604 if (!kind.isParameter()) return vector_ctype;
1605 var fields = [_]Info.Field{
1606 .{
1607 .name = .{ .index = .array },
1608 .ctype = vector_ctype,
1609 .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)),
1610 },
1611 };
1612 return pool.fromFields(allocator, .@"struct", &fields, kind);
1613 },
1614 .vector_16_i8_type => {
1615 const vector_ctype = try pool.getVector(allocator, .{
1616 .elem_ctype = .i8,
1617 .len = 16,
1618 .nonstring = true,
1619 });
1620 if (!kind.isParameter()) return vector_ctype;
1621 var fields = [_]Info.Field{
1622 .{
1623 .name = .{ .index = .array },
1624 .ctype = vector_ctype,
1625 .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)),
1626 },
1627 };
1628 return pool.fromFields(allocator, .@"struct", &fields, kind);
1629 },
1630 .vector_32_i8_type => {
1631 const vector_ctype = try pool.getVector(allocator, .{
1632 .elem_ctype = .i8,
1633 .len = 32,
1634 .nonstring = true,
1635 });
1636 if (!kind.isParameter()) return vector_ctype;
1637 var fields = [_]Info.Field{
1638 .{
1639 .name = .{ .index = .array },
1640 .ctype = vector_ctype,
1641 .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)),
1642 },
1643 };
1644 return pool.fromFields(allocator, .@"struct", &fields, kind);
1645 },
1646 .vector_64_i8_type => {
1647 const vector_ctype = try pool.getVector(allocator, .{
1648 .elem_ctype = .i8,
1649 .len = 64,
1650 .nonstring = true,
1651 });
1652 if (!kind.isParameter()) return vector_ctype;
1653 var fields = [_]Info.Field{
1654 .{
1655 .name = .{ .index = .array },
1656 .ctype = vector_ctype,
1657 .alignas = AlignAs.fromAbiAlignment(Type.i8.abiAlignment(zcu)),
1658 },
1659 };
1660 return pool.fromFields(allocator, .@"struct", &fields, kind);
1661 },
1662 .vector_1_u8_type => {
1663 const vector_ctype = try pool.getVector(allocator, .{
1664 .elem_ctype = .u8,
1665 .len = 1,
1666 .nonstring = true,
1667 });
1668 if (!kind.isParameter()) return vector_ctype;
1669 var fields = [_]Info.Field{
1670 .{
1671 .name = .{ .index = .array },
1672 .ctype = vector_ctype,
1673 .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1674 },
1675 };
1676 return pool.fromFields(allocator, .@"struct", &fields, kind);
1677 },
1678 .vector_2_u8_type => {
1679 const vector_ctype = try pool.getVector(allocator, .{
1680 .elem_ctype = .u8,
1681 .len = 2,
1682 .nonstring = true,
1683 });
1684 if (!kind.isParameter()) return vector_ctype;
1685 var fields = [_]Info.Field{
1686 .{
1687 .name = .{ .index = .array },
1688 .ctype = vector_ctype,
1689 .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1690 },
1691 };
1692 return pool.fromFields(allocator, .@"struct", &fields, kind);
1693 },
1694 .vector_4_u8_type => {
1695 const vector_ctype = try pool.getVector(allocator, .{
1696 .elem_ctype = .u8,
1697 .len = 4,
1698 .nonstring = true,
1699 });
1700 if (!kind.isParameter()) return vector_ctype;
1701 var fields = [_]Info.Field{
1702 .{
1703 .name = .{ .index = .array },
1704 .ctype = vector_ctype,
1705 .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1706 },
1707 };
1708 return pool.fromFields(allocator, .@"struct", &fields, kind);
1709 },
1710 .vector_8_u8_type => {
1711 const vector_ctype = try pool.getVector(allocator, .{
1712 .elem_ctype = .u8,
1713 .len = 8,
1714 .nonstring = true,
1715 });
1716 if (!kind.isParameter()) return vector_ctype;
1717 var fields = [_]Info.Field{
1718 .{
1719 .name = .{ .index = .array },
1720 .ctype = vector_ctype,
1721 .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1722 },
1723 };
1724 return pool.fromFields(allocator, .@"struct", &fields, kind);
1725 },
1726 .vector_16_u8_type => {
1727 const vector_ctype = try pool.getVector(allocator, .{
1728 .elem_ctype = .u8,
1729 .len = 16,
1730 .nonstring = true,
1731 });
1732 if (!kind.isParameter()) return vector_ctype;
1733 var fields = [_]Info.Field{
1734 .{
1735 .name = .{ .index = .array },
1736 .ctype = vector_ctype,
1737 .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1738 },
1739 };
1740 return pool.fromFields(allocator, .@"struct", &fields, kind);
1741 },
1742 .vector_32_u8_type => {
1743 const vector_ctype = try pool.getVector(allocator, .{
1744 .elem_ctype = .u8,
1745 .len = 32,
1746 .nonstring = true,
1747 });
1748 if (!kind.isParameter()) return vector_ctype;
1749 var fields = [_]Info.Field{
1750 .{
1751 .name = .{ .index = .array },
1752 .ctype = vector_ctype,
1753 .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1754 },
1755 };
1756 return pool.fromFields(allocator, .@"struct", &fields, kind);
1757 },
1758 .vector_64_u8_type => {
1759 const vector_ctype = try pool.getVector(allocator, .{
1760 .elem_ctype = .u8,
1761 .len = 64,
1762 .nonstring = true,
1763 });
1764 if (!kind.isParameter()) return vector_ctype;
1765 var fields = [_]Info.Field{
1766 .{
1767 .name = .{ .index = .array },
1768 .ctype = vector_ctype,
1769 .alignas = AlignAs.fromAbiAlignment(Type.u8.abiAlignment(zcu)),
1770 },
1771 };
1772 return pool.fromFields(allocator, .@"struct", &fields, kind);
1773 },
1774 .vector_2_i16_type => {
1775 const vector_ctype = try pool.getVector(allocator, .{
1776 .elem_ctype = .i16,
1777 .len = 2,
1778 });
1779 if (!kind.isParameter()) return vector_ctype;
1780 var fields = [_]Info.Field{
1781 .{
1782 .name = .{ .index = .array },
1783 .ctype = vector_ctype,
1784 .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1785 },
1786 };
1787 return pool.fromFields(allocator, .@"struct", &fields, kind);
1788 },
1789 .vector_4_i16_type => {
1790 const vector_ctype = try pool.getVector(allocator, .{
1791 .elem_ctype = .i16,
1792 .len = 4,
1793 });
1794 if (!kind.isParameter()) return vector_ctype;
1795 var fields = [_]Info.Field{
1796 .{
1797 .name = .{ .index = .array },
1798 .ctype = vector_ctype,
1799 .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1800 },
1801 };
1802 return pool.fromFields(allocator, .@"struct", &fields, kind);
1803 },
1804 .vector_8_i16_type => {
1805 const vector_ctype = try pool.getVector(allocator, .{
1806 .elem_ctype = .i16,
1807 .len = 8,
1808 });
1809 if (!kind.isParameter()) return vector_ctype;
1810 var fields = [_]Info.Field{
1811 .{
1812 .name = .{ .index = .array },
1813 .ctype = vector_ctype,
1814 .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1815 },
1816 };
1817 return pool.fromFields(allocator, .@"struct", &fields, kind);
1818 },
1819 .vector_16_i16_type => {
1820 const vector_ctype = try pool.getVector(allocator, .{
1821 .elem_ctype = .i16,
1822 .len = 16,
1823 });
1824 if (!kind.isParameter()) return vector_ctype;
1825 var fields = [_]Info.Field{
1826 .{
1827 .name = .{ .index = .array },
1828 .ctype = vector_ctype,
1829 .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1830 },
1831 };
1832 return pool.fromFields(allocator, .@"struct", &fields, kind);
1833 },
1834 .vector_32_i16_type => {
1835 const vector_ctype = try pool.getVector(allocator, .{
1836 .elem_ctype = .i16,
1837 .len = 32,
1838 });
1839 if (!kind.isParameter()) return vector_ctype;
1840 var fields = [_]Info.Field{
1841 .{
1842 .name = .{ .index = .array },
1843 .ctype = vector_ctype,
1844 .alignas = AlignAs.fromAbiAlignment(Type.i16.abiAlignment(zcu)),
1845 },
1846 };
1847 return pool.fromFields(allocator, .@"struct", &fields, kind);
1848 },
1849 .vector_4_u16_type => {
1850 const vector_ctype = try pool.getVector(allocator, .{
1851 .elem_ctype = .u16,
1852 .len = 4,
1853 });
1854 if (!kind.isParameter()) return vector_ctype;
1855 var fields = [_]Info.Field{
1856 .{
1857 .name = .{ .index = .array },
1858 .ctype = vector_ctype,
1859 .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)),
1860 },
1861 };
1862 return pool.fromFields(allocator, .@"struct", &fields, kind);
1863 },
1864 .vector_8_u16_type => {
1865 const vector_ctype = try pool.getVector(allocator, .{
1866 .elem_ctype = .u16,
1867 .len = 8,
1868 });
1869 if (!kind.isParameter()) return vector_ctype;
1870 var fields = [_]Info.Field{
1871 .{
1872 .name = .{ .index = .array },
1873 .ctype = vector_ctype,
1874 .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)),
1875 },
1876 };
1877 return pool.fromFields(allocator, .@"struct", &fields, kind);
1878 },
1879 .vector_16_u16_type => {
1880 const vector_ctype = try pool.getVector(allocator, .{
1881 .elem_ctype = .u16,
1882 .len = 16,
1883 });
1884 if (!kind.isParameter()) return vector_ctype;
1885 var fields = [_]Info.Field{
1886 .{
1887 .name = .{ .index = .array },
1888 .ctype = vector_ctype,
1889 .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)),
1890 },
1891 };
1892 return pool.fromFields(allocator, .@"struct", &fields, kind);
1893 },
1894 .vector_32_u16_type => {
1895 const vector_ctype = try pool.getVector(allocator, .{
1896 .elem_ctype = .u16,
1897 .len = 32,
1898 });
1899 if (!kind.isParameter()) return vector_ctype;
1900 var fields = [_]Info.Field{
1901 .{
1902 .name = .{ .index = .array },
1903 .ctype = vector_ctype,
1904 .alignas = AlignAs.fromAbiAlignment(Type.u16.abiAlignment(zcu)),
1905 },
1906 };
1907 return pool.fromFields(allocator, .@"struct", &fields, kind);
1908 },
1909 .vector_2_i32_type => {
1910 const vector_ctype = try pool.getVector(allocator, .{
1911 .elem_ctype = .i32,
1912 .len = 2,
1913 });
1914 if (!kind.isParameter()) return vector_ctype;
1915 var fields = [_]Info.Field{
1916 .{
1917 .name = .{ .index = .array },
1918 .ctype = vector_ctype,
1919 .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)),
1920 },
1921 };
1922 return pool.fromFields(allocator, .@"struct", &fields, kind);
1923 },
1924 .vector_4_i32_type => {
1925 const vector_ctype = try pool.getVector(allocator, .{
1926 .elem_ctype = .i32,
1927 .len = 4,
1928 });
1929 if (!kind.isParameter()) return vector_ctype;
1930 var fields = [_]Info.Field{
1931 .{
1932 .name = .{ .index = .array },
1933 .ctype = vector_ctype,
1934 .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)),
1935 },
1936 };
1937 return pool.fromFields(allocator, .@"struct", &fields, kind);
1938 },
1939 .vector_8_i32_type => {
1940 const vector_ctype = try pool.getVector(allocator, .{
1941 .elem_ctype = .i32,
1942 .len = 8,
1943 });
1944 if (!kind.isParameter()) return vector_ctype;
1945 var fields = [_]Info.Field{
1946 .{
1947 .name = .{ .index = .array },
1948 .ctype = vector_ctype,
1949 .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)),
1950 },
1951 };
1952 return pool.fromFields(allocator, .@"struct", &fields, kind);
1953 },
1954 .vector_16_i32_type => {
1955 const vector_ctype = try pool.getVector(allocator, .{
1956 .elem_ctype = .i32,
1957 .len = 16,
1958 });
1959 if (!kind.isParameter()) return vector_ctype;
1960 var fields = [_]Info.Field{
1961 .{
1962 .name = .{ .index = .array },
1963 .ctype = vector_ctype,
1964 .alignas = AlignAs.fromAbiAlignment(Type.i32.abiAlignment(zcu)),
1965 },
1966 };
1967 return pool.fromFields(allocator, .@"struct", &fields, kind);
1968 },
1969 .vector_4_u32_type => {
1970 const vector_ctype = try pool.getVector(allocator, .{
1971 .elem_ctype = .u32,
1972 .len = 4,
1973 });
1974 if (!kind.isParameter()) return vector_ctype;
1975 var fields = [_]Info.Field{
1976 .{
1977 .name = .{ .index = .array },
1978 .ctype = vector_ctype,
1979 .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)),
1980 },
1981 };
1982 return pool.fromFields(allocator, .@"struct", &fields, kind);
1983 },
1984 .vector_8_u32_type => {
1985 const vector_ctype = try pool.getVector(allocator, .{
1986 .elem_ctype = .u32,
1987 .len = 8,
1988 });
1989 if (!kind.isParameter()) return vector_ctype;
1990 var fields = [_]Info.Field{
1991 .{
1992 .name = .{ .index = .array },
1993 .ctype = vector_ctype,
1994 .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)),
1995 },
1996 };
1997 return pool.fromFields(allocator, .@"struct", &fields, kind);
1998 },
1999 .vector_16_u32_type => {
2000 const vector_ctype = try pool.getVector(allocator, .{
2001 .elem_ctype = .u32,
2002 .len = 16,
2003 });
2004 if (!kind.isParameter()) return vector_ctype;
2005 var fields = [_]Info.Field{
2006 .{
2007 .name = .{ .index = .array },
2008 .ctype = vector_ctype,
2009 .alignas = AlignAs.fromAbiAlignment(Type.u32.abiAlignment(zcu)),
2010 },
2011 };
2012 return pool.fromFields(allocator, .@"struct", &fields, kind);
2013 },
2014 .vector_2_i64_type => {
2015 const vector_ctype = try pool.getVector(allocator, .{
2016 .elem_ctype = .i64,
2017 .len = 2,
2018 });
2019 if (!kind.isParameter()) return vector_ctype;
2020 var fields = [_]Info.Field{
2021 .{
2022 .name = .{ .index = .array },
2023 .ctype = vector_ctype,
2024 .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)),
2025 },
2026 };
2027 return pool.fromFields(allocator, .@"struct", &fields, kind);
2028 },
2029 .vector_4_i64_type => {
2030 const vector_ctype = try pool.getVector(allocator, .{
2031 .elem_ctype = .i64,
2032 .len = 4,
2033 });
2034 if (!kind.isParameter()) return vector_ctype;
2035 var fields = [_]Info.Field{
2036 .{
2037 .name = .{ .index = .array },
2038 .ctype = vector_ctype,
2039 .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)),
2040 },
2041 };
2042 return pool.fromFields(allocator, .@"struct", &fields, kind);
2043 },
2044 .vector_8_i64_type => {
2045 const vector_ctype = try pool.getVector(allocator, .{
2046 .elem_ctype = .i64,
2047 .len = 8,
2048 });
2049 if (!kind.isParameter()) return vector_ctype;
2050 var fields = [_]Info.Field{
2051 .{
2052 .name = .{ .index = .array },
2053 .ctype = vector_ctype,
2054 .alignas = AlignAs.fromAbiAlignment(Type.i64.abiAlignment(zcu)),
2055 },
2056 };
2057 return pool.fromFields(allocator, .@"struct", &fields, kind);
2058 },
2059 .vector_2_u64_type => {
2060 const vector_ctype = try pool.getVector(allocator, .{
2061 .elem_ctype = .u64,
2062 .len = 2,
2063 });
2064 if (!kind.isParameter()) return vector_ctype;
2065 var fields = [_]Info.Field{
2066 .{
2067 .name = .{ .index = .array },
2068 .ctype = vector_ctype,
2069 .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)),
2070 },
2071 };
2072 return pool.fromFields(allocator, .@"struct", &fields, kind);
2073 },
2074 .vector_4_u64_type => {
2075 const vector_ctype = try pool.getVector(allocator, .{
2076 .elem_ctype = .u64,
2077 .len = 4,
2078 });
2079 if (!kind.isParameter()) return vector_ctype;
2080 var fields = [_]Info.Field{
2081 .{
2082 .name = .{ .index = .array },
2083 .ctype = vector_ctype,
2084 .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)),
2085 },
2086 };
2087 return pool.fromFields(allocator, .@"struct", &fields, kind);
2088 },
2089 .vector_8_u64_type => {
2090 const vector_ctype = try pool.getVector(allocator, .{
2091 .elem_ctype = .u64,
2092 .len = 8,
2093 });
2094 if (!kind.isParameter()) return vector_ctype;
2095 var fields = [_]Info.Field{
2096 .{
2097 .name = .{ .index = .array },
2098 .ctype = vector_ctype,
2099 .alignas = AlignAs.fromAbiAlignment(Type.u64.abiAlignment(zcu)),
2100 },
2101 };
2102 return pool.fromFields(allocator, .@"struct", &fields, kind);
2103 },
2104 .vector_1_u128_type => {
2105 const vector_ctype = try pool.getVector(allocator, .{
2106 .elem_ctype = .u128,
2107 .len = 1,
2108 });
2109 if (!kind.isParameter()) return vector_ctype;
2110 var fields = [_]Info.Field{
2111 .{
2112 .name = .{ .index = .array },
2113 .ctype = vector_ctype,
2114 .alignas = AlignAs.fromAbiAlignment(Type.u128.abiAlignment(zcu)),
2115 },
2116 };
2117 return pool.fromFields(allocator, .@"struct", &fields, kind);
2118 },
2119 .vector_2_u128_type => {
2120 const vector_ctype = try pool.getVector(allocator, .{
2121 .elem_ctype = .u128,
2122 .len = 2,
2123 });
2124 if (!kind.isParameter()) return vector_ctype;
2125 var fields = [_]Info.Field{
2126 .{
2127 .name = .{ .index = .array },
2128 .ctype = vector_ctype,
2129 .alignas = AlignAs.fromAbiAlignment(Type.u128.abiAlignment(zcu)),
2130 },
2131 };
2132 return pool.fromFields(allocator, .@"struct", &fields, kind);
2133 },
2134 .vector_1_u256_type => {
2135 const vector_ctype = try pool.getVector(allocator, .{
2136 .elem_ctype = try pool.fromIntInfo(allocator, .{
2137 .signedness = .unsigned,
2138 .bits = 256,
2139 }, mod, kind),
2140 .len = 1,
2141 });
2142 if (!kind.isParameter()) return vector_ctype;
2143 var fields = [_]Info.Field{
2144 .{
2145 .name = .{ .index = .array },
2146 .ctype = vector_ctype,
2147 .alignas = AlignAs.fromAbiAlignment(Type.u256.abiAlignment(zcu)),
2148 },
2149 };
2150 return pool.fromFields(allocator, .@"struct", &fields, kind);
2151 },
2152 .vector_4_f16_type => {
2153 const vector_ctype = try pool.getVector(allocator, .{
2154 .elem_ctype = .f16,
2155 .len = 4,
2156 });
2157 if (!kind.isParameter()) return vector_ctype;
2158 var fields = [_]Info.Field{
2159 .{
2160 .name = .{ .index = .array },
2161 .ctype = vector_ctype,
2162 .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)),
2163 },
2164 };
2165 return pool.fromFields(allocator, .@"struct", &fields, kind);
2166 },
2167 .vector_8_f16_type => {
2168 const vector_ctype = try pool.getVector(allocator, .{
2169 .elem_ctype = .f16,
2170 .len = 8,
2171 });
2172 if (!kind.isParameter()) return vector_ctype;
2173 var fields = [_]Info.Field{
2174 .{
2175 .name = .{ .index = .array },
2176 .ctype = vector_ctype,
2177 .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)),
2178 },
2179 };
2180 return pool.fromFields(allocator, .@"struct", &fields, kind);
2181 },
2182 .vector_16_f16_type => {
2183 const vector_ctype = try pool.getVector(allocator, .{
2184 .elem_ctype = .f16,
2185 .len = 16,
2186 });
2187 if (!kind.isParameter()) return vector_ctype;
2188 var fields = [_]Info.Field{
2189 .{
2190 .name = .{ .index = .array },
2191 .ctype = vector_ctype,
2192 .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)),
2193 },
2194 };
2195 return pool.fromFields(allocator, .@"struct", &fields, kind);
2196 },
2197 .vector_32_f16_type => {
2198 const vector_ctype = try pool.getVector(allocator, .{
2199 .elem_ctype = .f16,
2200 .len = 32,
2201 });
2202 if (!kind.isParameter()) return vector_ctype;
2203 var fields = [_]Info.Field{
2204 .{
2205 .name = .{ .index = .array },
2206 .ctype = vector_ctype,
2207 .alignas = AlignAs.fromAbiAlignment(Type.f16.abiAlignment(zcu)),
2208 },
2209 };
2210 return pool.fromFields(allocator, .@"struct", &fields, kind);
2211 },
2212 .vector_2_f32_type => {
2213 const vector_ctype = try pool.getVector(allocator, .{
2214 .elem_ctype = .f32,
2215 .len = 2,
2216 });
2217 if (!kind.isParameter()) return vector_ctype;
2218 var fields = [_]Info.Field{
2219 .{
2220 .name = .{ .index = .array },
2221 .ctype = vector_ctype,
2222 .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)),
2223 },
2224 };
2225 return pool.fromFields(allocator, .@"struct", &fields, kind);
2226 },
2227 .vector_4_f32_type => {
2228 const vector_ctype = try pool.getVector(allocator, .{
2229 .elem_ctype = .f32,
2230 .len = 4,
2231 });
2232 if (!kind.isParameter()) return vector_ctype;
2233 var fields = [_]Info.Field{
2234 .{
2235 .name = .{ .index = .array },
2236 .ctype = vector_ctype,
2237 .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)),
2238 },
2239 };
2240 return pool.fromFields(allocator, .@"struct", &fields, kind);
2241 },
2242 .vector_8_f32_type => {
2243 const vector_ctype = try pool.getVector(allocator, .{
2244 .elem_ctype = .f32,
2245 .len = 8,
2246 });
2247 if (!kind.isParameter()) return vector_ctype;
2248 var fields = [_]Info.Field{
2249 .{
2250 .name = .{ .index = .array },
2251 .ctype = vector_ctype,
2252 .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)),
2253 },
2254 };
2255 return pool.fromFields(allocator, .@"struct", &fields, kind);
2256 },
2257 .vector_16_f32_type => {
2258 const vector_ctype = try pool.getVector(allocator, .{
2259 .elem_ctype = .f32,
2260 .len = 16,
2261 });
2262 if (!kind.isParameter()) return vector_ctype;
2263 var fields = [_]Info.Field{
2264 .{
2265 .name = .{ .index = .array },
2266 .ctype = vector_ctype,
2267 .alignas = AlignAs.fromAbiAlignment(Type.f32.abiAlignment(zcu)),
2268 },
2269 };
2270 return pool.fromFields(allocator, .@"struct", &fields, kind);
2271 },
2272 .vector_2_f64_type => {
2273 const vector_ctype = try pool.getVector(allocator, .{
2274 .elem_ctype = .f64,
2275 .len = 2,
2276 });
2277 if (!kind.isParameter()) return vector_ctype;
2278 var fields = [_]Info.Field{
2279 .{
2280 .name = .{ .index = .array },
2281 .ctype = vector_ctype,
2282 .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)),
2283 },
2284 };
2285 return pool.fromFields(allocator, .@"struct", &fields, kind);
2286 },
2287 .vector_4_f64_type => {
2288 const vector_ctype = try pool.getVector(allocator, .{
2289 .elem_ctype = .f64,
2290 .len = 4,
2291 });
2292 if (!kind.isParameter()) return vector_ctype;
2293 var fields = [_]Info.Field{
2294 .{
2295 .name = .{ .index = .array },
2296 .ctype = vector_ctype,
2297 .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)),
2298 },
2299 };
2300 return pool.fromFields(allocator, .@"struct", &fields, kind);
2301 },
2302 .vector_8_f64_type => {
2303 const vector_ctype = try pool.getVector(allocator, .{
2304 .elem_ctype = .f64,
2305 .len = 8,
2306 });
2307 if (!kind.isParameter()) return vector_ctype;
2308 var fields = [_]Info.Field{
2309 .{
2310 .name = .{ .index = .array },
2311 .ctype = vector_ctype,
2312 .alignas = AlignAs.fromAbiAlignment(Type.f64.abiAlignment(zcu)),
2313 },
2314 };
2315 return pool.fromFields(allocator, .@"struct", &fields, kind);
2316 },
2317
2318 .undef,
2319 .undef_bool,
2320 .undef_usize,
2321 .undef_u1,
2322 .zero,
2323 .zero_usize,
2324 .zero_u1,
2325 .zero_u8,
2326 .one,
2327 .one_usize,
2328 .one_u1,
2329 .one_u8,
2330 .four_u8,
2331 .negative_one,
2332 .void_value,
2333 .unreachable_value,
2334 .null_value,
2335 .bool_true,
2336 .bool_false,
2337 .empty_tuple,
2338 .none,
2339 => unreachable, // values, not types
2340
2341 _ => |ip_index| switch (ip.indexToKey(ip_index)) {
2342 .int_type => |int_info| return pool.fromIntInfo(allocator, int_info, mod, kind),
2343 .ptr_type => |ptr_info| switch (ptr_info.flags.size) {
2344 .one, .many, .c => {
2345 const elem_ctype = elem_ctype: {
2346 if (ptr_info.packed_offset.host_size > 0 and
2347 ptr_info.flags.vector_index == .none)
2348 break :elem_ctype try pool.fromIntInfo(allocator, .{
2349 .signedness = .unsigned,
2350 .bits = ptr_info.packed_offset.host_size * 8,
2351 }, mod, .forward);
2352 const elem: Info.Aligned = .{
2353 .ctype = try pool.fromType(
2354 allocator,
2355 scratch,
2356 Type.fromInterned(ptr_info.child),
2357 pt,
2358 mod,
2359 .forward,
2360 ),
2361 .alignas = AlignAs.fromAlignment(.{
2362 .@"align" = ptr_info.flags.alignment,
2363 .abi = Type.fromInterned(ptr_info.child).abiAlignment(zcu),
2364 }),
2365 };
2366 break :elem_ctype if (elem.alignas.abiOrder().compare(.gte))
2367 elem.ctype
2368 else
2369 try pool.getAligned(allocator, elem);
2370 };
2371 const elem_tag: Info.Tag = switch (elem_ctype.info(pool)) {
2372 .aligned => |aligned_info| aligned_info.ctype.info(pool),
2373 else => |elem_tag| elem_tag,
2374 };
2375 return pool.getPointer(allocator, .{
2376 .elem_ctype = elem_ctype,
2377 .@"const" = switch (elem_tag) {
2378 .basic,
2379 .pointer,
2380 .aligned,
2381 .array,
2382 .vector,
2383 .fwd_decl,
2384 .aggregate,
2385 => ptr_info.flags.is_const,
2386 .function => false,
2387 },
2388 .@"volatile" = ptr_info.flags.is_volatile,
2389 .nonstring = elem_ctype.isAnyChar() and switch (ptr_info.sentinel) {
2390 .none => true,
2391 .zero_u8 => false,
2392 else => |sentinel| !Value.fromInterned(sentinel).compareAllWithZero(.eq, zcu),
2393 },
2394 });
2395 },
2396 .slice => {
2397 const target = &mod.resolved_target.result;
2398 var fields = [_]Info.Field{
2399 .{
2400 .name = .{ .index = .ptr },
2401 .ctype = try pool.fromType(
2402 allocator,
2403 scratch,
2404 Type.fromInterned(ip.slicePtrType(ip_index)),
2405 pt,
2406 mod,
2407 kind,
2408 ),
2409 .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target)),
2410 },
2411 .{
2412 .name = .{ .index = .len },
2413 .ctype = .usize,
2414 .alignas = AlignAs.fromAbiAlignment(
2415 .fromByteUnits(std.zig.target.intAlignment(target, target.ptrBitWidth())),
2416 ),
2417 },
2418 };
2419 return pool.fromFields(allocator, .@"struct", &fields, kind);
2420 },
2421 },
2422 .array_type => |array_info| {
2423 const len = array_info.lenIncludingSentinel();
2424 if (len == 0) return .void;
2425 const elem_type = Type.fromInterned(array_info.child);
2426 const elem_ctype = try pool.fromType(
2427 allocator,
2428 scratch,
2429 elem_type,
2430 pt,
2431 mod,
2432 kind.noParameter().asComplete(),
2433 );
2434 if (elem_ctype.index == .void) return .void;
2435 const array_ctype = try pool.getArray(allocator, .{
2436 .elem_ctype = elem_ctype,
2437 .len = len,
2438 .nonstring = elem_ctype.isAnyChar() and switch (array_info.sentinel) {
2439 .none => true,
2440 .zero_u8 => false,
2441 else => |sentinel| !Value.fromInterned(sentinel).compareAllWithZero(.eq, zcu),
2442 },
2443 });
2444 if (!kind.isParameter()) return array_ctype;
2445 var fields = [_]Info.Field{
2446 .{
2447 .name = .{ .index = .array },
2448 .ctype = array_ctype,
2449 .alignas = AlignAs.fromAbiAlignment(elem_type.abiAlignment(zcu)),
2450 },
2451 };
2452 return pool.fromFields(allocator, .@"struct", &fields, kind);
2453 },
2454 .vector_type => |vector_info| {
2455 if (vector_info.len == 0) return .void;
2456 const elem_type = Type.fromInterned(vector_info.child);
2457 const elem_ctype = try pool.fromType(
2458 allocator,
2459 scratch,
2460 elem_type,
2461 pt,
2462 mod,
2463 kind.noParameter().asComplete(),
2464 );
2465 if (elem_ctype.index == .void) return .void;
2466 const vector_ctype = try pool.getVector(allocator, .{
2467 .elem_ctype = elem_ctype,
2468 .len = vector_info.len,
2469 .nonstring = elem_ctype.isAnyChar(),
2470 });
2471 if (!kind.isParameter()) return vector_ctype;
2472 var fields = [_]Info.Field{
2473 .{
2474 .name = .{ .index = .array },
2475 .ctype = vector_ctype,
2476 .alignas = AlignAs.fromAbiAlignment(elem_type.abiAlignment(zcu)),
2477 },
2478 };
2479 return pool.fromFields(allocator, .@"struct", &fields, kind);
2480 },
2481 .opt_type => |payload_type| {
2482 if (Type.fromInterned(payload_type).isNoReturn(zcu)) return .void;
2483 const payload_ctype = try pool.fromType(
2484 allocator,
2485 scratch,
2486 Type.fromInterned(payload_type),
2487 pt,
2488 mod,
2489 kind.noParameter(),
2490 );
2491 if (payload_ctype.index == .void) return .bool;
2492 switch (payload_type) {
2493 .anyerror_type => return payload_ctype,
2494 else => switch (ip.indexToKey(payload_type)) {
2495 .ptr_type => |payload_ptr_info| if (payload_ptr_info.flags.size != .c and
2496 !payload_ptr_info.flags.is_allowzero) return payload_ctype,
2497 .error_set_type, .inferred_error_set_type => return payload_ctype,
2498 else => {},
2499 },
2500 }
2501 var fields = [_]Info.Field{
2502 .{
2503 .name = .{ .index = .is_null },
2504 .ctype = .bool,
2505 .alignas = AlignAs.fromAbiAlignment(.@"1"),
2506 },
2507 .{
2508 .name = .{ .index = .payload },
2509 .ctype = payload_ctype,
2510 .alignas = AlignAs.fromAbiAlignment(
2511 Type.fromInterned(payload_type).abiAlignment(zcu),
2512 ),
2513 },
2514 };
2515 return pool.fromFields(allocator, .@"struct", &fields, kind);
2516 },
2517 .anyframe_type => unreachable,
2518 .error_union_type => |error_union_info| {
2519 const error_set_bits = pt.zcu.errorSetBits();
2520 const error_set_ctype = try pool.fromIntInfo(allocator, .{
2521 .signedness = .unsigned,
2522 .bits = error_set_bits,
2523 }, mod, kind);
2524 if (Type.fromInterned(error_union_info.payload_type).isNoReturn(zcu)) return error_set_ctype;
2525 const payload_type = Type.fromInterned(error_union_info.payload_type);
2526 const payload_ctype = try pool.fromType(
2527 allocator,
2528 scratch,
2529 payload_type,
2530 pt,
2531 mod,
2532 kind.noParameter(),
2533 );
2534 if (payload_ctype.index == .void) return error_set_ctype;
2535 const target = &mod.resolved_target.result;
2536 var fields = [_]Info.Field{
2537 .{
2538 .name = .{ .index = .@"error" },
2539 .ctype = error_set_ctype,
2540 .alignas = AlignAs.fromAbiAlignment(
2541 .fromByteUnits(std.zig.target.intAlignment(target, error_set_bits)),
2542 ),
2543 },
2544 .{
2545 .name = .{ .index = .payload },
2546 .ctype = payload_ctype,
2547 .alignas = AlignAs.fromAbiAlignment(payload_type.abiAlignment(zcu)),
2548 },
2549 };
2550 return pool.fromFields(allocator, .@"struct", &fields, kind);
2551 },
2552 .simple_type => unreachable,
2553 .struct_type => {
2554 const loaded_struct = ip.loadStructType(ip_index);
2555 switch (loaded_struct.layout) {
2556 .auto, .@"extern" => {
2557 const fwd_decl = try pool.getFwdDecl(allocator, .{
2558 .tag = .@"struct",
2559 .name = .{ .index = ip_index },
2560 });
2561 if (kind.isForward()) return if (ty.hasRuntimeBits(zcu))
2562 fwd_decl
2563 else
2564 .void;
2565 const scratch_top = scratch.items.len;
2566 defer scratch.shrinkRetainingCapacity(scratch_top);
2567 try scratch.ensureUnusedCapacity(
2568 allocator,
2569 loaded_struct.field_types.len * @typeInfo(Field).@"struct".fields.len,
2570 );
2571 var hasher = Hasher.init;
2572 var tag: Pool.Tag = .aggregate_struct;
2573 var field_it = loaded_struct.iterateRuntimeOrder(ip);
2574 while (field_it.next()) |field_index| {
2575 const field_type = Type.fromInterned(
2576 loaded_struct.field_types.get(ip)[field_index],
2577 );
2578 const field_ctype = try pool.fromType(
2579 allocator,
2580 scratch,
2581 field_type,
2582 pt,
2583 mod,
2584 kind.noParameter(),
2585 );
2586 if (field_ctype.index == .void) continue;
2587 const field_name = try pool.string(allocator, loaded_struct.field_names.get(ip)[field_index].toSlice(ip));
2588 const field_alignas = AlignAs.fromAlignment(.{
2589 .@"align" = loaded_struct.field_aligns.getOrNone(ip, field_index),
2590 .abi = field_type.abiAlignment(zcu),
2591 });
2592 pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{
2593 .name = field_name.index,
2594 .ctype = field_ctype.index,
2595 .flags = .{ .alignas = field_alignas },
2596 });
2597 if (field_alignas.abiOrder().compare(.lt))
2598 tag = .aggregate_struct_packed;
2599 }
2600 const fields_len: u32 = @intCast(@divExact(
2601 scratch.items.len - scratch_top,
2602 @typeInfo(Field).@"struct".fields.len,
2603 ));
2604 if (fields_len == 0) return .void;
2605 try pool.ensureUnusedCapacity(allocator, 1);
2606 const extra_index = try pool.addHashedExtra(allocator, &hasher, Aggregate, .{
2607 .fwd_decl = fwd_decl.index,
2608 .fields_len = fields_len,
2609 }, fields_len * @typeInfo(Field).@"struct".fields.len);
2610 pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2611 return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index);
2612 },
2613 .@"packed" => return pool.fromType(
2614 allocator,
2615 scratch,
2616 .fromInterned(loaded_struct.packed_backing_int_type),
2617 pt,
2618 mod,
2619 kind,
2620 ),
2621 }
2622 },
2623 .tuple_type => |tuple_info| {
2624 const scratch_top = scratch.items.len;
2625 defer scratch.shrinkRetainingCapacity(scratch_top);
2626 try scratch.ensureUnusedCapacity(allocator, tuple_info.types.len *
2627 @typeInfo(Field).@"struct".fields.len);
2628 var hasher = Hasher.init;
2629 for (0..tuple_info.types.len) |field_index| {
2630 if (tuple_info.values.get(ip)[field_index] != .none) continue;
2631 const field_type = Type.fromInterned(
2632 tuple_info.types.get(ip)[field_index],
2633 );
2634 const field_ctype = try pool.fromType(
2635 allocator,
2636 scratch,
2637 field_type,
2638 pt,
2639 mod,
2640 kind.noParameter(),
2641 );
2642 if (field_ctype.index == .void) continue;
2643 const field_name = try pool.fmt(allocator, "f{d}", .{field_index});
2644 pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{
2645 .name = field_name.index,
2646 .ctype = field_ctype.index,
2647 .flags = .{ .alignas = AlignAs.fromAbiAlignment(
2648 field_type.abiAlignment(zcu),
2649 ) },
2650 });
2651 }
2652 const fields_len: u32 = @intCast(@divExact(
2653 scratch.items.len - scratch_top,
2654 @typeInfo(Field).@"struct".fields.len,
2655 ));
2656 if (fields_len == 0) return .void;
2657 if (kind.isForward()) {
2658 try pool.ensureUnusedCapacity(allocator, 1);
2659 const extra_index = try pool.addHashedExtra(
2660 allocator,
2661 &hasher,
2662 FwdDeclAnon,
2663 .{ .fields_len = fields_len },
2664 fields_len * @typeInfo(Field).@"struct".fields.len,
2665 );
2666 pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2667 return pool.tagTrailingExtra(
2668 allocator,
2669 hasher,
2670 .fwd_decl_struct_anon,
2671 extra_index,
2672 );
2673 }
2674 const fwd_decl = try pool.fromType(allocator, scratch, ty, pt, mod, .forward);
2675 try pool.ensureUnusedCapacity(allocator, 1);
2676 const extra_index = try pool.addHashedExtra(allocator, &hasher, Aggregate, .{
2677 .fwd_decl = fwd_decl.index,
2678 .fields_len = fields_len,
2679 }, fields_len * @typeInfo(Field).@"struct".fields.len);
2680 pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2681 return pool.tagTrailingExtraAssumeCapacity(hasher, .aggregate_struct, extra_index);
2682 },
2683 .union_type => {
2684 const loaded_union = ip.loadUnionType(ip_index);
2685 switch (loaded_union.layout) {
2686 .auto, .@"extern" => {
2687 const fwd_decl = try pool.getFwdDecl(allocator, .{
2688 .tag = if (loaded_union.has_runtime_tag) .@"struct" else .@"union",
2689 .name = .{ .index = ip_index },
2690 });
2691 if (kind.isForward()) return if (ty.hasRuntimeBits(zcu))
2692 fwd_decl
2693 else
2694 .void;
2695 const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type);
2696 const scratch_top = scratch.items.len;
2697 defer scratch.shrinkRetainingCapacity(scratch_top);
2698 try scratch.ensureUnusedCapacity(
2699 allocator,
2700 loaded_union.field_types.len * @typeInfo(Field).@"struct".fields.len,
2701 );
2702 var hasher = Hasher.init;
2703 var tag: Pool.Tag = .aggregate_union;
2704 var payload_align: InternPool.Alignment = .@"1";
2705 for (0..loaded_union.field_types.len) |field_index| {
2706 const field_type = Type.fromInterned(
2707 loaded_union.field_types.get(ip)[field_index],
2708 );
2709 if (field_type.isNoReturn(zcu)) continue;
2710 const field_ctype = try pool.fromType(
2711 allocator,
2712 scratch,
2713 field_type,
2714 pt,
2715 mod,
2716 kind.noParameter(),
2717 );
2718 if (field_ctype.index == .void) continue;
2719 const field_name = try pool.string(
2720 allocator,
2721 loaded_tag.field_names.get(ip)[field_index].toSlice(ip),
2722 );
2723 const field_alignas = AlignAs.fromAlignment(.{
2724 .@"align" = loaded_union.field_aligns.getOrNone(ip, field_index),
2725 .abi = field_type.abiAlignment(zcu),
2726 });
2727 pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{
2728 .name = field_name.index,
2729 .ctype = field_ctype.index,
2730 .flags = .{ .alignas = field_alignas },
2731 });
2732 if (field_alignas.abiOrder().compare(.lt))
2733 tag = .aggregate_union_packed;
2734 payload_align = payload_align.maxStrict(field_alignas.@"align");
2735 }
2736 const fields_len: u32 = @intCast(@divExact(
2737 scratch.items.len - scratch_top,
2738 @typeInfo(Field).@"struct".fields.len,
2739 ));
2740 if (!loaded_union.has_runtime_tag) {
2741 if (fields_len == 0) return .void;
2742 try pool.ensureUnusedCapacity(allocator, 1);
2743 const extra_index = try pool.addHashedExtra(
2744 allocator,
2745 &hasher,
2746 Aggregate,
2747 .{ .fwd_decl = fwd_decl.index, .fields_len = fields_len },
2748 fields_len * @typeInfo(Field).@"struct".fields.len,
2749 );
2750 pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2751 return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index);
2752 }
2753 try pool.ensureUnusedCapacity(allocator, 2);
2754 var struct_fields: [2]Info.Field = undefined;
2755 var struct_fields_len: usize = 0;
2756 const tag_type = Type.fromInterned(loaded_tag.int_tag_type);
2757 const tag_ctype: CType = try pool.fromType(
2758 allocator,
2759 scratch,
2760 tag_type,
2761 pt,
2762 mod,
2763 kind.noParameter(),
2764 );
2765 if (tag_ctype.index != .void) {
2766 struct_fields[struct_fields_len] = .{
2767 .name = .{ .index = .tag },
2768 .ctype = tag_ctype,
2769 .alignas = AlignAs.fromAbiAlignment(tag_type.abiAlignment(zcu)),
2770 };
2771 struct_fields_len += 1;
2772 }
2773 if (fields_len > 0) {
2774 const payload_ctype = payload_ctype: {
2775 const extra_index = try pool.addHashedExtra(
2776 allocator,
2777 &hasher,
2778 AggregateAnon,
2779 .{
2780 .index = ip_index,
2781 .id = 0,
2782 .fields_len = fields_len,
2783 },
2784 fields_len * @typeInfo(Field).@"struct".fields.len,
2785 );
2786 pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2787 break :payload_ctype pool.tagTrailingExtraAssumeCapacity(
2788 hasher,
2789 switch (tag) {
2790 .aggregate_union => .aggregate_union_anon,
2791 .aggregate_union_packed => .aggregate_union_packed_anon,
2792 else => unreachable,
2793 },
2794 extra_index,
2795 );
2796 };
2797 if (payload_ctype.index != .void) {
2798 struct_fields[struct_fields_len] = .{
2799 .name = .{ .index = .payload },
2800 .ctype = payload_ctype,
2801 .alignas = AlignAs.fromAbiAlignment(payload_align),
2802 };
2803 struct_fields_len += 1;
2804 }
2805 }
2806 if (struct_fields_len == 0) return .void;
2807 sortFields(struct_fields[0..struct_fields_len]);
2808 return pool.getAggregate(allocator, .{
2809 .tag = .@"struct",
2810 .name = .{ .fwd_decl = fwd_decl },
2811 .fields = struct_fields[0..struct_fields_len],
2812 });
2813 },
2814 .@"packed" => return pool.fromIntInfo(allocator, .{
2815 .signedness = .unsigned,
2816 .bits = @intCast(ty.bitSize(zcu)),
2817 }, mod, kind),
2818 }
2819 },
2820 .opaque_type => return .void,
2821 .enum_type => return pool.fromType(
2822 allocator,
2823 scratch,
2824 .fromInterned(ip.loadEnumType(ip_index).int_tag_type),
2825 pt,
2826 mod,
2827 kind,
2828 ),
2829 .func_type => |func_info| {
2830 if (!ty.fnHasRuntimeBits(zcu)) return .void;
2831
2832 const scratch_top = scratch.items.len;
2833 defer scratch.shrinkRetainingCapacity(scratch_top);
2834 try scratch.ensureUnusedCapacity(allocator, func_info.param_types.len);
2835 var hasher = Hasher.init;
2836 const return_type = Type.fromInterned(func_info.return_type);
2837 const return_ctype: CType =
2838 if (!Type.fromInterned(func_info.return_type).isNoReturn(zcu)) try pool.fromType(
2839 allocator,
2840 scratch,
2841 return_type,
2842 pt,
2843 mod,
2844 kind.asParameter(),
2845 ) else .void;
2846 for (0..func_info.param_types.len) |param_index| {
2847 const param_type = Type.fromInterned(
2848 func_info.param_types.get(ip)[param_index],
2849 );
2850 const param_ctype = try pool.fromType(
2851 allocator,
2852 scratch,
2853 param_type,
2854 pt,
2855 mod,
2856 kind.asParameter(),
2857 );
2858 if (param_ctype.index == .void) continue;
2859 hasher.update(param_ctype.hash(pool));
2860 scratch.appendAssumeCapacity(@intFromEnum(param_ctype.index));
2861 }
2862 const param_ctypes_len: u32 = @intCast(scratch.items.len - scratch_top);
2863 try pool.ensureUnusedCapacity(allocator, 1);
2864 const extra_index = try pool.addHashedExtra(allocator, &hasher, Function, .{
2865 .return_ctype = return_ctype.index,
2866 .param_ctypes_len = param_ctypes_len,
2867 }, param_ctypes_len);
2868 pool.extra.appendSliceAssumeCapacity(scratch.items[scratch_top..]);
2869 return pool.tagTrailingExtraAssumeCapacity(hasher, switch (func_info.is_var_args) {
2870 false => .function,
2871 true => .function_varargs,
2872 }, extra_index);
2873 },
2874 .error_set_type,
2875 .inferred_error_set_type,
2876 => return pool.fromIntInfo(allocator, .{
2877 .signedness = .unsigned,
2878 .bits = pt.zcu.errorSetBits(),
2879 }, mod, kind),
2880
2881 .undef,
2882 .simple_value,
2883 .variable,
2884 .@"extern",
2885 .func,
2886 .int,
2887 .err,
2888 .error_union,
2889 .enum_literal,
2890 .enum_tag,
2891 .float,
2892 .ptr,
2893 .slice,
2894 .opt,
2895 .aggregate,
2896 .un,
2897 .bitpack,
2898 .memoized_call,
2899 => unreachable, // values, not types
2900 },
2901 }
2902 }
2903
2904 pub fn getOrPutAdapted(
2905 pool: *Pool,
2906 allocator: std.mem.Allocator,
2907 source_pool: *const Pool,
2908 source_ctype: CType,
2909 pool_adapter: anytype,
2910 ) !struct { CType, bool } {
2911 const tag = source_pool.items.items(.tag)[
2912 source_ctype.toPoolIndex() orelse return .{ source_ctype, true }
2913 ];
2914 try pool.ensureUnusedCapacity(allocator, 1);
2915 const CTypeAdapter = struct {
2916 pool: *const Pool,
2917 source_pool: *const Pool,
2918 source_info: Info,
2919 pool_adapter: @TypeOf(pool_adapter),
2920 pub fn hash(map_adapter: @This(), key_ctype: CType) Map.Hash {
2921 return key_ctype.hash(map_adapter.source_pool);
2922 }
2923 pub fn eql(map_adapter: @This(), _: CType, _: void, pool_index: usize) bool {
2924 return map_adapter.source_info.eqlAdapted(
2925 map_adapter.source_pool,
2926 .fromPoolIndex(pool_index),
2927 map_adapter.pool,
2928 map_adapter.pool_adapter,
2929 );
2930 }
2931 };
2932 const source_info = source_ctype.info(source_pool);
2933 const gop = pool.map.getOrPutAssumeCapacityAdapted(source_ctype, CTypeAdapter{
2934 .pool = pool,
2935 .source_pool = source_pool,
2936 .source_info = source_info,
2937 .pool_adapter = pool_adapter,
2938 });
2939 errdefer _ = pool.map.pop();
2940 const ctype: CType = .fromPoolIndex(gop.index);
2941 if (!gop.found_existing) switch (source_info) {
2942 .basic => unreachable,
2943 .pointer => |pointer_info| pool.items.appendAssumeCapacity(switch (pointer_info.nonstring) {
2944 false => .{
2945 .tag = tag,
2946 .data = @intFromEnum(pool_adapter.copy(pointer_info.elem_ctype).index),
2947 },
2948 true => .{
2949 .tag = .nonstring,
2950 .data = @intFromEnum(pool_adapter.copy(.{ .index = @enumFromInt(
2951 source_pool.items.items(.data)[source_ctype.toPoolIndex().?],
2952 ) }).index),
2953 },
2954 }),
2955 .aligned => |aligned_info| pool.items.appendAssumeCapacity(.{
2956 .tag = tag,
2957 .data = try pool.addExtra(allocator, Aligned, .{
2958 .ctype = pool_adapter.copy(aligned_info.ctype).index,
2959 .flags = .{ .alignas = aligned_info.alignas },
2960 }, 0),
2961 }),
2962 .array, .vector => |sequence_info| pool.items.appendAssumeCapacity(switch (sequence_info.nonstring) {
2963 false => .{
2964 .tag = tag,
2965 .data = switch (tag) {
2966 .array_small, .vector => try pool.addExtra(allocator, SequenceSmall, .{
2967 .elem_ctype = pool_adapter.copy(sequence_info.elem_ctype).index,
2968 .len = @intCast(sequence_info.len),
2969 }, 0),
2970 .array_large => try pool.addExtra(allocator, SequenceLarge, .{
2971 .elem_ctype = pool_adapter.copy(sequence_info.elem_ctype).index,
2972 .len_lo = @truncate(sequence_info.len >> 0),
2973 .len_hi = @truncate(sequence_info.len >> 32),
2974 }, 0),
2975 else => unreachable,
2976 },
2977 },
2978 true => .{
2979 .tag = .nonstring,
2980 .data = @intFromEnum(pool_adapter.copy(.{ .index = @enumFromInt(
2981 source_pool.items.items(.data)[source_ctype.toPoolIndex().?],
2982 ) }).index),
2983 },
2984 }),
2985 .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) {
2986 .anon => |fields| {
2987 pool.items.appendAssumeCapacity(.{
2988 .tag = tag,
2989 .data = try pool.addExtra(allocator, FwdDeclAnon, .{
2990 .fields_len = fields.len,
2991 }, fields.len * @typeInfo(Field).@"struct".fields.len),
2992 });
2993 for (0..fields.len) |field_index| {
2994 const field = fields.at(field_index, source_pool);
2995 const field_name = if (field.name.toPoolSlice(source_pool)) |slice|
2996 try pool.string(allocator, slice)
2997 else
2998 field.name;
2999 pool.addExtraAssumeCapacity(Field, .{
3000 .name = field_name.index,
3001 .ctype = pool_adapter.copy(field.ctype).index,
3002 .flags = .{ .alignas = field.alignas },
3003 });
3004 }
3005 },
3006 .index => |index| pool.items.appendAssumeCapacity(.{
3007 .tag = tag,
3008 .data = @intFromEnum(index),
3009 }),
3010 },
3011 .aggregate => |aggregate_info| {
3012 pool.items.appendAssumeCapacity(.{
3013 .tag = tag,
3014 .data = switch (aggregate_info.name) {
3015 .anon => |anon| try pool.addExtra(allocator, AggregateAnon, .{
3016 .index = anon.index,
3017 .id = anon.id,
3018 .fields_len = aggregate_info.fields.len,
3019 }, aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len),
3020 .fwd_decl => |fwd_decl| try pool.addExtra(allocator, Aggregate, .{
3021 .fwd_decl = pool_adapter.copy(fwd_decl).index,
3022 .fields_len = aggregate_info.fields.len,
3023 }, aggregate_info.fields.len * @typeInfo(Field).@"struct".fields.len),
3024 },
3025 });
3026 for (0..aggregate_info.fields.len) |field_index| {
3027 const field = aggregate_info.fields.at(field_index, source_pool);
3028 const field_name = if (field.name.toPoolSlice(source_pool)) |slice|
3029 try pool.string(allocator, slice)
3030 else
3031 field.name;
3032 pool.addExtraAssumeCapacity(Field, .{
3033 .name = field_name.index,
3034 .ctype = pool_adapter.copy(field.ctype).index,
3035 .flags = .{ .alignas = field.alignas },
3036 });
3037 }
3038 },
3039 .function => |function_info| {
3040 pool.items.appendAssumeCapacity(.{
3041 .tag = tag,
3042 .data = try pool.addExtra(allocator, Function, .{
3043 .return_ctype = pool_adapter.copy(function_info.return_ctype).index,
3044 .param_ctypes_len = function_info.param_ctypes.len,
3045 }, function_info.param_ctypes.len),
3046 });
3047 for (0..function_info.param_ctypes.len) |param_index| pool.extra.appendAssumeCapacity(
3048 @intFromEnum(pool_adapter.copy(
3049 function_info.param_ctypes.at(param_index, source_pool),
3050 ).index),
3051 );
3052 },
3053 };
3054 assert(source_info.eqlAdapted(source_pool, ctype, pool, pool_adapter));
3055 assert(source_ctype.hash(source_pool) == ctype.hash(pool));
3056 return .{ ctype, gop.found_existing };
3057 }
3058
3059 pub fn string(pool: *Pool, allocator: std.mem.Allocator, slice: []const u8) !String {
3060 try pool.string_bytes.appendSlice(allocator, slice);
3061 return pool.trailingString(allocator);
3062 }
3063
3064 pub fn fmt(
3065 pool: *Pool,
3066 allocator: std.mem.Allocator,
3067 comptime fmt_str: []const u8,
3068 fmt_args: anytype,
3069 ) !String {
3070 try pool.string_bytes.print(allocator, fmt_str, fmt_args);
3071 return pool.trailingString(allocator);
3072 }
3073
3074 fn ensureUnusedCapacity(pool: *Pool, allocator: std.mem.Allocator, len: u32) !void {
3075 try pool.map.ensureUnusedCapacity(allocator, len);
3076 try pool.items.ensureUnusedCapacity(allocator, len);
3077 }
3078
3079 const Hasher = struct {
3080 const Impl = std.hash.Wyhash;
3081 impl: Impl,
3082
3083 const init: Hasher = .{ .impl = Impl.init(0) };
3084
3085 fn updateExtra(hasher: *Hasher, comptime Extra: type, extra: Extra, pool: *const Pool) void {
3086 inline for (@typeInfo(Extra).@"struct".fields) |field| {
3087 const value = @field(extra, field.name);
3088 switch (field.type) {
3089 Pool.Tag, String, CType => unreachable,
3090 CType.Index => hasher.update((CType{ .index = value }).hash(pool)),
3091 String.Index => if ((String{ .index = value }).toPoolSlice(pool)) |slice|
3092 hasher.update(slice)
3093 else
3094 hasher.update(@intFromEnum(value)),
3095 else => hasher.update(value),
3096 }
3097 }
3098 }
3099 fn update(hasher: *Hasher, data: anytype) void {
3100 switch (@TypeOf(data)) {
3101 Pool.Tag => @compileError("pass tag to final"),
3102 CType, CType.Index => @compileError("hash ctype.hash(pool) instead"),
3103 String, String.Index => @compileError("hash string.slice(pool) instead"),
3104 u32, InternPool.Index, Aligned.Flags => hasher.impl.update(std.mem.asBytes(&data)),
3105 []const u8 => hasher.impl.update(data),
3106 else => @compileError("unhandled type: " ++ @typeName(@TypeOf(data))),
3107 }
3108 }
3109
3110 fn final(hasher: Hasher, tag: Pool.Tag) Map.Hash {
3111 var impl = hasher.impl;
3112 impl.update(std.mem.asBytes(&tag));
3113 return @truncate(impl.final());
3114 }
3115 };
3116
3117 fn tagData(
3118 pool: *Pool,
3119 allocator: std.mem.Allocator,
3120 hasher: Hasher,
3121 tag: Pool.Tag,
3122 data: u32,
3123 ) !CType {
3124 try pool.ensureUnusedCapacity(allocator, 1);
3125 const Key = struct { hash: Map.Hash, tag: Pool.Tag, data: u32 };
3126 const CTypeAdapter = struct {
3127 pool: *const Pool,
3128 pub fn hash(_: @This(), key: Key) Map.Hash {
3129 return key.hash;
3130 }
3131 pub fn eql(ctype_adapter: @This(), lhs_key: Key, _: void, rhs_index: usize) bool {
3132 const rhs_item = ctype_adapter.pool.items.get(rhs_index);
3133 return lhs_key.tag == rhs_item.tag and lhs_key.data == rhs_item.data;
3134 }
3135 };
3136 const gop = pool.map.getOrPutAssumeCapacityAdapted(
3137 Key{ .hash = hasher.final(tag), .tag = tag, .data = data },
3138 CTypeAdapter{ .pool = pool },
3139 );
3140 if (!gop.found_existing) pool.items.appendAssumeCapacity(.{ .tag = tag, .data = data });
3141 return .fromPoolIndex(gop.index);
3142 }
3143
3144 fn tagExtra(
3145 pool: *Pool,
3146 allocator: std.mem.Allocator,
3147 tag: Pool.Tag,
3148 comptime Extra: type,
3149 extra: Extra,
3150 ) !CType {
3151 var hasher = Hasher.init;
3152 hasher.updateExtra(Extra, extra, pool);
3153 return pool.tagTrailingExtra(
3154 allocator,
3155 hasher,
3156 tag,
3157 try pool.addExtra(allocator, Extra, extra, 0),
3158 );
3159 }
3160
3161 fn tagTrailingExtra(
3162 pool: *Pool,
3163 allocator: std.mem.Allocator,
3164 hasher: Hasher,
3165 tag: Pool.Tag,
3166 extra_index: ExtraIndex,
3167 ) !CType {
3168 try pool.ensureUnusedCapacity(allocator, 1);
3169 return pool.tagTrailingExtraAssumeCapacity(hasher, tag, extra_index);
3170 }
3171
3172 fn tagTrailingExtraAssumeCapacity(
3173 pool: *Pool,
3174 hasher: Hasher,
3175 tag: Pool.Tag,
3176 extra_index: ExtraIndex,
3177 ) CType {
3178 const Key = struct { hash: Map.Hash, tag: Pool.Tag, extra: []const u32 };
3179 const CTypeAdapter = struct {
3180 pool: *const Pool,
3181 pub fn hash(_: @This(), key: Key) Map.Hash {
3182 return key.hash;
3183 }
3184 pub fn eql(ctype_adapter: @This(), lhs_key: Key, _: void, rhs_index: usize) bool {
3185 const rhs_item = ctype_adapter.pool.items.get(rhs_index);
3186 if (lhs_key.tag != rhs_item.tag) return false;
3187 const rhs_extra = ctype_adapter.pool.extra.items[rhs_item.data..];
3188 return std.mem.startsWith(u32, rhs_extra, lhs_key.extra);
3189 }
3190 };
3191 const gop = pool.map.getOrPutAssumeCapacityAdapted(
3192 Key{ .hash = hasher.final(tag), .tag = tag, .extra = pool.extra.items[extra_index..] },
3193 CTypeAdapter{ .pool = pool },
3194 );
3195 if (gop.found_existing)
3196 pool.extra.shrinkRetainingCapacity(extra_index)
3197 else
3198 pool.items.appendAssumeCapacity(.{ .tag = tag, .data = extra_index });
3199 return .fromPoolIndex(gop.index);
3200 }
3201
3202 fn sortFields(fields: []Info.Field) void {
3203 std.mem.sort(Info.Field, fields, {}, struct {
3204 fn before(_: void, lhs_field: Info.Field, rhs_field: Info.Field) bool {
3205 return lhs_field.alignas.order(rhs_field.alignas).compare(.gt);
3206 }
3207 }.before);
3208 }
3209
3210 fn trailingString(pool: *Pool, allocator: std.mem.Allocator) !String {
3211 const start = pool.string_indices.getLast();
3212 const slice: []const u8 = pool.string_bytes.items[start..];
3213 if (slice.len >= 2 and slice[0] == 'f' and switch (slice[1]) {
3214 '0' => slice.len == 2,
3215 '1'...'9' => true,
3216 else => false,
3217 }) if (std.fmt.parseInt(u31, slice[1..], 10)) |unnamed| {
3218 pool.string_bytes.shrinkRetainingCapacity(start);
3219 return String.fromUnnamed(unnamed);
3220 } else |_| {};
3221 if (std.meta.stringToEnum(String.Index, slice)) |index| {
3222 pool.string_bytes.shrinkRetainingCapacity(start);
3223 return .{ .index = index };
3224 }
3225
3226 try pool.string_map.ensureUnusedCapacity(allocator, 1);
3227 try pool.string_indices.ensureUnusedCapacity(allocator, 1);
3228
3229 const gop = pool.string_map.getOrPutAssumeCapacityAdapted(slice, String.Adapter{ .pool = pool });
3230 if (gop.found_existing)
3231 pool.string_bytes.shrinkRetainingCapacity(start)
3232 else
3233 pool.string_indices.appendAssumeCapacity(@intCast(pool.string_bytes.items.len));
3234 return String.fromPoolIndex(gop.index);
3235 }
3236
3237 const Item = struct {
3238 tag: Pool.Tag,
3239 data: u32,
3240 };
3241
3242 const ExtraIndex = u32;
3243
3244 const Tag = enum(u8) {
3245 basic,
3246 pointer,
3247 pointer_const,
3248 pointer_volatile,
3249 pointer_const_volatile,
3250 aligned,
3251 array_small,
3252 array_large,
3253 vector,
3254 nonstring,
3255 fwd_decl_struct_anon,
3256 fwd_decl_union_anon,
3257 fwd_decl_struct,
3258 fwd_decl_union,
3259 aggregate_struct_anon,
3260 aggregate_struct_packed_anon,
3261 aggregate_union_anon,
3262 aggregate_union_packed_anon,
3263 aggregate_struct,
3264 aggregate_struct_packed,
3265 aggregate_union,
3266 aggregate_union_packed,
3267 function,
3268 function_varargs,
3269 };
3270
3271 const Aligned = struct {
3272 ctype: CType.Index,
3273 flags: Flags,
3274
3275 const Flags = packed struct(u32) {
3276 alignas: AlignAs,
3277 _: u20 = 0,
3278 };
3279 };
3280
3281 const SequenceSmall = struct {
3282 elem_ctype: CType.Index,
3283 len: u32,
3284 };
3285
3286 const SequenceLarge = struct {
3287 elem_ctype: CType.Index,
3288 len_lo: u32,
3289 len_hi: u32,
3290
3291 fn len(extra: SequenceLarge) u64 {
3292 return @as(u64, extra.len_lo) << 0 |
3293 @as(u64, extra.len_hi) << 32;
3294 }
3295 };
3296
3297 const Field = struct {
3298 name: String.Index,
3299 ctype: CType.Index,
3300 flags: Flags,
3301
3302 const Flags = Aligned.Flags;
3303 };
3304
3305 const FwdDeclAnon = struct {
3306 fields_len: u32,
3307 };
3308
3309 const AggregateAnon = struct {
3310 index: InternPool.Index,
3311 id: u32,
3312 fields_len: u32,
3313 };
3314
3315 const Aggregate = struct {
3316 fwd_decl: CType.Index,
3317 fields_len: u32,
3318 };
3319
3320 const Function = struct {
3321 return_ctype: CType.Index,
3322 param_ctypes_len: u32,
3323 };
3324
3325 fn addExtra(
3326 pool: *Pool,
3327 allocator: std.mem.Allocator,
3328 comptime Extra: type,
3329 extra: Extra,
3330 trailing_len: usize,
3331 ) !ExtraIndex {
3332 try pool.extra.ensureUnusedCapacity(
3333 allocator,
3334 @typeInfo(Extra).@"struct".fields.len + trailing_len,
3335 );
3336 defer pool.addExtraAssumeCapacity(Extra, extra);
3337 return @intCast(pool.extra.items.len);
3338 }
3339 fn addExtraAssumeCapacity(pool: *Pool, comptime Extra: type, extra: Extra) void {
3340 addExtraAssumeCapacityTo(&pool.extra, Extra, extra);
3341 }
3342 fn addExtraAssumeCapacityTo(
3343 array: *std.ArrayList(u32),
3344 comptime Extra: type,
3345 extra: Extra,
3346 ) void {
3347 inline for (@typeInfo(Extra).@"struct".fields) |field| {
3348 const value = @field(extra, field.name);
3349 array.appendAssumeCapacity(switch (field.type) {
3350 u32 => value,
3351 CType.Index, String.Index, InternPool.Index => @intFromEnum(value),
3352 Aligned.Flags => @bitCast(value),
3353 else => @compileError("bad field type: " ++ field.name ++ ": " ++
3354 @typeName(field.type)),
3355 });
3356 }
3357 }
3358
3359 fn addHashedExtra(
3360 pool: *Pool,
3361 allocator: std.mem.Allocator,
3362 hasher: *Hasher,
3363 comptime Extra: type,
3364 extra: Extra,
3365 trailing_len: usize,
3366 ) !ExtraIndex {
3367 hasher.updateExtra(Extra, extra, pool);
3368 return pool.addExtra(allocator, Extra, extra, trailing_len);
3369 }
3370 fn addHashedExtraAssumeCapacity(
3371 pool: *Pool,
3372 hasher: *Hasher,
3373 comptime Extra: type,
3374 extra: Extra,
3375 ) void {
3376 hasher.updateExtra(Extra, extra, pool);
3377 pool.addExtraAssumeCapacity(Extra, extra);
3378 }
3379 fn addHashedExtraAssumeCapacityTo(
3380 pool: *Pool,
3381 array: *std.ArrayList(u32),
3382 hasher: *Hasher,
3383 comptime Extra: type,
3384 extra: Extra,
3385 ) void {
3386 hasher.updateExtra(Extra, extra, pool);
3387 addExtraAssumeCapacityTo(array, Extra, extra);
3388 }
3389
3390 const ExtraTrail = struct {
3391 extra_index: ExtraIndex,
3392
3393 fn next(
3394 extra_trail: *ExtraTrail,
3395 len: u32,
3396 comptime Extra: type,
3397 pool: *const Pool,
3398 ) []const Extra {
3399 defer extra_trail.extra_index += @intCast(len);
3400 return @ptrCast(pool.extra.items[extra_trail.extra_index..][0..len]);
3401 }
3402 };
3403
3404 fn getExtraTrail(
3405 pool: *const Pool,
3406 comptime Extra: type,
3407 extra_index: ExtraIndex,
3408 ) struct { extra: Extra, trail: ExtraTrail } {
3409 var extra: Extra = undefined;
3410 const fields = @typeInfo(Extra).@"struct".fields;
3411 inline for (fields, pool.extra.items[extra_index..][0..fields.len]) |field, value|
3412 @field(extra, field.name) = switch (field.type) {
3413 u32 => value,
3414 CType.Index, String.Index, InternPool.Index => @enumFromInt(value),
3415 Aligned.Flags => @bitCast(value),
3416 else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
3417 };
3418 return .{
3419 .extra = extra,
3420 .trail = .{ .extra_index = extra_index + @as(ExtraIndex, @intCast(fields.len)) },
3421 };
3422 }
3423
3424 fn getExtra(pool: *const Pool, comptime Extra: type, extra_index: ExtraIndex) Extra {
3425 return pool.getExtraTrail(Extra, extra_index).extra;
3426 }
3427};
3428
3429pub const AlignAs = packed struct {
3430 @"align": InternPool.Alignment,
3431 abi: InternPool.Alignment,
3432
3433 pub fn fromAlignment(alignas: AlignAs) AlignAs {
3434 assert(alignas.abi != .none);
3435 return .{
3436 .@"align" = if (alignas.@"align" != .none) alignas.@"align" else alignas.abi,
3437 .abi = alignas.abi,
3438 };
3439 }
3440 pub fn fromAbiAlignment(abi: InternPool.Alignment) AlignAs {
3441 assert(abi != .none);
3442 return .{ .@"align" = abi, .abi = abi };
3443 }
3444 pub fn fromByteUnits(@"align": u64, abi: u64) AlignAs {
3445 return fromAlignment(.{
3446 .@"align" = InternPool.Alignment.fromByteUnits(@"align"),
3447 .abi = InternPool.Alignment.fromNonzeroByteUnits(abi),
3448 });
3449 }
3450
3451 pub fn order(lhs: AlignAs, rhs: AlignAs) std.math.Order {
3452 return lhs.@"align".order(rhs.@"align");
3453 }
3454 pub fn abiOrder(alignas: AlignAs) std.math.Order {
3455 return alignas.@"align".order(alignas.abi);
3456 }
3457 pub fn toByteUnits(alignas: AlignAs) u64 {
3458 return alignas.@"align".toByteUnits().?;
3459 }
3460};
3461
3462const std = @import("std");
3463const assert = std.debug.assert;
3464const Writer = std.Io.Writer;
3465
3466const CType = @This();
3467const InternPool = @import("../../InternPool.zig");
3468const Module = @import("../../Package/Module.zig");
3469const Type = @import("../../Type.zig");
3470const Value = @import("../../Value.zig");
3471const Zcu = @import("../../Zcu.zig");
src/codegen/c/type.zig created+1013
......@@ -0,0 +1,1013 @@
1pub const CType = union(enum) {
2 pub const render_defs = @import("type/render_defs.zig");
3
4 // The first nodes are primitive types (or standard typedefs).
5
6 void,
7 bool,
8 int: Int,
9 float: Float,
10
11 // These next nodes are all typedefs, structs, or unions.
12
13 @"fn": Type,
14 @"enum": Type,
15 bitpack: Type,
16 @"struct": Type,
17 union_auto: Type,
18 union_extern: Type,
19 slice: Type,
20 opt: Type,
21 arr: Type,
22 vec: Type,
23 errunion: struct { payload_ty: Type },
24 aligned: struct {
25 ty: Type,
26 alignment: InternPool.Alignment,
27 },
28 bigint: BigInt,
29
30 // The remaining nodes have children.
31
32 pointer: struct {
33 @"const": bool,
34 @"volatile": bool,
35 elem_ty: *const CType,
36 nonstring: bool,
37 },
38 array: struct {
39 len: u64,
40 elem_ty: *const CType,
41 nonstring: bool,
42 },
43 function: struct {
44 param_tys: []const CType,
45 ret_ty: *const CType,
46 varargs: bool,
47 },
48
49 /// Returns `true` if this node has a postfix operator, meaning an `[...]` or `(...)` appears
50 /// after the identifier in a declarator with this type. In this case, if this node is wrapped
51 /// in a pointer type, we will need to add parentheses due to operator precedence.
52 ///
53 /// For instance, when lowering a Zig declaration `foo: *const fn (c_int) void`, it would be a
54 /// bug to write the C declarator as `void *foo(int)`, because the `(int)` suffix declaring the
55 /// function type has higher precedence than the `*` prefix declaring the pointer type. Instead,
56 /// this type must be lowered as `void (*foo)(int)`.
57 fn kind(cty: *const CType) enum {
58 /// `cty` is just a C type specifier, i.e. a typedef or a named struct/union type.
59 specifier,
60 /// `cty` is a C function or array type. It will have a postfix "operator" in its suffix to
61 /// declare the type, either `(...)` (for a function type) or `[...]` (for an array type).
62 postfix_op,
63 /// `cty` is a C pointer type. Its prefix will end with "*".
64 pointer,
65 } {
66 return switch (cty.*) {
67 .void,
68 .bool,
69 .int,
70 .float,
71 .@"fn",
72 .@"enum",
73 .bitpack,
74 .@"struct",
75 .union_auto,
76 .union_extern,
77 .slice,
78 .opt,
79 .arr,
80 .vec,
81 .errunion,
82 .aligned,
83 .bigint,
84 => .specifier,
85
86 .array,
87 .function,
88 => .postfix_op,
89
90 .pointer => .pointer,
91 };
92 }
93
94 pub const Int = enum {
95 char,
96
97 @"unsigned short",
98 @"unsigned int",
99 @"unsigned long",
100 @"unsigned long long",
101
102 @"signed short",
103 @"signed int",
104 @"signed long",
105 @"signed long long",
106
107 uint8_t,
108 uint16_t,
109 uint32_t,
110 uint64_t,
111 zig_u128,
112
113 int8_t,
114 int16_t,
115 int32_t,
116 int64_t,
117 zig_i128,
118
119 uintptr_t,
120 intptr_t,
121
122 pub fn bits(int: Int, target: *const std.Target) u16 {
123 return switch (int) {
124 // zig fmt: off
125 .char => target.cTypeBitSize(.char),
126
127 .@"unsigned short" => target.cTypeBitSize(.ushort),
128 .@"unsigned int" => target.cTypeBitSize(.uint),
129 .@"unsigned long" => target.cTypeBitSize(.ulong),
130 .@"unsigned long long" => target.cTypeBitSize(.ulonglong),
131
132 .@"signed short" => target.cTypeBitSize(.short),
133 .@"signed int" => target.cTypeBitSize(.int),
134 .@"signed long" => target.cTypeBitSize(.long),
135 .@"signed long long" => target.cTypeBitSize(.longlong),
136
137 .uintptr_t, .intptr_t => target.ptrBitWidth(),
138
139 .uint8_t, .int8_t => 8,
140 .uint16_t, .int16_t => 16,
141 .uint32_t, .int32_t => 32,
142 .uint64_t, .int64_t => 64,
143 .zig_u128, .zig_i128 => 128,
144 // zig fmt: on
145 };
146 }
147 };
148
149 pub const BigInt = struct {
150 limb_size: LimbSize,
151 /// Always greater than 1.
152 limbs_len: u16,
153
154 pub const LimbSize = enum {
155 @"8",
156 @"16",
157 @"32",
158 @"64",
159 @"128",
160 pub fn bits(s: LimbSize) u8 {
161 return switch (s) {
162 .@"8" => 8,
163 .@"16" => 16,
164 .@"32" => 32,
165 .@"64" => 64,
166 .@"128" => 128,
167 };
168 }
169 pub fn unsigned(s: LimbSize) Int {
170 return switch (s) {
171 .@"8" => .uint8_t,
172 .@"16" => .uint16_t,
173 .@"32" => .uint32_t,
174 .@"64" => .uint64_t,
175 .@"128" => .zig_u128,
176 };
177 }
178 pub fn signed(s: LimbSize) Int {
179 return switch (s) {
180 .@"8" => .int8_t,
181 .@"16" => .int16_t,
182 .@"32" => .int32_t,
183 .@"64" => .int64_t,
184 .@"128" => .zig_i128,
185 };
186 }
187 };
188 };
189
190 pub const Float = enum {
191 @"long double",
192 zig_f16,
193 zig_f32,
194 zig_f64,
195 zig_f80,
196 zig_f128,
197 zig_u128,
198 zig_i128,
199 };
200
201 pub fn isStringElem(cty: CType) bool {
202 return switch (cty) {
203 .int => |int| switch (int) {
204 .char, .int8_t, .uint8_t => true,
205 else => false,
206 },
207 else => false,
208 };
209 }
210
211 pub fn lower(
212 ty: Type,
213 deps: *Dependencies,
214 arena: Allocator,
215 zcu: *const Zcu,
216 ) Allocator.Error!CType {
217 return lowerInner(ty, false, deps, arena, zcu);
218 }
219 fn lowerInner(
220 start_ty: Type,
221 allow_incomplete: bool,
222 deps: *Dependencies,
223 arena: Allocator,
224 zcu: *const Zcu,
225 ) Allocator.Error!CType {
226 const gpa = zcu.comp.gpa;
227 const ip = &zcu.intern_pool;
228 var cur_ty = start_ty;
229 while (true) {
230 switch (cur_ty.zigTypeTag(zcu)) {
231 .type,
232 .comptime_int,
233 .comptime_float,
234 .undefined,
235 .null,
236 .enum_literal,
237 .@"opaque",
238 .noreturn,
239 .void,
240 => return .void,
241
242 .bool => return .bool,
243
244 .int, .error_set => switch (classifyInt(cur_ty, zcu)) {
245 .void => return .void,
246 .small => |s| return .{ .int = s },
247 .big => |big| {
248 try deps.bigint.put(gpa, big, {});
249 return .{ .bigint = big };
250 },
251 },
252
253 .float => return .{ .float = switch (cur_ty.toIntern()) {
254 .c_longdouble_type => .@"long double",
255 .f16_type => .zig_f16,
256 .f32_type => .zig_f32,
257 .f64_type => .zig_f64,
258 .f80_type => .zig_f80,
259 .f128_type => .zig_f128,
260 else => unreachable,
261 } },
262 .vector => {
263 try deps.addType(gpa, cur_ty, allow_incomplete);
264 return .{ .vec = cur_ty };
265 },
266 .array => {
267 try deps.addType(gpa, cur_ty, allow_incomplete);
268 return .{ .arr = cur_ty };
269 },
270
271 .pointer => {
272 const ptr = cur_ty.ptrInfo(zcu);
273 switch (ptr.flags.size) {
274 .slice => {
275 try deps.addType(gpa, cur_ty, allow_incomplete);
276 return .{ .slice = cur_ty };
277 },
278 .one, .many, .c => {
279 const elem_ty: Type = .fromInterned(ptr.child);
280 const is_fn_ptr = elem_ty.zigTypeTag(zcu) == .@"fn";
281 const elem_cty: CType = elem_cty: {
282 if (ptr.packed_offset.host_size > 0 and ptr.flags.vector_index == .none) {
283 switch (classifyBitInt(.unsigned, ptr.packed_offset.host_size * 8, zcu)) {
284 .void => break :elem_cty .void,
285 .small => |s| break :elem_cty .{ .int = s },
286 .big => |big| {
287 try deps.bigint.put(gpa, big, {});
288 break :elem_cty .{ .bigint = big };
289 },
290 }
291 }
292 if (ptr.flags.alignment != .none and !is_fn_ptr) {
293 // The pointer has an explicit alignment---if it's an underalignment
294 // then we need to use an "aligned" typedef.
295 const ptr_align = ptr.flags.alignment;
296 if (!alwaysHasLayout(elem_ty, ip) or
297 ptr_align.compareStrict(.lt, elem_ty.abiAlignment(zcu)))
298 {
299 const gop = try deps.aligned_type_fwd.getOrPut(gpa, elem_ty.toIntern());
300 if (!gop.found_existing) gop.value_ptr.* = 0;
301 gop.value_ptr.* |= @as(u64, 1) << ptr_align.toLog2Units();
302 break :elem_cty .{ .aligned = .{
303 .ty = elem_ty,
304 .alignment = ptr_align,
305 } };
306 }
307 }
308 break :elem_cty try .lowerInner(elem_ty, true, deps, arena, zcu);
309 };
310 const elem_cty_buf = try arena.create(CType);
311 elem_cty_buf.* = elem_cty;
312 return .{ .pointer = .{
313 .@"const" = ptr.flags.is_const and !is_fn_ptr,
314 .@"volatile" = ptr.flags.is_volatile and !is_fn_ptr,
315 .elem_ty = elem_cty_buf,
316 .nonstring = nonstring: {
317 if (!elem_cty.isStringElem()) break :nonstring false;
318 if (ptr.sentinel == .none) break :nonstring true;
319 break :nonstring Value.compareHetero(
320 .fromInterned(ptr.sentinel),
321 .neq,
322 .zero_comptime_int,
323 zcu,
324 );
325 },
326 } };
327 },
328 }
329 },
330
331 .@"fn" => {
332 const func_type = ip.indexToKey(cur_ty.toIntern()).func_type;
333 direct: {
334 const ret_ty: Type = .fromInterned(func_type.return_type);
335 if (!alwaysHasLayout(ret_ty, ip)) break :direct;
336 var params_len: usize = 0; // only counts parameter types with runtime bits
337 for (func_type.param_types.get(ip)) |param_ty_ip| {
338 const param_ty: Type = .fromInterned(param_ty_ip);
339 if (!alwaysHasLayout(param_ty, ip)) break :direct;
340 if (param_ty.hasRuntimeBits(zcu)) params_len += 1;
341 }
342 // We can actually write this function type directly!
343 if (!cur_ty.fnHasRuntimeBits(zcu)) return .void;
344 const ret_cty_buf = try arena.create(CType);
345 if (!ret_ty.hasRuntimeBits(zcu)) {
346 // Incomplete function return types must always be `void`.
347 ret_cty_buf.* = .void;
348 } else {
349 ret_cty_buf.* = try .lowerInner(ret_ty, allow_incomplete, deps, arena, zcu);
350 }
351 const param_cty_buf = try arena.alloc(CType, params_len);
352 var param_index: usize = 0;
353 for (func_type.param_types.get(ip)) |param_ty_ip| {
354 const param_ty: Type = .fromInterned(param_ty_ip);
355 if (!param_ty.hasRuntimeBits(zcu)) continue;
356 param_cty_buf[param_index] = try .lowerInner(param_ty, allow_incomplete, deps, arena, zcu);
357 param_index += 1;
358 }
359 assert(param_index == params_len);
360 return .{ .function = .{
361 .ret_ty = ret_cty_buf,
362 .param_tys = param_cty_buf,
363 .varargs = func_type.is_var_args,
364 } };
365 }
366 try deps.addType(gpa, cur_ty, allow_incomplete);
367 return .{ .@"fn" = cur_ty };
368 },
369
370 .@"struct" => {
371 try deps.addType(gpa, cur_ty, allow_incomplete);
372 switch (cur_ty.containerLayout(zcu)) {
373 .auto, .@"extern" => return .{ .@"struct" = cur_ty },
374 .@"packed" => return .{ .bitpack = cur_ty },
375 }
376 },
377 .@"union" => {
378 try deps.addType(gpa, cur_ty, allow_incomplete);
379 switch (cur_ty.containerLayout(zcu)) {
380 .auto => return .{ .union_auto = cur_ty },
381 .@"extern" => return .{ .union_extern = cur_ty },
382 .@"packed" => return .{ .bitpack = cur_ty },
383 }
384 },
385 .@"enum" => {
386 try deps.addType(gpa, cur_ty, allow_incomplete);
387 return .{ .@"enum" = cur_ty };
388 },
389
390 .optional => {
391 // This query does not require any type resolution.
392 if (cur_ty.optionalReprIsPayload(zcu)) {
393 // Either a pointer-like optional, or an optional error set. Just lower the payload.
394 cur_ty = cur_ty.optionalChild(zcu);
395 continue;
396 }
397 if (alwaysHasLayout(cur_ty, ip)) switch (classifyOptional(cur_ty, zcu)) {
398 .error_set, .ptr_like, .slice_like => unreachable, // handled above
399 .npv_payload => return .void,
400 .opv_payload, .@"struct" => {},
401 };
402 try deps.addType(gpa, cur_ty, allow_incomplete);
403 return .{ .opt = cur_ty };
404 },
405
406 .error_union => {
407 const payload_ty = cur_ty.errorUnionPayload(zcu);
408 if (allow_incomplete) {
409 try deps.errunion_type_fwd.put(gpa, payload_ty.toIntern(), {});
410 } else {
411 try deps.errunion_type.put(gpa, payload_ty.toIntern(), {});
412 }
413 return .{ .errunion = .{
414 .payload_ty = payload_ty,
415 } };
416 },
417
418 .frame,
419 .@"anyframe",
420 => unreachable,
421 }
422 comptime unreachable;
423 }
424 }
425
426 pub fn classifyOptional(opt_ty: Type, zcu: *const Zcu) enum {
427 /// The optional is something like `?noreturn`; it lowers to `void`.
428 npv_payload,
429 /// The payload type is an error set; the representation matches that of the error set, with
430 /// the value 0 representing `null`.
431 error_set,
432 /// The payload type is a non-optional pointer; the NULL pointer is used for `null`.
433 ptr_like,
434 /// The payload type is a non-optional slice; a NULL pointer field is used for `null`.
435 slice_like,
436 /// The optional is something like `?void`; it lowers to a struct, but one containing only
437 /// one field `is_null` (the payload is omitted).
438 opv_payload,
439 /// The optional uses the "default" lowering of a struct with two fields, like this:
440 /// struct optional_1234 { payload_ty payload; bool is_null; }
441 @"struct",
442 } {
443 const payload_ty = opt_ty.optionalChild(zcu);
444 if (opt_ty.optionalReprIsPayload(zcu)) {
445 return switch (payload_ty.zigTypeTag(zcu)) {
446 .error_set => .error_set,
447 .pointer => if (payload_ty.isSlice(zcu)) .slice_like else .ptr_like,
448 else => unreachable,
449 };
450 } else {
451 return switch (payload_ty.classify(zcu)) {
452 .no_possible_value => .npv_payload,
453 .one_possible_value => .opv_payload,
454 else => .@"struct",
455 };
456 }
457 }
458
459 pub const IntClass = union(enum) {
460 /// The integer type is zero-bit, so lowers to `void`.
461 void,
462 /// The integer is under 128 bits long, so lowers to this C integer type.
463 small: Int,
464 /// The integer is over 128 bits long, so lowers to an array of limbs.
465 big: BigInt,
466 };
467
468 /// Asserts that `ty` is an integer, enum, bitpack, or error set.
469 pub fn classifyInt(ty: Type, zcu: *const Zcu) IntClass {
470 const int_ty: Type = switch (ty.zigTypeTag(zcu)) {
471 .error_set => return classifyBitInt(.unsigned, zcu.errorSetBits(), zcu),
472 .@"enum" => ty.intTagType(zcu),
473 .@"struct", .@"union" => ty.bitpackBackingInt(zcu),
474 .int => ty,
475 else => unreachable,
476 };
477 switch (int_ty.toIntern()) {
478 // zig fmt: off
479 .usize_type => return .{ .small = .uintptr_t },
480 .isize_type => return .{ .small = .intptr_t },
481
482 .c_char_type => return .{ .small = .char },
483
484 .c_short_type => return .{ .small = .@"signed short" },
485 .c_int_type => return .{ .small = .@"signed int" },
486 .c_long_type => return .{ .small = .@"signed long" },
487 .c_longlong_type => return .{ .small = .@"signed long long" },
488
489 .c_ushort_type => return .{ .small = .@"unsigned short" },
490 .c_uint_type => return .{ .small = .@"unsigned int" },
491 .c_ulong_type => return .{ .small = .@"unsigned long" },
492 .c_ulonglong_type => return .{ .small = .@"unsigned long long" },
493 // zig fmt: on
494
495 else => {
496 const int = ty.intInfo(zcu);
497 return classifyBitInt(int.signedness, int.bits, zcu);
498 },
499 }
500 }
501 fn classifyBitInt(signedness: std.builtin.Signedness, bits: u16, zcu: *const Zcu) IntClass {
502 return switch (bits) {
503 0 => .void,
504 1...8 => switch (signedness) {
505 .unsigned => .{ .small = .uint8_t },
506 .signed => .{ .small = .int8_t },
507 },
508 9...16 => switch (signedness) {
509 .unsigned => .{ .small = .uint16_t },
510 .signed => .{ .small = .int16_t },
511 },
512 17...32 => switch (signedness) {
513 .unsigned => .{ .small = .uint32_t },
514 .signed => .{ .small = .int32_t },
515 },
516 33...64 => switch (signedness) {
517 .unsigned => .{ .small = .uint64_t },
518 .signed => .{ .small = .int64_t },
519 },
520 65...128 => switch (signedness) {
521 .unsigned => .{ .small = .zig_u128 },
522 .signed => .{ .small = .zig_i128 },
523 },
524 else => {
525 @branchHint(.unlikely);
526 const target = zcu.getTarget();
527 const limb_bytes = std.zig.target.intAlignment(target, bits);
528 return .{ .big = .{
529 .limb_size = switch (limb_bytes) {
530 1 => .@"8",
531 2 => .@"16",
532 4 => .@"32",
533 8 => .@"64",
534 16 => .@"128",
535 else => unreachable,
536 },
537 .limbs_len = @divExact(
538 std.zig.target.intByteSize(target, bits),
539 limb_bytes,
540 ),
541 } };
542 },
543 };
544 }
545
546 /// Describes a set of types which must be declared or completed in the C source file before
547 /// some string of rendered C code (such as a function), due to said C code using these types.
548 pub const Dependencies = struct {
549 /// Key is any Zig type which corresponds to a C `struct`, `union`, or `typedef`. That C
550 /// type must be declared and complete.
551 type: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
552
553 /// Key is a Zig type which is the *payload* of an error union. The C `struct` type
554 /// corresponding to such an error union must be declared and complete.
555 ///
556 /// These are separate from `type` to avoid redundant types for every different error set
557 /// used with the same payload type---for instance a different C type for every `E!void`.
558 errunion_type: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
559
560 /// Like `type`, but the type does not necessarily need to be completed yet: a forward
561 /// declaration is sufficient.
562 type_fwd: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
563
564 /// Like `errunion_type`, but the type does not necessarily need to be completed yet: a
565 /// forward declaration is sufficient.
566 errunion_type_fwd: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
567
568 /// Key is a Zig type; value is a bitmask of alignments. For every bit which is set, an
569 /// aligned typedef is required. For instance, if bit 3 is set, the C type 'aligned__8_foo'
570 /// must be declared through `typedef` (but not necessarily completed yet).
571 aligned_type_fwd: std.AutoArrayHashMapUnmanaged(InternPool.Index, u64),
572
573 /// Key specifies a big-int type whose C `struct` must be declared and complete.
574 bigint: std.AutoArrayHashMapUnmanaged(BigInt, void),
575
576 pub const empty: Dependencies = .{
577 .type = .empty,
578 .errunion_type = .empty,
579 .type_fwd = .empty,
580 .errunion_type_fwd = .empty,
581 .aligned_type_fwd = .empty,
582 .bigint = .empty,
583 };
584
585 pub fn deinit(deps: *Dependencies, gpa: Allocator) void {
586 deps.type.deinit(gpa);
587 deps.errunion_type.deinit(gpa);
588 deps.type_fwd.deinit(gpa);
589 deps.errunion_type_fwd.deinit(gpa);
590 deps.aligned_type_fwd.deinit(gpa);
591 deps.bigint.deinit(gpa);
592 }
593
594 pub fn clearRetainingCapacity(deps: *Dependencies) void {
595 deps.type.clearRetainingCapacity();
596 deps.errunion_type.clearRetainingCapacity();
597 deps.type_fwd.clearRetainingCapacity();
598 deps.errunion_type_fwd.clearRetainingCapacity();
599 deps.aligned_type_fwd.clearRetainingCapacity();
600 deps.bigint.clearRetainingCapacity();
601 }
602
603 pub fn move(deps: *Dependencies) Dependencies {
604 const moved = deps.*;
605 deps.* = .empty;
606 return moved;
607 }
608
609 fn addType(deps: *Dependencies, gpa: Allocator, ty: Type, allow_incomplete: bool) Allocator.Error!void {
610 if (allow_incomplete) {
611 try deps.type_fwd.put(gpa, ty.toIntern(), {});
612 } else {
613 try deps.type.put(gpa, ty.toIntern(), {});
614 }
615 }
616 };
617
618 /// Formats the bytes which appear *before* the identifier in a declarator. This includes the
619 /// type specifier and all "prefix type operators" in the declarator. e.g:
620 /// * for the declarator "int foo", writes "int "
621 /// * for the declarator "struct thing *foo", writes "struct thing *"
622 /// * for the declarator "void *(*foo)(int)", writes "void *(*"
623 pub fn fmtDeclaratorPrefix(cty: CType, zcu: *const Zcu) Formatter {
624 return .{
625 .cty = cty,
626 .zcu = zcu,
627 .kind = .declarator_prefix,
628 };
629 }
630 /// Formats the bytes which appear *before* the identifier in a declarator. This includes the
631 /// type specifier and all "prefix type operators" in the declarator. e.g:
632 /// * for the declarator "int foo", writes ""
633 /// * for the declarator "struct thing *foo", writes ""
634 /// * for the declarator "void *(*foo)(int)", writes ")(int)"
635 pub fn fmtDeclaratorSuffix(cty: CType, zcu: *const Zcu) Formatter {
636 return .{
637 .cty = cty,
638 .zcu = zcu,
639 .kind = .declarator_suffix,
640 };
641 }
642 /// Like `fmtDeclaratorSuffix`, except never emits a `zig_nonstring` annotation.
643 pub fn fmtDeclaratorSuffixIgnoreNonstring(cty: CType, zcu: *const Zcu) Formatter {
644 return .{
645 .cty = cty,
646 .zcu = zcu,
647 .kind = .declarator_suffix_ignore_nonstring,
648 };
649 }
650 /// Formats a type's full name, e.g. "int", "struct foo *", "void *(uint32_t)".
651 ///
652 /// This is almost identical to `fmtDeclaratorPrefix` followed by `fmtDeclaratorSuffix`, but
653 /// that sequence of calls may emit trailing whitespace where this one does not---for instance,
654 /// those calls would write the type "void" as "void ".
655 pub fn fmtTypeName(cty: CType, zcu: *const Zcu) Formatter {
656 return .{
657 .cty = cty,
658 .zcu = zcu,
659 .kind = .type_name,
660 };
661 }
662
663 const Formatter = struct {
664 cty: CType,
665 zcu: *const Zcu,
666 kind: enum { type_name, declarator_prefix, declarator_suffix, declarator_suffix_ignore_nonstring },
667
668 pub fn format(ctx: Formatter, w: *Writer) Writer.Error!void {
669 switch (ctx.kind) {
670 .type_name => {
671 try ctx.cty.writeTypePrefix(w, ctx.zcu);
672 try ctx.cty.writeTypeSuffix(w, ctx.zcu);
673 },
674 .declarator_prefix => {
675 try ctx.cty.writeTypePrefix(w, ctx.zcu);
676 switch (ctx.cty.kind()) {
677 .specifier => try w.writeByte(' '), // write "int " rather than "int"
678 .pointer => {}, // we already have something like "foo *"
679 .postfix_op => {}, // we already have something like "ret_ty "
680 }
681 },
682 .declarator_suffix => {
683 try ctx.cty.writeTypeSuffix(w, ctx.zcu);
684 const nonstring = switch (ctx.cty) {
685 .array => |arr| arr.nonstring,
686 .pointer => |ptr| ptr.nonstring,
687 else => false,
688 };
689 if (nonstring) try w.writeAll(" zig_nonstring");
690 },
691 .declarator_suffix_ignore_nonstring => {
692 try ctx.cty.writeTypeSuffix(w, ctx.zcu);
693 },
694 }
695 }
696 };
697
698 fn writeTypePrefix(cty: CType, w: *Writer, zcu: *const Zcu) Writer.Error!void {
699 switch (cty) {
700 .void => try w.writeAll("void"),
701 .bool => try w.writeAll("bool"),
702 .int => |int| try w.writeAll(@tagName(int)),
703 .float => |float| try w.writeAll(@tagName(float)),
704 .@"fn" => |ty| try w.print("{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
705 .@"enum" => |ty| try w.print("enum__{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
706 .bitpack => |ty| try w.print("bitpack__{f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
707 .@"struct" => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
708 .union_auto => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
709 .union_extern => |ty| try w.print("union {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
710 .slice => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
711 .opt => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
712 .arr => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
713 .vec => |ty| try w.print("struct {f}_{d}", .{ fmtZigType(ty, zcu), ty.toIntern() }),
714 .errunion => |eu| try w.print("struct errunion_{f}_{d}", .{
715 fmtZigType(eu.payload_ty, zcu),
716 eu.payload_ty.toIntern(),
717 }),
718 .aligned => |aligned| try w.print("aligned__{d}_{f}_{d}", .{
719 aligned.alignment.toByteUnits().?,
720 fmtZigType(aligned.ty, zcu),
721 aligned.ty.toIntern(),
722 }),
723 .bigint => |bigint| try w.print("struct int_{d}x{d}", .{
724 bigint.limb_size.bits(),
725 bigint.limbs_len,
726 }),
727
728 .pointer => |ptr| {
729 try ptr.elem_ty.writeTypePrefix(w, zcu);
730 switch (ptr.elem_ty.kind()) {
731 .pointer, .postfix_op => {},
732 .specifier => {
733 // We want "foo *" or "foo const *" rather than "foo*" or "fooconst *".
734 try w.writeByte(' ');
735 },
736 }
737 if (ptr.@"const") try w.writeAll("const ");
738 if (ptr.@"volatile") try w.writeAll("volatile ");
739 switch (ptr.elem_ty.kind()) {
740 .specifier, .pointer => {},
741 .postfix_op => {
742 // Prefix "*" is lower precedence than postfix "(x)" or "[x]" so use parens
743 // to disambiguate; e.g. "void (*foo)(int)" instead of "void *foo(int)".
744 try w.writeByte('(');
745 },
746 }
747 try w.writeByte('*');
748 },
749
750 .array => |array| {
751 try array.elem_ty.writeTypePrefix(w, zcu);
752 switch (array.elem_ty.kind()) {
753 .pointer, .postfix_op => {},
754 .specifier => {
755 // We want e.g. "struct foo [5]" rather than "struct foo[5]".
756 try w.writeByte(' ');
757 },
758 }
759 },
760
761 .function => |function| {
762 try function.ret_ty.writeTypePrefix(w, zcu);
763 switch (function.ret_ty.kind()) {
764 .pointer, .postfix_op => {},
765 .specifier => {
766 // We want e.g. "struct foo (void)" rather than "struct foo(void)".
767 try w.writeByte(' ');
768 },
769 }
770 },
771 }
772 }
773 fn writeTypeSuffix(cty: CType, w: *Writer, zcu: *const Zcu) Writer.Error!void {
774 switch (cty) {
775 // simple type specifiers
776 .void,
777 .bool,
778 .int,
779 .float,
780 .@"fn",
781 .@"enum",
782 .bitpack,
783 .@"struct",
784 .union_auto,
785 .union_extern,
786 .slice,
787 .opt,
788 .arr,
789 .vec,
790 .errunion,
791 .aligned,
792 .bigint,
793 => {},
794
795 .pointer => |ptr| {
796 // Match opening paren "(" write `writeTypePrefix`.
797 switch (ptr.elem_ty.kind()) {
798 .specifier, .pointer => {},
799 .postfix_op => try w.writeByte(')'),
800 }
801 try ptr.elem_ty.writeTypeSuffix(w, zcu);
802 },
803
804 .array => |array| {
805 try w.print("[{d}]", .{array.len});
806 try array.elem_ty.writeTypeSuffix(w, zcu);
807 },
808
809 .function => |function| {
810 if (function.param_tys.len == 0 and !function.varargs) {
811 try w.writeAll("(void)");
812 } else {
813 try w.writeByte('(');
814 for (function.param_tys, 0..) |param_ty, param_index| {
815 if (param_index > 0) try w.writeAll(", ");
816 try param_ty.writeTypePrefix(w, zcu);
817 try param_ty.writeTypeSuffix(w, zcu);
818 }
819 if (function.varargs) {
820 if (function.param_tys.len > 0) try w.writeAll(", ");
821 try w.writeAll("...");
822 }
823 try w.writeByte(')');
824 }
825 try function.ret_ty.writeTypeSuffix(w, zcu);
826 },
827 }
828 }
829
830 /// Renders Zig types using only bytes allowed in C identifiers in a somewhat-understandable
831 /// way. The output is *not* guaranteed to be unique.
832 fn fmtZigType(ty: Type, zcu: *const Zcu) FormatZigType {
833 return .{ .ty = ty, .zcu = zcu };
834 }
835 const FormatZigType = struct {
836 ty: Type,
837 zcu: *const Zcu,
838 pub fn format(ctx: FormatZigType, w: *Writer) Writer.Error!void {
839 const ty = ctx.ty;
840 const zcu = ctx.zcu;
841 const ip = &zcu.intern_pool;
842 switch (ty.zigTypeTag(zcu)) {
843 .frame => unreachable,
844 .@"anyframe" => unreachable,
845
846 .type => try w.writeAll("type"),
847 .void => try w.writeAll("void"),
848 .bool => try w.writeAll("bool"),
849 .noreturn => try w.writeAll("noreturn"),
850 .comptime_int => try w.writeAll("comptime_int"),
851 .comptime_float => try w.writeAll("comptime_float"),
852 .enum_literal => try w.writeAll("enum_literal"),
853 .undefined => try w.writeAll("undefined"),
854 .null => try w.writeAll("null"),
855
856 .int => switch (ty.toIntern()) {
857 .usize_type => try w.writeAll("usize"),
858 .isize_type => try w.writeAll("isize"),
859 .c_char_type => try w.writeAll("c_char"),
860 .c_short_type => try w.writeAll("c_short"),
861 .c_ushort_type => try w.writeAll("c_ushort"),
862 .c_int_type => try w.writeAll("c_int"),
863 .c_uint_type => try w.writeAll("c_uint"),
864 .c_long_type => try w.writeAll("c_long"),
865 .c_ulong_type => try w.writeAll("c_ulong"),
866 .c_longlong_type => try w.writeAll("c_longlong"),
867 .c_ulonglong_type => try w.writeAll("c_ulonglong"),
868 else => {
869 const info = ty.intInfo(zcu);
870 switch (info.signedness) {
871 .unsigned => try w.print("u{d}", .{info.bits}),
872 .signed => try w.print("i{d}", .{info.bits}),
873 }
874 },
875 },
876 .float => switch (ty.toIntern()) {
877 .c_longdouble_type => try w.writeAll("c_longdouble"),
878 .f16_type => try w.writeAll("f16"),
879 .f32_type => try w.writeAll("f32"),
880 .f64_type => try w.writeAll("f64"),
881 .f80_type => try w.writeAll("f80"),
882 .f128_type => try w.writeAll("f128"),
883 else => unreachable,
884 },
885 .error_set => switch (ty.toIntern()) {
886 .anyerror_type => try w.writeAll("anyerror"),
887 else => try w.print("error_{d}", .{@intFromEnum(ty.toIntern())}),
888 },
889 .optional => try w.print("opt_{f}", .{fmtZigType(ty.optionalChild(zcu), zcu)}),
890 .error_union => try w.print("errunion_{f}", .{fmtZigType(ty.errorUnionPayload(zcu), zcu)}),
891
892 .pointer => switch (ty.ptrSize(zcu)) {
893 .one, .many, .c => try w.print("ptr_{f}", .{fmtZigType(ty.childType(zcu), zcu)}),
894 .slice => try w.print("slice_{f}", .{fmtZigType(ty.childType(zcu), zcu)}),
895 },
896 .@"fn" => {
897 const func_type = ip.indexToKey(ty.toIntern()).func_type;
898 try w.writeAll("fn_"); // intentional double underscore to start
899 for (func_type.param_types.get(ip)) |param_ty_ip| {
900 const param_ty: Type = .fromInterned(param_ty_ip);
901 try w.print("_P{f}", .{fmtZigType(param_ty, zcu)});
902 }
903 if (func_type.is_var_args) {
904 try w.writeAll("_VA");
905 }
906 const ret_ty: Type = .fromInterned(func_type.return_type);
907 try w.print("_R{f}", .{fmtZigType(ret_ty, zcu)});
908 },
909
910 .vector => try w.print("vec_{d}_{f}", .{
911 ty.arrayLen(zcu),
912 fmtZigType(ty.childType(zcu), zcu),
913 }),
914
915 .array => if (ty.sentinel(zcu)) |s| try w.print("arr_{d}s{d}_{f}", .{
916 ty.arrayLen(zcu),
917 @intFromEnum(s.toIntern()),
918 fmtZigType(ty.childType(zcu), zcu),
919 }) else try w.print("arr_{d}_{f}", .{
920 ty.arrayLen(zcu),
921 fmtZigType(ty.childType(zcu), zcu),
922 }),
923
924 .@"struct" => if (ty.isTuple(zcu)) {
925 const len = ty.structFieldCount(zcu);
926 try w.print("tuple_{d}", .{len});
927 for (0..len) |field_index| {
928 const field_ty = ty.fieldType(field_index, zcu);
929 try w.print("_{f}", .{fmtZigType(field_ty, zcu)});
930 }
931 } else {
932 const name = ty.containerTypeName(ip).toSlice(ip);
933 try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)});
934 },
935 .@"opaque" => if (ty.toIntern() == .anyopaque_type) {
936 try w.writeAll("anyopaque");
937 } else {
938 const name = ty.containerTypeName(ip).toSlice(ip);
939 try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)});
940 },
941 .@"union", .@"enum" => {
942 const name = ty.containerTypeName(ip).toSlice(ip);
943 try w.print("{f}", .{@import("../c.zig").fmtIdentUnsolo(name)});
944 },
945 }
946 }
947 };
948
949 /// Returns `true` if the layout of `ty` is known without any type resolution required. This
950 /// allows some types to be lowered directly where 'typedef' would otherwise be necessary.
951 fn alwaysHasLayout(ty: Type, ip: *const InternPool) bool {
952 return switch (ip.indexToKey(ty.toIntern())) {
953 .int_type,
954 .ptr_type,
955 .anyframe_type,
956 .simple_type,
957 .opaque_type,
958 .error_set_type,
959 .inferred_error_set_type,
960 => true,
961
962 .struct_type,
963 .union_type,
964 .enum_type,
965 => false,
966
967 .array_type => |arr| alwaysHasLayout(.fromInterned(arr.child), ip),
968 .vector_type => |vec| alwaysHasLayout(.fromInterned(vec.child), ip),
969 .opt_type => |child| alwaysHasLayout(.fromInterned(child), ip),
970 .error_union_type => |eu| alwaysHasLayout(.fromInterned(eu.payload_type), ip),
971
972 .tuple_type => |tuple| for (tuple.types.get(ip)) |field_ty| {
973 if (!alwaysHasLayout(.fromInterned(field_ty), ip)) break false;
974 } else true,
975
976 .func_type => |f| for (f.param_types.get(ip)) |param_ty| {
977 if (!alwaysHasLayout(.fromInterned(param_ty), ip)) break false;
978 } else alwaysHasLayout(.fromInterned(f.return_type), ip),
979
980 // values, not types
981 .undef,
982 .simple_value,
983 .variable,
984 .@"extern",
985 .func,
986 .int,
987 .err,
988 .error_union,
989 .enum_literal,
990 .enum_tag,
991 .float,
992 .ptr,
993 .slice,
994 .opt,
995 .aggregate,
996 .un,
997 .bitpack,
998 // memoization, not types
999 .memoized_call,
1000 => unreachable,
1001 };
1002 }
1003};
1004
1005const Zcu = @import("../../Zcu.zig");
1006const Type = @import("../../Type.zig");
1007const Value = @import("../../Value.zig");
1008const InternPool = @import("../../InternPool.zig");
1009
1010const std = @import("std");
1011const assert = std.debug.assert;
1012const Allocator = std.mem.Allocator;
1013const Writer = std.Io.Writer;
src/codegen/c/type/render_defs.zig created+651
......@@ -0,0 +1,651 @@
1/// Renders the `typedef` for an aligned type.
2pub fn defineAligned(
3 ty: Type,
4 alignment: Alignment,
5 complete: bool,
6 deps: *CType.Dependencies,
7 arena: Allocator,
8 w: *Writer,
9 pt: Zcu.PerThread,
10) (Allocator.Error || Writer.Error)!void {
11 const zcu = pt.zcu;
12
13 const name_cty: CType = .{ .aligned = .{
14 .ty = ty,
15 .alignment = alignment,
16 } };
17
18 const cty: CType = try .lower(ty, deps, arena, zcu);
19
20 try w.writeAll("typedef ");
21 if (complete and alignment.compareStrict(.lt, ty.abiAlignment(zcu))) {
22 try w.print("zig_under_align({d}) ", .{alignment.toByteUnits().?});
23 }
24 try w.print("{f}{f}{f}; /* align({d}) {f} */\n", .{
25 cty.fmtDeclaratorPrefix(zcu),
26 name_cty.fmtTypeName(zcu),
27 cty.fmtDeclaratorSuffix(zcu),
28 alignment.toByteUnits().?,
29 ty.fmt(pt),
30 });
31}
32/// Renders the definition of a big-int `struct`.
33pub fn defineBigInt(big: CType.BigInt, w: *Writer, zcu: *const Zcu) Writer.Error!void {
34 const name_cty: CType = .{ .bigint = .{
35 .limb_size = big.limb_size,
36 .limbs_len = big.limbs_len,
37 } };
38 const limb_cty: CType = .{ .int = big.limb_size.unsigned() };
39 const array_cty: CType = .{ .array = .{
40 .len = big.limbs_len,
41 .elem_ty = &limb_cty,
42 .nonstring = limb_cty.isStringElem(),
43 } };
44 try w.print("{f} {{ {f}limbs{f}; }}; /* {d} bits */\n", .{
45 name_cty.fmtTypeName(zcu),
46 array_cty.fmtDeclaratorPrefix(zcu),
47 array_cty.fmtDeclaratorSuffix(zcu),
48 big.limb_size.bits() * @as(u17, big.limbs_len),
49 });
50}
51
52/// Renders a forward declaration of the `struct` which represents an error union whose payload type
53/// is `payload_ty` (the error set type is unspecified).
54pub fn errunionFwdDecl(payload_ty: Type, w: *Writer, zcu: *const Zcu) Writer.Error!void {
55 const name_cty: CType = .{ .errunion = .{
56 .payload_ty = payload_ty,
57 } };
58 try w.print("{f};\n", .{name_cty.fmtTypeName(zcu)});
59}
60/// Renders the definition of the `struct` which represents an error union whose payload type is
61/// `payload_ty` (the error set type is unspecified).
62///
63/// Asserts that the layout of `payload_ty` is resolved.
64pub fn errunionDefineComplete(
65 payload_ty: Type,
66 deps: *CType.Dependencies,
67 arena: Allocator,
68 w: *Writer,
69 pt: Zcu.PerThread,
70) (Allocator.Error || Writer.Error)!void {
71 const zcu = pt.zcu;
72
73 payload_ty.assertHasLayout(zcu);
74
75 const name_cty: CType = .{ .errunion = .{
76 .payload_ty = payload_ty,
77 } };
78
79 const error_cty: CType = try .lower(.anyerror, deps, arena, zcu);
80
81 if (payload_ty.hasRuntimeBits(zcu)) {
82 const payload_cty: CType = try .lower(payload_ty, deps, arena, zcu);
83 try w.print(
84 \\{f} {{ /* anyerror!{f} */
85 \\ {f}payload{f};
86 \\ {f}error{f};
87 \\}};
88 \\
89 , .{
90 name_cty.fmtTypeName(zcu),
91 payload_ty.fmt(pt),
92 payload_cty.fmtDeclaratorPrefix(zcu),
93 payload_cty.fmtDeclaratorSuffix(zcu),
94 error_cty.fmtDeclaratorPrefix(zcu),
95 error_cty.fmtDeclaratorSuffix(zcu),
96 });
97 } else {
98 try w.print("{f} {{ {f}error{f}; }}; /* anyerror!{f} */\n", .{
99 name_cty.fmtTypeName(zcu),
100 error_cty.fmtDeclaratorPrefix(zcu),
101 error_cty.fmtDeclaratorSuffix(zcu),
102 payload_ty.fmt(pt),
103 });
104 }
105}
106
107/// If the Zig type `ty` lowers to a `struct` or `union` type, renders a forward declaration of that
108/// type. Does not write anything for error union types, because their forward declarations are
109/// instead rendered by `errunionFwdDecl`.
110pub fn fwdDecl(ty: Type, w: *Writer, zcu: *const Zcu) Writer.Error!void {
111 const name_cty: CType = switch (ty.zigTypeTag(zcu)) {
112 .@"struct" => switch (ty.containerLayout(zcu)) {
113 .auto, .@"extern" => .{ .@"struct" = ty },
114 .@"packed" => return,
115 },
116 .@"union" => switch (ty.containerLayout(zcu)) {
117 .auto => .{ .union_auto = ty },
118 .@"extern" => .{ .union_extern = ty },
119 .@"packed" => return,
120 },
121 .pointer => if (ty.isSlice(zcu)) .{ .slice = ty } else return,
122 .optional => .{ .opt = ty },
123 .array => .{ .arr = ty },
124 .vector => .{ .vec = ty },
125 else => return,
126 };
127 try w.print("{f};\n", .{name_cty.fmtTypeName(zcu)});
128}
129
130/// If the Zig type `ty` lowers to a `typedef`, renders a typedef of that type to `void`, because
131/// the type's layout is not resolved. This is only necessary for `typedef`s because a `struct` or
132/// `union` which is never defined is already an incomplete type, just like `void`.
133pub fn defineIncomplete(ty: Type, w: *Writer, pt: Zcu.PerThread) Writer.Error!void {
134 const zcu = pt.zcu;
135 const name_cty: CType = switch (ty.zigTypeTag(zcu)) {
136 .@"fn" => .{ .@"fn" = ty },
137 .@"enum" => .{ .@"enum" = ty },
138 .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {
139 .auto, .@"extern" => return,
140 .@"packed" => .{ .bitpack = ty },
141 },
142 else => return,
143 };
144 try w.print("typedef void {f}; /* {f} */\n", .{
145 name_cty.fmtTypeName(zcu),
146 ty.fmt(pt),
147 });
148}
149
150/// If the Zig type `ty` lowers to a `struct` or `union` type, or to a `typedef`, renders the
151/// definition of that type. Does not write anything for error union types, because their
152/// definitions are instead rendered by `errunionDefine`.
153///
154/// Asserts that the layout of `ty` is resolved.
155pub fn defineComplete(
156 ty: Type,
157 deps: *CType.Dependencies,
158 arena: Allocator,
159 w: *Writer,
160 pt: Zcu.PerThread,
161) (Allocator.Error || Writer.Error)!void {
162 const zcu = pt.zcu;
163
164 ty.assertHasLayout(zcu);
165
166 switch (ty.zigTypeTag(zcu)) {
167 .@"fn" => if (!ty.fnHasRuntimeBits(zcu)) {
168 const name_cty: CType = .{ .@"fn" = ty };
169 try w.print("typedef void {f}; /* {f} */\n", .{
170 name_cty.fmtTypeName(zcu),
171 ty.fmt(pt),
172 });
173 } else {
174 const ip = &zcu.intern_pool;
175 const func_type = ip.indexToKey(ty.toIntern()).func_type;
176
177 // While incomplete types are usually an acceptable substitute for "void", this is not
178 // true in function return types, where "void" is the only incomplete type permitted.
179 const actual_ret_ty: Type = .fromInterned(func_type.return_type);
180 const effective_ret_ty: Type = switch (actual_ret_ty.classify(zcu)) {
181 .no_possible_value => .noreturn,
182 .one_possible_value, .fully_comptime => .void, // no runtime bits
183 .partially_comptime, .runtime => actual_ret_ty, // yes runtime bits
184 };
185
186 const name_cty: CType = .{ .@"fn" = ty };
187 const ret_cty: CType = try .lower(effective_ret_ty, deps, arena, zcu);
188
189 try w.print("typedef {f}{f}(", .{
190 ret_cty.fmtDeclaratorPrefix(zcu),
191 name_cty.fmtTypeName(zcu),
192 });
193 var any_params = false;
194 for (func_type.param_types.get(ip)) |param_ty_ip| {
195 const param_ty: Type = .fromInterned(param_ty_ip);
196 if (!param_ty.hasRuntimeBits(zcu)) continue;
197 if (any_params) try w.writeAll(", ");
198 any_params = true;
199 const param_cty: CType = try .lower(param_ty, deps, arena, zcu);
200 try w.print("{f}", .{param_cty.fmtTypeName(zcu)});
201 }
202 if (func_type.is_var_args) {
203 if (any_params) try w.writeAll(", ");
204 try w.writeAll("...");
205 } else if (!any_params) {
206 try w.writeAll("void");
207 }
208 try w.print("){f}; /* {f} */\n", .{
209 ret_cty.fmtDeclaratorSuffixIgnoreNonstring(zcu),
210 ty.fmt(pt),
211 });
212 },
213 .@"enum" => {
214 const name_cty: CType = .{ .@"enum" = ty };
215 const cty: CType = try .lower(ty.intTagType(zcu), deps, arena, zcu);
216 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{
217 cty.fmtDeclaratorPrefix(zcu),
218 name_cty.fmtTypeName(zcu),
219 cty.fmtDeclaratorSuffix(zcu),
220 ty.fmt(pt),
221 });
222 },
223 .@"struct" => if (ty.isTuple(zcu)) {
224 try defineTuple(ty, deps, arena, w, pt);
225 } else switch (ty.containerLayout(zcu)) {
226 .auto, .@"extern" => try defineStruct(ty, deps, arena, w, pt),
227 .@"packed" => try defineBitpack(ty, deps, arena, w, pt),
228 },
229 .@"union" => switch (ty.containerLayout(zcu)) {
230 .auto => try defineUnionAuto(ty, deps, arena, w, pt),
231 .@"extern" => try defineUnionExtern(ty, deps, arena, w, pt),
232 .@"packed" => try defineBitpack(ty, deps, arena, w, pt),
233 },
234 .pointer => if (ty.isSlice(zcu)) {
235 const name_cty: CType = .{ .slice = ty };
236 const ptr_cty: CType = try .lower(ty.slicePtrFieldType(zcu), deps, arena, zcu);
237 try w.print(
238 \\{f} {{ /* {f} */
239 \\ {f}ptr{f};
240 \\ size_t len;
241 \\}};
242 \\
243 , .{
244 name_cty.fmtTypeName(zcu),
245 ty.fmt(pt),
246 ptr_cty.fmtDeclaratorPrefix(zcu),
247 ptr_cty.fmtDeclaratorSuffix(zcu),
248 });
249 },
250 .optional => switch (CType.classifyOptional(ty, zcu)) {
251 .error_set,
252 .ptr_like,
253 .slice_like,
254 .npv_payload,
255 => {},
256
257 .opv_payload => {
258 const name_cty: CType = .{ .opt = ty };
259 try w.print("{f} {{ bool is_null; }}; /* {f} */\n", .{
260 name_cty.fmtTypeName(zcu),
261 ty.fmt(pt),
262 });
263 },
264
265 .@"struct" => {
266 const name_cty: CType = .{ .opt = ty };
267 const payload_cty: CType = try .lower(ty.optionalChild(zcu), deps, arena, zcu);
268 try w.print(
269 \\{f} {{ /* {f} */
270 \\ {f}payload{f};
271 \\ bool is_null;
272 \\}};
273 \\
274 , .{
275 name_cty.fmtTypeName(zcu),
276 ty.fmt(pt),
277 payload_cty.fmtDeclaratorPrefix(zcu),
278 payload_cty.fmtDeclaratorSuffix(zcu),
279 });
280 },
281 },
282 .array => if (ty.hasRuntimeBits(zcu)) {
283 const name_cty: CType = .{ .arr = ty };
284 const elem_cty: CType = try .lower(ty.childType(zcu), deps, arena, zcu);
285 const array_cty: CType = .{ .array = .{
286 .len = ty.arrayLenIncludingSentinel(zcu),
287 .elem_ty = &elem_cty,
288 .nonstring = nonstring: {
289 if (!elem_cty.isStringElem()) break :nonstring false;
290 const s = ty.sentinel(zcu) orelse break :nonstring true;
291 break :nonstring Value.compareHetero(s, .neq, .zero_comptime_int, zcu);
292 },
293 } };
294 try w.print("{f} {{ {f}array{f}; }}; /* {f} */\n", .{
295 name_cty.fmtTypeName(zcu),
296 array_cty.fmtDeclaratorPrefix(zcu),
297 array_cty.fmtDeclaratorSuffix(zcu),
298 ty.fmt(pt),
299 });
300 },
301 .vector => if (ty.hasRuntimeBits(zcu)) {
302 const name_cty: CType = .{ .vec = ty };
303 const elem_cty: CType = try .lower(ty.childType(zcu), deps, arena, zcu);
304 const array_cty: CType = .{ .array = .{
305 .len = ty.arrayLenIncludingSentinel(zcu),
306 .elem_ty = &elem_cty,
307 .nonstring = elem_cty.isStringElem(),
308 } };
309 try w.print("{f} {{ {f}array{f}; }}; /* {f} */\n", .{
310 name_cty.fmtTypeName(zcu),
311 array_cty.fmtDeclaratorPrefix(zcu),
312 array_cty.fmtDeclaratorSuffix(zcu),
313 ty.fmt(pt),
314 });
315 },
316 else => {},
317 }
318}
319fn defineBitpack(
320 ty: Type,
321 deps: *CType.Dependencies,
322 arena: Allocator,
323 w: *Writer,
324 pt: Zcu.PerThread,
325) (Allocator.Error || Writer.Error)!void {
326 const zcu = pt.zcu;
327 const name_cty: CType = .{ .bitpack = ty };
328 const cty: CType = try .lower(ty.bitpackBackingInt(zcu), deps, arena, zcu);
329 try w.print("typedef {f}{f}{f}; /* {f} */\n", .{
330 cty.fmtDeclaratorPrefix(zcu),
331 name_cty.fmtTypeName(zcu),
332 cty.fmtDeclaratorSuffix(zcu),
333 ty.fmt(pt),
334 });
335}
336fn defineTuple(
337 ty: Type,
338 deps: *CType.Dependencies,
339 arena: Allocator,
340 w: *Writer,
341 pt: Zcu.PerThread,
342) (Allocator.Error || Writer.Error)!void {
343 const zcu = pt.zcu;
344 if (!ty.hasRuntimeBits(zcu)) return;
345 const ip = &zcu.intern_pool;
346 const tuple = ip.indexToKey(ty.toIntern()).tuple_type;
347
348 // Fields cannot be underaligned, because tuple fields cannot have specified alignments.
349 // However, overaligned fields are possible thanks to intermediate zero-bit fields.
350
351 const tuple_align = ty.abiAlignment(zcu);
352
353 // If the alignment of other fields would not give the tuple sufficient alignment, we
354 // need to align the first field (which does not affect its offset, because 0 is always
355 // well-aligned) to indirectly specify the tuple alignment.
356 const overalign: bool = for (tuple.types.get(ip)) |field_ty_ip| {
357 const field_ty: Type = .fromInterned(field_ty_ip);
358 if (!field_ty.hasRuntimeBits(zcu)) continue;
359 const natural_align = field_ty.defaultStructFieldAlignment(.auto, zcu);
360 if (natural_align.compareStrict(.gte, tuple_align)) break false;
361 } else true;
362
363 const name_cty: CType = .{ .@"struct" = ty };
364 try w.print("{f} {{ /* {f} */\n", .{
365 name_cty.fmtTypeName(zcu),
366 ty.fmt(pt),
367 });
368 var zig_offset: u64 = 0;
369 var c_offset: u64 = 0;
370 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty_ip, field_val_ip, field_index| {
371 if (field_val_ip != .none) continue; // `comptime` field
372 const field_ty: Type = .fromInterned(field_ty_ip);
373 const field_align = field_ty.abiAlignment(zcu);
374 zig_offset = field_align.forward(zig_offset);
375 if (!field_ty.hasRuntimeBits(zcu)) continue;
376 c_offset = field_align.forward(c_offset);
377 if (zig_offset == 0 and overalign) {
378 // This is the first field; specify its alignment to align the tuple.
379 try w.print(" zig_align({d})", .{tuple_align.toByteUnits().?});
380 } else if (zig_offset > c_offset) {
381 // This field needs to be overaligned compared to what its offset would otherwise be.
382 const need_align: Alignment = .fromLog2Units(@ctz(zig_offset));
383 try w.print(" zig_align({d})", .{need_align.toByteUnits().?});
384 c_offset = need_align.forward(c_offset);
385 assert(c_offset == zig_offset);
386 }
387 const field_cty: CType = try .lower(field_ty, deps, arena, zcu);
388 try w.print(" {f}f{d}{f};\n", .{
389 field_cty.fmtDeclaratorPrefix(zcu),
390 field_index,
391 field_cty.fmtDeclaratorSuffix(zcu),
392 });
393 const field_size = field_ty.abiSize(zcu);
394 zig_offset += field_size;
395 c_offset += field_size;
396 }
397 try w.writeAll("};\n");
398}
399fn defineStruct(
400 ty: Type,
401 deps: *CType.Dependencies,
402 arena: Allocator,
403 w: *Writer,
404 pt: Zcu.PerThread,
405) (Allocator.Error || Writer.Error)!void {
406 const zcu = pt.zcu;
407 if (!ty.hasRuntimeBits(zcu)) return;
408 const ip = &zcu.intern_pool;
409
410 const struct_type = ip.loadStructType(ty.toIntern());
411
412 // If there are any underaligned fields, we need to byte-pack the struct.
413 const pack: bool = pack: {
414 var it = struct_type.iterateRuntimeOrder(ip);
415 var offset: u64 = 0;
416 while (it.next()) |field_index| {
417 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
418 if (!field_ty.hasRuntimeBits(zcu)) continue;
419 const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu);
420 const natural_offset = natural_align.forward(offset);
421 const actual_offset = struct_type.field_offsets.get(ip)[field_index];
422 if (actual_offset < natural_offset) break :pack true;
423 offset = actual_offset + field_ty.abiSize(zcu);
424 }
425 break :pack false;
426 };
427
428 // If the alignment of other fields would not give the struct sufficient alignment, we
429 // need to align the first field (which does not affect its offset, because 0 is always
430 // well-aligned) to indirectly specify the struct alignment.
431 const overalign: bool = switch (pack) {
432 true => struct_type.alignment.compareStrict(.gt, .@"1"),
433 false => overalign: {
434 var it = struct_type.iterateRuntimeOrder(ip);
435 while (it.next()) |field_index| {
436 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
437 if (!field_ty.hasRuntimeBits(zcu)) continue;
438 const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu);
439 if (natural_align.compareStrict(.gte, struct_type.alignment)) break :overalign false;
440 }
441 break :overalign true;
442 },
443 };
444
445 if (pack) try w.writeAll("zig_packed(");
446 const name_cty: CType = .{ .@"struct" = ty };
447 try w.print("{f} {{ /* {f} */\n", .{
448 name_cty.fmtTypeName(zcu),
449 ty.fmt(pt),
450 });
451 var it = struct_type.iterateRuntimeOrder(ip);
452 var offset: u64 = 0;
453 while (it.next()) |field_index| {
454 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
455 if (!field_ty.hasRuntimeBits(zcu)) continue;
456 const natural_align = field_ty.defaultStructFieldAlignment(struct_type.layout, zcu);
457 const natural_offset = switch (pack) {
458 true => offset,
459 false => natural_align.forward(offset),
460 };
461 const actual_offset = struct_type.field_offsets.get(ip)[field_index];
462 if (actual_offset == 0 and overalign) {
463 // This is the first field; specify its alignment to align the struct.
464 try w.print(" zig_align({d})", .{struct_type.alignment.toByteUnits().?});
465 } else if (actual_offset > natural_offset) {
466 // This field needs to be underaligned or overaligned compared to what its
467 // offset would otherwise be.
468 const need_align: Alignment = .fromLog2Units(@ctz(actual_offset));
469 if (need_align.compareStrict(.lt, natural_align)) {
470 try w.print(" zig_under_align({d})", .{need_align.toByteUnits().?});
471 } else {
472 try w.print(" zig_align({d})", .{need_align.toByteUnits().?});
473 }
474 }
475 const field_cty: CType = try .lower(field_ty, deps, arena, zcu);
476 const field_name = struct_type.field_names.get(ip)[field_index].toSlice(ip);
477 try w.print(" {f}{f}{f};\n", .{
478 field_cty.fmtDeclaratorPrefix(zcu),
479 fmtIdentSolo(field_name),
480 field_cty.fmtDeclaratorSuffix(zcu),
481 });
482 offset = actual_offset + field_ty.abiSize(zcu);
483 }
484 assert(struct_type.alignment.forward(offset) == struct_type.size);
485 try w.writeByte('}');
486 if (pack) try w.writeByte(')');
487 try w.writeAll(";\n");
488}
489fn defineUnionAuto(
490 ty: Type,
491 deps: *CType.Dependencies,
492 arena: Allocator,
493 w: *Writer,
494 pt: Zcu.PerThread,
495) (Allocator.Error || Writer.Error)!void {
496 const zcu = pt.zcu;
497 if (!ty.hasRuntimeBits(zcu)) return;
498 const ip = &zcu.intern_pool;
499
500 const union_type = ip.loadUnionType(ty.toIntern());
501 const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type);
502
503 // If there are any underaligned fields, we need to byte-pack the union.
504 const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| {
505 const field_ty: Type = .fromInterned(field_ty_ip);
506 if (!field_ty.hasRuntimeBits(zcu)) continue;
507 const natural_align = field_ty.abiAlignment(zcu);
508 if (natural_align.compareStrict(.gt, union_type.alignment)) break true;
509 } else false;
510
511 // If the alignment of other fields would not give the union sufficient alignment, we
512 // need to align the first field (which does not affect its offset, because 0 is always
513 // well-aligned) to indirectly specify the union alignment.
514 const overalign: bool = switch (pack) {
515 true => union_type.alignment.compareStrict(.gt, .@"1"),
516 false => for (union_type.field_types.get(ip)) |field_ty_ip| {
517 const field_ty: Type = .fromInterned(field_ty_ip);
518 if (!field_ty.hasRuntimeBits(zcu)) continue;
519 const natural_align = field_ty.abiAlignment(zcu);
520 if (natural_align.compareStrict(.gte, union_type.alignment)) break false;
521 } else overalign: {
522 if (union_type.has_runtime_tag) {
523 const tag_align = enum_tag_ty.abiAlignment(zcu);
524 if (tag_align.compareStrict(.gte, union_type.alignment)) break :overalign false;
525 }
526 break :overalign true;
527 },
528 };
529
530 const payload_has_bits = !union_type.has_runtime_tag or union_type.size > enum_tag_ty.abiSize(zcu);
531
532 const name_cty: CType = .{ .union_auto = ty };
533 try w.print("{f} {{ /* {f} */\n", .{
534 name_cty.fmtTypeName(zcu),
535 ty.fmt(pt),
536 });
537 if (payload_has_bits) {
538 try w.writeByte(' ');
539 if (pack) try w.writeAll("zig_packed(");
540 try w.writeAll("union {\n");
541 for (0..enum_tag_ty.enumFieldCount(zcu)) |field_index| {
542 const field_ty = ty.fieldType(field_index, zcu);
543 if (!field_ty.hasRuntimeBits(zcu)) continue;
544 const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip);
545 const field_cty: CType = try .lower(field_ty, deps, arena, zcu);
546 try w.writeAll(" ");
547 if (overalign and field_index == 0) {
548 // This is the first field; specify its alignment to align the union.
549 try w.print("zig_align({d}) ", .{union_type.alignment.toByteUnits().?});
550 }
551 try w.print("{f}{f}{f};\n", .{
552 field_cty.fmtDeclaratorPrefix(zcu),
553 fmtIdentSolo(field_name),
554 field_cty.fmtDeclaratorSuffix(zcu),
555 });
556 }
557 try w.writeAll(" }");
558 if (pack) try w.writeByte(')');
559 try w.writeAll(" payload;\n");
560 }
561 if (union_type.has_runtime_tag) {
562 const tag_cty: CType = try .lower(enum_tag_ty, deps, arena, zcu);
563 try w.print(" {f}tag{f};\n", .{
564 tag_cty.fmtDeclaratorPrefix(zcu),
565 tag_cty.fmtDeclaratorSuffix(zcu),
566 });
567 }
568 try w.writeAll("};\n");
569}
570fn defineUnionExtern(
571 ty: Type,
572 deps: *CType.Dependencies,
573 arena: Allocator,
574 w: *Writer,
575 pt: Zcu.PerThread,
576) (Allocator.Error || Writer.Error)!void {
577 const zcu = pt.zcu;
578 if (!ty.hasRuntimeBits(zcu)) return;
579 const ip = &zcu.intern_pool;
580
581 const union_type = ip.loadUnionType(ty.toIntern());
582 assert(!union_type.has_runtime_tag);
583 const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type);
584
585 // If there are any underaligned fields, we need to byte-pack the union.
586 const pack: bool = for (union_type.field_types.get(ip)) |field_ty_ip| {
587 const field_ty: Type = .fromInterned(field_ty_ip);
588 if (!field_ty.hasRuntimeBits(zcu)) continue;
589 const natural_align = field_ty.abiAlignment(zcu);
590 if (natural_align.compareStrict(.gt, union_type.alignment)) break true;
591 } else false;
592
593 // If the alignment of other fields would not give the union sufficient alignment, we
594 // need to align the first field (which does not affect its offset, because 0 is always
595 // well-aligned) to indirectly specify the union alignment.
596 const overalign: bool = switch (pack) {
597 true => union_type.alignment.compareStrict(.gt, .@"1"),
598 false => for (union_type.field_types.get(ip)) |field_ty_ip| {
599 const field_ty: Type = .fromInterned(field_ty_ip);
600 if (!field_ty.hasRuntimeBits(zcu)) continue;
601 const natural_align = field_ty.abiAlignment(zcu);
602 if (natural_align.compareStrict(.gte, union_type.alignment)) break false;
603 } else overalign: {
604 if (union_type.has_runtime_tag) {
605 const tag_align = enum_tag_ty.abiAlignment(zcu);
606 if (tag_align.compareStrict(.gte, union_type.alignment)) break :overalign false;
607 }
608 break :overalign true;
609 },
610 };
611
612 if (pack) try w.writeAll("zig_packed(");
613
614 const name_cty: CType = .{ .union_extern = ty };
615 try w.print("{f} {{ /* {f} */\n", .{
616 name_cty.fmtTypeName(zcu),
617 ty.fmt(pt),
618 });
619
620 for (0..enum_tag_ty.enumFieldCount(zcu)) |field_index| {
621 const field_ty = ty.fieldType(field_index, zcu);
622 if (!field_ty.hasRuntimeBits(zcu)) continue;
623 const field_name = enum_tag_ty.enumFieldName(field_index, zcu).toSlice(ip);
624 const field_cty: CType = try .lower(field_ty, deps, arena, zcu);
625 if (overalign and field_index == 0) {
626 // This is the first field; specify its alignment to align the union.
627 try w.print(" zig_align({d})", .{union_type.alignment.toByteUnits().?});
628 }
629 try w.print(" {f}{f}{f};\n", .{
630 field_cty.fmtDeclaratorPrefix(zcu),
631 fmtIdentSolo(field_name),
632 field_cty.fmtDeclaratorSuffix(zcu),
633 });
634 }
635 try w.writeByte('}');
636 if (pack) try w.writeByte(')');
637 try w.writeAll(";\n");
638}
639
640const std = @import("std");
641const assert = std.debug.assert;
642const Writer = std.Io.Writer;
643const Allocator = std.mem.Allocator;
644
645const Zcu = @import("../../../Zcu.zig");
646const Type = @import("../../../Type.zig");
647const Value = @import("../../../Value.zig");
648const CType = @import("../type.zig").CType;
649const Alignment = @import("../../../InternPool.zig").Alignment;
650
651const fmtIdentSolo = @import("../../c.zig").fmtIdentSolo;
src/codegen/llvm.zig+11-21
......@@ -23,7 +23,6 @@ const Package = @import("../Package.zig");
2323const Air = @import("../Air.zig");
2424const Value = @import("../Value.zig");
2525const Type = @import("../Type.zig");
26const DebugConstPool = link.DebugConstPool;
2726const codegen = @import("../codegen.zig");
2827const x86_64_abi = @import("x86_64/abi.zig");
2928const wasm_c_abi = @import("wasm/abi.zig");
......@@ -532,8 +531,8 @@ pub const Object = struct {
532531 debug_file_map: std.AutoHashMapUnmanaged(Zcu.File.Index, Builder.Metadata),
533532
534533 /// This pool *only* contains types (and does not contain `@as(type, undefined)`).
535 debug_type_pool: DebugConstPool,
536 /// Keyed on `DebugConstPool.Index`.
534 debug_type_pool: link.ConstPool,
535 /// Keyed on `link.ConstPool.Index`.
537536 debug_types: std.ArrayList(Builder.Metadata),
538537 /// Initially `.none`, set if the type `anyerror` is lowered to a debug type. The type will not
539538 /// actually be created until `emit`, which must resolve this reference with an appropriate enum
......@@ -1622,10 +1621,7 @@ pub const Object = struct {
16221621 }
16231622
16241623 fn flushPendingDebugTypes(o: *Object, pt: Zcu.PerThread) Allocator.Error!void {
1625 o.debug_type_pool.flushPending(pt, .{ .llvm = o }) catch |err| switch (err) {
1626 error.OutOfMemory => |e| return e,
1627 else => unreachable, // TODO: stop self-hosted backends from returning all of this crap!
1628 };
1624 try o.debug_type_pool.flushPending(pt, .{ .llvm = o });
16291625 }
16301626
16311627 pub fn updateExports(
......@@ -1823,17 +1819,14 @@ pub const Object = struct {
18231819
18241820 pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void {
18251821 if (!o.builder.strip) {
1826 o.debug_type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success) catch |err| switch (err) {
1827 error.OutOfMemory => |e| return e,
1828 else => unreachable, // TODO: stop self-hosted backends from returning all of this crap!
1829 };
1822 try o.debug_type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success);
18301823 }
18311824 }
18321825
1833 /// Should only be called by the `DebugConstPool` implementation.
1826 /// Should only be called by the `link.ConstPool` implementation.
18341827 ///
18351828 /// `val` is always a type because `o.debug_type_pool` only contains types.
1836 pub fn addConst(o: *Object, pt: Zcu.PerThread, index: DebugConstPool.Index, val: InternPool.Index) Allocator.Error!void {
1829 pub fn addConst(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void {
18371830 const zcu = pt.zcu;
18381831 const gpa = zcu.comp.gpa;
18391832 assert(zcu.intern_pool.typeOf(val) == .type_type);
......@@ -1846,10 +1839,10 @@ pub const Object = struct {
18461839 o.debug_anyerror_fwd_ref = fwd_ref.toOptional();
18471840 }
18481841 }
1849 /// Should only be called by the `DebugConstPool` implementation.
1842 /// Should only be called by the `link.ConstPool` implementation.
18501843 ///
18511844 /// `val` is always a type because `o.debug_type_pool` only contains types.
1852 pub fn updateConstIncomplete(o: *Object, pt: Zcu.PerThread, index: DebugConstPool.Index, val: InternPool.Index) Allocator.Error!void {
1845 pub fn updateConstIncomplete(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void {
18531846 assert(pt.zcu.intern_pool.typeOf(val) == .type_type);
18541847 const fwd_ref = o.debug_types.items[@intFromEnum(index)];
18551848 assert(val != .anyerror_type);
......@@ -1857,10 +1850,10 @@ pub const Object = struct {
18571850 const debug_incomplete_type = try o.builder.debugSignedType(name_str, 0);
18581851 o.builder.resolveDebugForwardReference(fwd_ref, debug_incomplete_type);
18591852 }
1860 /// Should only be called by the `DebugConstPool` implementation.
1853 /// Should only be called by the `link.ConstPool` implementation.
18611854 ///
18621855 /// `val` is always a type because `o.debug_type_pool` only contains types.
1863 pub fn updateConst(o: *Object, pt: Zcu.PerThread, index: DebugConstPool.Index, val: InternPool.Index) Allocator.Error!void {
1856 pub fn updateConst(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void {
18641857 assert(pt.zcu.intern_pool.typeOf(val) == .type_type);
18651858 const fwd_ref = o.debug_types.items[@intFromEnum(index)];
18661859 if (val == .anyerror_type) {
......@@ -1890,10 +1883,7 @@ pub const Object = struct {
18901883
18911884 fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata {
18921885 assert(!o.builder.strip);
1893 const index = o.debug_type_pool.get(pt, .{ .llvm = o }, ty.toIntern()) catch |err| switch (err) {
1894 error.OutOfMemory => |e| return e,
1895 else => unreachable, // TODO: stop self-hosted backends from returning all of this crap!
1896 };
1886 const index = try o.debug_type_pool.get(pt, .{ .llvm = o }, ty.toIntern());
18971887 return o.debug_types.items[@intFromEnum(index)];
18981888 }
18991889
src/link.zig+2-2
......@@ -29,7 +29,7 @@ const codegen = @import("codegen.zig");
2929pub const aarch64 = @import("link/aarch64.zig");
3030pub const LdScript = @import("link/LdScript.zig");
3131pub const Queue = @import("link/Queue.zig");
32pub const DebugConstPool = @import("link/DebugConstPool.zig");
32pub const ConstPool = @import("link/ConstPool.zig");
3333
3434pub const Diags = struct {
3535 /// Stored here so that function definitions can distinguish between
......@@ -804,7 +804,7 @@ pub const File = struct {
804804 switch (base.tag) {
805805 .lld => unreachable,
806806 else => {},
807 inline .elf => |tag| {
807 inline .elf, .c => |tag| {
808808 dev.check(tag.devFeature());
809809 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success);
810810 },
src/link/C.zig+1272-627
......@@ -1,3 +1,9 @@
1/// Unlike other linker implementations, `link.C` does not attempt to incrementally link its output,
2/// because C has many language rules which make that impractical. Instead, we individually generate
3/// each declaration (NAV), and the output is stitched together (alongside types and UAVs) in an
4/// appropriate order in `flush`.
5const C = @This();
6
17const std = @import("std");
28const mem = std.mem;
39const assert = std.debug.assert;
......@@ -5,7 +11,6 @@ const Allocator = std.mem.Allocator;
511const fs = std.fs;
612const Path = std.Build.Cache.Path;
713
8const C = @This();
914const build_options = @import("build_options");
1015const Zcu = @import("../Zcu.zig");
1116const Module = @import("../Package/Module.zig");
......@@ -19,40 +24,45 @@ const Type = @import("../Type.zig");
1924const Value = @import("../Value.zig");
2025const AnyMir = @import("../codegen.zig").AnyMir;
2126
22pub const zig_h = "#include \"zig.h\"\n";
23
2427base: link.File,
25/// This linker backend does not try to incrementally link output C source code.
26/// Instead, it tracks all declarations in this table, and iterates over it
27/// in the flush function, stitching pre-rendered pieces of C code together.
28navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvBlock),
29/// All the string bytes of rendered C code, all squished into one array.
30/// While in progress, a separate buffer is used, and then when finished, the
31/// buffer is copied into this one.
28
29/// All the string bytes of rendered C code, all squished into one array. `String` is used to refer
30/// to specific slices of this array, used for the rendered C code of an individual UAV/NAV/type.
31///
32/// During code generation for functions, a separate buffer is used, and the contents of that buffer
33/// are copied into `string_bytes` when the function is emitted by `updateFunc`.
3234string_bytes: std.ArrayList(u8),
33/// Tracks all the anonymous decls that are used by all the decls so they can
34/// be rendered during flush().
35uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, AvBlock),
36/// Sparse set of uavs that are overaligned. Underaligned anon decls are
37/// lowered the same as ABI-aligned anon decls. The keys here are a subset of
38/// the keys of `uavs`.
39aligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
40
41exported_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ExportedBlock),
42exported_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ExportedBlock),
43
44/// Optimization, `updateDecl` reuses this buffer rather than creating a new
45/// one with every call.
46fwd_decl_buf: []u8,
47/// Optimization, `updateDecl` reuses this buffer rather than creating a new
48/// one with every call.
49code_header_buf: []u8,
50/// Optimization, `updateDecl` reuses this buffer rather than creating a new
51/// one with every call.
52code_buf: []u8,
53/// Optimization, `flush` reuses this buffer rather than creating a new
54/// one with every call.
55scratch_buf: []u32,
35
36/// Like with `string_bytes`, we concatenate all type dependencies into one array, and slice into it
37/// for specific groups of dependencies. These values are indices into `type_pool`, and thus also
38/// into `types`. We store these instead of `InternPool.Index` because it lets us avoid some hash
39/// map lookups in `flush`.
40type_dependencies: std.ArrayList(link.ConstPool.Index),
41/// For storing dependencies on "aligned" versions of types, we must associate each type with a
42/// bitmask of required alignments. As with `type_dependencies`, we concatenate all such masks into
43/// one array.
44align_dependency_masks: std.ArrayList(u64),
45
46/// All NAVs, regardless of whether they are functions or simple constants, are put in this map.
47navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, RenderedDecl),
48/// All UAVs which may be referenced are in this map. The UAV alignment is not included in the
49/// rendered C code stored here, because we don't know the alignment a UAV needs until `flush`.
50uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, RenderedDecl),
51/// Contains all types which are needed by some other rendered code. Does not contain any constants
52/// other than types.
53type_pool: link.ConstPool,
54/// Indices are `link.ConstPool.Index` from `type_pool`. Contains rendered C code for every type
55/// which may be referenced. Logic in `flush` will perform the appropriate topological sort to emit
56/// these type definitions in an order which C allows.
57types: std.ArrayList(RenderedType),
58
59/// The set of big int types required by *any* generated code so far. These are always safe to emit,
60/// so they do not participate in the dependency graph traversal in `flush`. Therefore, redundant
61/// big-int types may be emitted under incremental compilation.
62bigint_types: std.AutoArrayHashMapUnmanaged(codegen.CType.BigInt, void),
63
64exported_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, String),
65exported_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, String),
5666
5767/// A reference into `string_bytes`.
5868const String = extern struct {
......@@ -64,50 +74,320 @@ const String = extern struct {
6474 .len = 0,
6575 };
6676
67 fn concat(lhs: String, rhs: String) String {
68 assert(lhs.start + lhs.len == rhs.start);
77 fn get(s: String, c: *C) []const u8 {
78 return c.string_bytes.items[s.start..][0..s.len];
79 }
80};
81
82const CTypeDependencies = struct {
83 len: u32,
84 errunion_len: u32,
85 fwd_len: u32,
86 errunion_fwd_len: u32,
87 aligned_fwd_len: u32,
88
89 /// Index into `C.type_dependencies`. Starting at this index are:
90 /// * `len` dependencies on complete types
91 /// * `errunion_len` dependencies on complete error union types
92 /// * `fwd_len` dependencies on forward-declared types
93 /// * `errunion_fwd_len` dependencies on forward-declared error union types
94 /// * `aligned_fwd_len` dependencies on aligned types
95 type_start: u32,
96 /// Index into `C.align_dependency_masks`. Starting at this index are `aligned_type_fwd_len`
97 /// items containing the bitmasks for each aligned type (in `C.type_dependencies`).
98 align_mask_start: u32,
99
100 const Resolved = struct {
101 type: []const link.ConstPool.Index,
102 errunion_type: []const link.ConstPool.Index,
103 type_fwd: []const link.ConstPool.Index,
104 errunion_type_fwd: []const link.ConstPool.Index,
105 aligned_type_fwd: []const link.ConstPool.Index,
106 aligned_type_masks: []const u64,
107 };
108
109 fn get(td: *const CTypeDependencies, c: *const C) Resolved {
110 const types_overlong = c.type_dependencies.items[td.type_start..];
69111 return .{
70 .start = lhs.start,
71 .len = lhs.len + rhs.len,
112 .type = types_overlong[0..td.len],
113 .errunion_type = types_overlong[td.len..][0..td.errunion_len],
114 .type_fwd = types_overlong[td.len + td.errunion_len ..][0..td.fwd_len],
115 .errunion_type_fwd = types_overlong[td.len + td.errunion_len + td.fwd_len ..][0..td.errunion_fwd_len],
116 .aligned_type_fwd = types_overlong[td.len + td.errunion_len + td.fwd_len + td.errunion_fwd_len ..][0..td.aligned_fwd_len],
117 .aligned_type_masks = c.align_dependency_masks.items[td.align_mask_start..][0..td.aligned_fwd_len],
72118 };
73119 }
120
121 const empty: CTypeDependencies = .{
122 .len = 0,
123 .errunion_len = 0,
124 .fwd_len = 0,
125 .errunion_fwd_len = 0,
126 .aligned_fwd_len = 0,
127 .type_start = 0,
128 .align_mask_start = 0,
129 };
74130};
75131
76/// Per-declaration data.
77pub const AvBlock = struct {
78 fwd_decl: String = .empty,
79 code: String = .empty,
80 /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate
81 /// over each `Decl` and generate the definition for each used `CType` once.
82 ctype_pool: codegen.CType.Pool = .empty,
83 /// May contain string references to ctype_pool
84 lazy_fns: codegen.LazyFnMap = .{},
85
86 fn deinit(ab: *AvBlock, gpa: Allocator) void {
87 ab.lazy_fns.deinit(gpa);
88 ab.ctype_pool.deinit(gpa);
89 ab.* = undefined;
132const RenderedDecl = struct {
133 fwd_decl: String,
134 code: String,
135 ctype_deps: CTypeDependencies,
136 need_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
137 need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
138 need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
139 need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
140
141 const init: RenderedDecl = .{
142 .fwd_decl = .empty,
143 .code = .empty,
144 .ctype_deps = .empty,
145 .need_uavs = .empty,
146 .need_tag_name_funcs = .empty,
147 .need_never_tail_funcs = .empty,
148 .need_never_inline_funcs = .empty,
149 };
150
151 fn deinit(rd: *RenderedDecl, gpa: Allocator) void {
152 rd.need_uavs.deinit(gpa);
153 rd.need_tag_name_funcs.deinit(gpa);
154 rd.need_never_tail_funcs.deinit(gpa);
155 rd.need_never_inline_funcs.deinit(gpa);
156 rd.* = undefined;
157 }
158
159 /// We are about to re-render this declaration, but we want to reuse the existing buffers, so
160 /// call `clearRetainCapacity` on the containers. Sets `fwd_decl` and `code` to `undefined`,
161 /// because we shouldn't be using the old values any longer.
162 fn clearRetainingCapacity(rd: *RenderedDecl) void {
163 rd.fwd_decl = undefined;
164 rd.code = undefined;
165 rd.need_uavs.clearRetainingCapacity();
166 rd.need_tag_name_funcs.clearRetainingCapacity();
167 rd.need_never_tail_funcs.clearRetainingCapacity();
168 rd.need_never_inline_funcs.clearRetainingCapacity();
90169 }
91170};
92171
93/// Per-exported-symbol data.
94pub const ExportedBlock = struct {
95 fwd_decl: String = .empty,
172const RenderedType = struct {
173 /// If this type lowers to an aggregate, this is a forward declaration of its struct/union tag.
174 /// Otherwise, this is `.empty`.
175 ///
176 /// Populated immediately and never changes.
177 fwd_decl: String,
178
179 /// A forward declaration of an error union type with this type as its *payload*.
180 ///
181 /// Populated immediately and never changes.
182 errunion_fwd_decl: String,
183
184 /// If this type lowers to an aggregate, this is the struct/union definition.
185 /// If this type lowers to a typedef, this is that typedef.
186 /// Otherwise, this is `.empty`.
187 definition: String,
188 /// The `struct` definition for an error union type with this type as its *payload*.
189 ///
190 /// This string is empty iff the payload type does not have a resolved layout. If the layout is
191 /// resolved, the error union struct is defined, even if the payload type lacks runtime bits.
192 errunion_definition: String,
193
194 /// Dependencies which must be satisfied before emitting the name of this type. As such, they
195 /// must be satisfied before emitting `errunion_definition` or any aligned typedef.
196 ///
197 /// Populated immediately and never changes.
198 deps: CTypeDependencies,
199
200 /// Dependencies which must be satisfied before emitting `definition`.
201 definition_deps: CTypeDependencies,
96202};
97203
98pub fn getString(this: C, s: String) []const u8 {
99 return this.string_bytes.items[s.start..][0..s.len];
204/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type.
205pub fn addConst(
206 c: *C,
207 pt: Zcu.PerThread,
208 pool_index: link.ConstPool.Index,
209 val: InternPool.Index,
210) Allocator.Error!void {
211 const zcu = pt.zcu;
212 const gpa = zcu.comp.gpa;
213 assert(zcu.intern_pool.typeOf(val) == .type_type);
214 assert(@intFromEnum(pool_index) == c.types.items.len);
215
216 const ty: Type = .fromInterned(val);
217
218 const fwd_decl: String = fwd_decl: {
219 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
220 defer c.string_bytes = aw.toArrayList();
221 const start = aw.written().len;
222 codegen.CType.render_defs.fwdDecl(ty, &aw.writer, zcu) catch |err| switch (err) {
223 error.WriteFailed => return error.OutOfMemory,
224 };
225 break :fwd_decl .{
226 .start = @intCast(start),
227 .len = @intCast(aw.written().len - start),
228 };
229 };
230
231 const errunion_fwd_decl: String = errunion_fwd_decl: {
232 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
233 defer c.string_bytes = aw.toArrayList();
234 const start = aw.written().len;
235 codegen.CType.render_defs.errunionFwdDecl(ty, &aw.writer, zcu) catch |err| switch (err) {
236 error.WriteFailed => return error.OutOfMemory,
237 };
238 break :errunion_fwd_decl .{
239 .start = @intCast(start),
240 .len = @intCast(aw.written().len - start),
241 };
242 };
243
244 try c.types.append(gpa, .{
245 .fwd_decl = fwd_decl,
246 .errunion_fwd_decl = errunion_fwd_decl,
247 // This field will be populated just below.
248 .deps = undefined,
249 // The remaining fields will be populated later by either `updateConstIncomplete` or
250 // `updateConstComplete` (it is guaranteed that at least one will be called).
251 .definition = undefined,
252 .errunion_definition = undefined,
253 .definition_deps = undefined,
254 });
255
256 {
257 // Find the dependencies required to just render the type `ty`.
258 var arena: std.heap.ArenaAllocator = .init(gpa);
259 defer arena.deinit();
260 var deps: codegen.CType.Dependencies = .empty;
261 defer deps.deinit(gpa);
262 _ = try codegen.CType.lower(ty, &deps, arena.allocator(), zcu);
263 // This call may add more items to `c.types`.
264 const type_deps = try c.addCTypeDependencies(pt, &deps);
265 c.types.items[@intFromEnum(pool_index)].deps = type_deps;
266 }
100267}
101268
102pub fn addString(this: *C, s: []const u8) Allocator.Error!String {
103 const comp = this.base.comp;
104 const gpa = comp.gpa;
105 try this.string_bytes.appendSlice(gpa, s);
106 return .{
107 .start = @intCast(this.string_bytes.items.len - s.len),
108 .len = @intCast(s.len),
269/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type.
270pub fn updateConstIncomplete(
271 c: *C,
272 pt: Zcu.PerThread,
273 index: link.ConstPool.Index,
274 val: InternPool.Index,
275) Allocator.Error!void {
276 const zcu = pt.zcu;
277 const gpa = zcu.comp.gpa;
278
279 assert(zcu.intern_pool.typeOf(val) == .type_type);
280 const ty: Type = .fromInterned(val);
281
282 const rendered: *RenderedType = &c.types.items[@intFromEnum(index)];
283
284 rendered.errunion_definition = .empty;
285 rendered.definition_deps = .empty;
286 rendered.definition = definition: {
287 if (rendered.fwd_decl.len != 0) {
288 // This is a struct or union type. We will never complete it, but we must forward
289 // declare it to ensure that its first usage does not appear in a different scope.
290 break :definition rendered.fwd_decl;
291 }
292 // Otherwise, we might need to `typedef` to `void`.
293 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
294 defer c.string_bytes = aw.toArrayList();
295 const start = aw.written().len;
296 codegen.CType.render_defs.defineIncomplete(ty, &aw.writer, pt) catch |err| switch (err) {
297 error.WriteFailed => return error.OutOfMemory,
298 };
299 break :definition .{
300 .start = @intCast(start),
301 .len = @intCast(aw.written().len - start),
302 };
109303 };
110304}
305/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type.
306pub fn updateConst(
307 c: *C,
308 pt: Zcu.PerThread,
309 index: link.ConstPool.Index,
310 val: InternPool.Index,
311) Allocator.Error!void {
312 const zcu = pt.zcu;
313 const gpa = zcu.comp.gpa;
314
315 assert(zcu.intern_pool.typeOf(val) == .type_type);
316 const ty: Type = .fromInterned(val);
317
318 const rendered: *RenderedType = &c.types.items[@intFromEnum(index)];
319
320 var arena: std.heap.ArenaAllocator = .init(gpa);
321 defer arena.deinit();
322
323 var deps: codegen.CType.Dependencies = .empty;
324 defer deps.deinit(gpa);
325
326 {
327 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
328 defer c.string_bytes = aw.toArrayList();
329 const start = aw.written().len;
330 codegen.CType.render_defs.errunionDefineComplete(
331 ty,
332 &deps,
333 arena.allocator(),
334 &aw.writer,
335 pt,
336 ) catch |err| switch (err) {
337 error.WriteFailed => return error.OutOfMemory,
338 error.OutOfMemory => |e| return e,
339 };
340 rendered.errunion_definition = .{
341 .start = @intCast(start),
342 .len = @intCast(aw.written().len - start),
343 };
344 }
345
346 deps.clearRetainingCapacity();
347
348 {
349 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
350 defer c.string_bytes = aw.toArrayList();
351 const start = aw.written().len;
352 codegen.CType.render_defs.defineComplete(
353 ty,
354 &deps,
355 arena.allocator(),
356 &aw.writer,
357 pt,
358 ) catch |err| switch (err) {
359 error.WriteFailed => return error.OutOfMemory,
360 error.OutOfMemory => |e| return e,
361 };
362 // Remove dependency on a forward declaration of ourselves; we're defining this type so that
363 // forward declaration obviously exists!
364 _ = deps.type_fwd.swapRemove(ty.toIntern());
365 rendered.definition = .{
366 .start = @intCast(start),
367 .len = @intCast(aw.written().len - start),
368 };
369 }
370
371 {
372 // This call invalidates `rendered`.
373 const definition_deps = try c.addCTypeDependencies(pt, &deps);
374 c.types.items[@intFromEnum(index)].definition_deps = definition_deps;
375 }
376}
377
378fn addString(c: *C, vec: []const []const u8) Allocator.Error!String {
379 const gpa = c.base.comp.gpa;
380
381 var len: u32 = 0;
382 for (vec) |s| len += @intCast(s.len);
383 try c.string_bytes.ensureUnusedCapacity(gpa, len);
384
385 const start: u32 = @intCast(c.string_bytes.items.len);
386 for (vec) |s| c.string_bytes.appendSliceAssumeCapacity(s);
387 assert(c.string_bytes.items.len == start + len);
388
389 return .{ .start = start, .len = len };
390}
111391
112392pub fn open(
113393 arena: Allocator,
......@@ -156,267 +436,622 @@ pub fn createEmpty(
156436 .file = file,
157437 .build_id = options.build_id,
158438 },
159 .navs = .empty,
160439 .string_bytes = .empty,
440 .type_dependencies = .empty,
441 .align_dependency_masks = .empty,
442 .navs = .empty,
161443 .uavs = .empty,
162 .aligned_uavs = .empty,
444 .type_pool = .empty,
445 .types = .empty,
446 .bigint_types = .empty,
163447 .exported_navs = .empty,
164448 .exported_uavs = .empty,
165 .fwd_decl_buf = &.{},
166 .code_header_buf = &.{},
167 .code_buf = &.{},
168 .scratch_buf = &.{},
169449 };
170450
171451 return c_file;
172452}
173453
174pub fn deinit(self: *C) void {
175 const gpa = self.base.comp.gpa;
176
177 for (self.navs.values()) |*db| {
178 db.deinit(gpa);
179 }
180 self.navs.deinit(gpa);
181
182 for (self.uavs.values()) |*db| {
183 db.deinit(gpa);
184 }
185 self.uavs.deinit(gpa);
186 self.aligned_uavs.deinit(gpa);
454pub fn deinit(c: *C) void {
455 const gpa = c.base.comp.gpa;
187456
188 self.exported_navs.deinit(gpa);
189 self.exported_uavs.deinit(gpa);
457 for (c.navs.values()) |*r| r.deinit(gpa);
458 for (c.uavs.values()) |*r| r.deinit(gpa);
459
460 c.string_bytes.deinit(gpa);
461 c.type_dependencies.deinit(gpa);
462 c.align_dependency_masks.deinit(gpa);
463 c.navs.deinit(gpa);
464 c.uavs.deinit(gpa);
465 c.type_pool.deinit(gpa);
466 c.types.deinit(gpa);
467 c.bigint_types.deinit(gpa);
468 c.exported_navs.deinit(gpa);
469 c.exported_uavs.deinit(gpa);
470}
190471
191 self.string_bytes.deinit(gpa);
192 gpa.free(self.fwd_decl_buf);
193 gpa.free(self.code_header_buf);
194 gpa.free(self.code_buf);
195 gpa.free(self.scratch_buf);
472pub fn updateContainerType(
473 c: *C,
474 pt: Zcu.PerThread,
475 ty: InternPool.Index,
476 success: bool,
477) link.File.UpdateContainerTypeError!void {
478 try c.type_pool.updateContainerType(pt, .{ .c = c }, ty, success);
196479}
197480
198481pub fn updateFunc(
199 self: *C,
482 c: *C,
200483 pt: Zcu.PerThread,
201484 func_index: InternPool.Index,
202485 mir: *AnyMir,
203) link.File.UpdateNavError!void {
486) Allocator.Error!void {
204487 const zcu = pt.zcu;
205488 const gpa = zcu.gpa;
206 const func = zcu.funcInfo(func_index);
489 const nav = zcu.funcInfo(func_index).owner_nav;
207490
208 const gop = try self.navs.getOrPut(gpa, func.owner_nav);
209 if (gop.found_existing) gop.value_ptr.deinit(gpa);
210 gop.value_ptr.* = .{
211 .code = .empty,
212 .fwd_decl = .empty,
213 .ctype_pool = mir.c.ctype_pool.move(),
214 .lazy_fns = mir.c.lazy_fns.move(),
491 const rendered_decl: *RenderedDecl = rd: {
492 const gop = try c.navs.getOrPut(gpa, nav);
493 if (gop.found_existing) gop.value_ptr.deinit(gpa);
494 break :rd gop.value_ptr;
215495 };
216 gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl);
217 const code_header = try self.addString(mir.c.code_header);
218 const code = try self.addString(mir.c.code);
219 gop.value_ptr.code = code_header.concat(code);
220 try self.addUavsFromCodegen(&mir.c.uavs);
221}
222
223fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void {
224 const gpa = self.base.comp.gpa;
225 const uav = self.uavs.keys()[i];
226
227 var object: codegen.Object = .{
228 .dg = .{
229 .gpa = gpa,
230 .pt = pt,
231 .mod = pt.zcu.root_mod,
232 .error_msg = null,
233 .pass = .{ .uav = uav },
234 .is_naked_fn = false,
235 .expected_block = null,
236 .fwd_decl = undefined,
237 .ctype_pool = .empty,
238 .scratch = .initBuffer(self.scratch_buf),
239 .uavs = .empty,
240 },
241 .code_header = undefined,
242 .code = undefined,
243 .indent_counter = 0,
244 };
245 object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
246 object.code = .initOwnedSlice(gpa, self.code_buf);
247 defer {
248 object.dg.uavs.deinit(gpa);
249 object.dg.ctype_pool.deinit(object.dg.gpa);
250
251 self.fwd_decl_buf = object.dg.fwd_decl.toArrayList().allocatedSlice();
252 self.code_buf = object.code.toArrayList().allocatedSlice();
253 self.scratch_buf = object.dg.scratch.allocatedSlice();
254 }
255 try object.dg.ctype_pool.init(gpa);
256
257 const c_value: codegen.CValue = .{ .constant = Value.fromInterned(uav) };
258 const alignment: Alignment = self.aligned_uavs.get(uav) orelse .none;
259 codegen.genDeclValue(&object, c_value.constant, c_value, alignment, .none) catch |err| switch (err) {
260 error.AnalysisFail => {
261 @panic("TODO: C backend AnalysisFail on anonymous decl");
262 //try zcu.failed_decls.put(gpa, decl_index, object.dg.error_msg.?);
263 //return;
264 },
265 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
496 c.navs.lockPointers();
497 defer c.navs.unlockPointers();
498
499 rendered_decl.* = .{
500 .fwd_decl = try c.addString(&.{mir.c.fwd_decl}),
501 .code = try c.addString(&.{ mir.c.code_header, mir.c.code }),
502 .ctype_deps = try c.addCTypeDependencies(pt, &mir.c.ctype_deps),
503 .need_uavs = mir.c.need_uavs.move(),
504 .need_tag_name_funcs = mir.c.need_tag_name_funcs.move(),
505 .need_never_tail_funcs = mir.c.need_never_tail_funcs.move(),
506 .need_never_inline_funcs = mir.c.need_never_inline_funcs.move(),
266507 };
267508
268 try self.addUavsFromCodegen(&object.dg.uavs);
509 const old_uavs_len = c.uavs.count();
510 try c.uavs.ensureUnusedCapacity(gpa, rendered_decl.need_uavs.count());
511 for (rendered_decl.need_uavs.keys()) |val| {
512 const gop = c.uavs.getOrPutAssumeCapacity(val);
513 if (gop.found_existing) {
514 assert(gop.index < old_uavs_len);
515 } else {
516 assert(gop.index >= old_uavs_len);
517 }
518 }
519 try c.updateNewUavs(pt, old_uavs_len);
269520
270 object.dg.ctype_pool.freeUnusedCapacity(gpa);
271 self.uavs.values()[i] = .{
272 .fwd_decl = try self.addString(object.dg.fwd_decl.written()),
273 .code = try self.addString(object.code.written()),
274 .ctype_pool = object.dg.ctype_pool.move(),
275 };
521 try c.type_pool.flushPending(pt, .{ .c = c });
276522}
277523
278pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.File.UpdateNavError!void {
524pub fn updateNav(
525 c: *C,
526 pt: Zcu.PerThread,
527 nav_index: InternPool.Nav.Index,
528) Allocator.Error!void {
279529 const tracy = trace(@src());
280530 defer tracy.end();
281531
282 const gpa = self.base.comp.gpa;
532 const gpa = c.base.comp.gpa;
283533 const zcu = pt.zcu;
284534 const ip = &zcu.intern_pool;
285535
286536 const nav = ip.getNav(nav_index);
287 const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
537 switch (ip.indexToKey(nav.status.fully_resolved.val)) {
288538 .func => return,
289 .@"extern" => .none,
290 .variable => |variable| variable.init,
291 else => nav.status.fully_resolved.val,
539 .@"extern" => {},
540 else => {
541 const nav_ty: Type = .fromInterned(nav.typeOf(ip));
542 if (!nav_ty.hasRuntimeBits(zcu)) {
543 if (c.navs.fetchSwapRemove(nav_index)) |kv| {
544 var old_rendered = kv.value;
545 old_rendered.deinit(gpa);
546 }
547 return;
548 }
549 },
550 }
551
552 const rendered_decl: *RenderedDecl = rd: {
553 const gop = try c.navs.getOrPut(gpa, nav_index);
554 if (gop.found_existing) {
555 gop.value_ptr.clearRetainingCapacity();
556 } else {
557 gop.value_ptr.* = .init;
558 }
559 break :rd gop.value_ptr;
292560 };
293 if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) return;
561 c.navs.lockPointers();
562 defer c.navs.unlockPointers();
294563
295 const gop = try self.navs.getOrPut(gpa, nav_index);
296 errdefer _ = self.navs.pop();
297 if (!gop.found_existing) gop.value_ptr.* = .{};
298 const ctype_pool = &gop.value_ptr.ctype_pool;
299 try ctype_pool.init(gpa);
300 ctype_pool.clearRetainingCapacity();
564 {
565 var arena: std.heap.ArenaAllocator = .init(gpa);
566 defer arena.deinit();
301567
302 var object: codegen.Object = .{
303 .dg = .{
568 var dg: codegen.DeclGen = .{
304569 .gpa = gpa,
570 .arena = arena.allocator(),
305571 .pt = pt,
306572 .mod = zcu.navFileScope(nav_index).mod.?,
307573 .error_msg = null,
308 .pass = .{ .nav = nav_index },
574 .owner_nav = nav_index.toOptional(),
309575 .is_naked_fn = false,
310576 .expected_block = null,
311 .fwd_decl = undefined,
312 .ctype_pool = ctype_pool.*,
313 .scratch = .initBuffer(self.scratch_buf),
314 .uavs = .empty,
315 },
316 .code_header = undefined,
317 .code = undefined,
318 .indent_counter = 0,
577 .ctype_deps = .empty,
578 .uavs = rendered_decl.need_uavs.move(),
579 };
580
581 defer {
582 rendered_decl.need_uavs = dg.uavs.move();
583 dg.ctype_deps.deinit(gpa);
584 }
585
586 rendered_decl.fwd_decl = fwd_decl: {
587 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
588 defer c.string_bytes = aw.toArrayList();
589 const start = aw.written().len;
590 codegen.genDeclFwd(&dg, &aw.writer) catch |err| switch (err) {
591 error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, dg.error_msg.?)) {
592 error.CodegenFail => return,
593 error.OutOfMemory => |e| return e,
594 },
595 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
596 };
597 break :fwd_decl .{
598 .start = @intCast(start),
599 .len = @intCast(aw.written().len - start),
600 };
601 };
602
603 rendered_decl.code = code: {
604 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
605 defer c.string_bytes = aw.toArrayList();
606 const start = aw.written().len;
607 codegen.genDecl(&dg, &aw.writer) catch |err| switch (err) {
608 error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, dg.error_msg.?)) {
609 error.CodegenFail => return,
610 error.OutOfMemory => |e| return e,
611 },
612 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
613 };
614 break :code .{
615 .start = @intCast(start),
616 .len = @intCast(aw.written().len - start),
617 };
618 };
619
620 rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps);
621 }
622
623 const old_uavs_len = c.uavs.count();
624 try c.uavs.ensureUnusedCapacity(gpa, rendered_decl.need_uavs.count());
625 for (rendered_decl.need_uavs.keys()) |val| {
626 const gop = c.uavs.getOrPutAssumeCapacity(val);
627 if (gop.found_existing) {
628 assert(gop.index < old_uavs_len);
629 } else {
630 assert(gop.index >= old_uavs_len);
631 }
632 }
633 try c.updateNewUavs(pt, old_uavs_len);
634
635 try c.type_pool.flushPending(pt, .{ .c = c });
636}
637
638/// Unlike `updateNav` and `updateFunc`, this does *not* add newly-discovered UAVs to `c.uavs`. The
639/// caller is instead responsible for doing that (by iterating `rendered_decl.need_uavs`). However,
640/// this function *does* still add newly-discovered *types* to `c.type_pool`.
641///
642/// This function does not accept an alignment for the UAV, because the alignment needed on a UAV is
643/// not known until `flush` (since we need to have seen all uses of the UAV first). Instead, `flush`
644/// will prefix the UAV definition with an appropriate alignment annotation if necessary.
645fn updateUav(
646 c: *C,
647 pt: Zcu.PerThread,
648 val: Value,
649 rendered_decl: *RenderedDecl,
650) Allocator.Error!void {
651 const tracy = trace(@src());
652 defer tracy.end();
653
654 const gpa = c.base.comp.gpa;
655
656 var arena: std.heap.ArenaAllocator = .init(gpa);
657 defer arena.deinit();
658
659 var dg: codegen.DeclGen = .{
660 .gpa = gpa,
661 .arena = arena.allocator(),
662 .pt = pt,
663 .mod = pt.zcu.root_mod,
664 .error_msg = null,
665 .owner_nav = .none,
666 .is_naked_fn = false,
667 .expected_block = null,
668 .ctype_deps = .empty,
669 .uavs = .empty,
319670 };
320 object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
321 object.code = .initOwnedSlice(gpa, self.code_buf);
322671 defer {
323 object.dg.uavs.deinit(gpa);
324 ctype_pool.* = object.dg.ctype_pool.move();
325 ctype_pool.freeUnusedCapacity(gpa);
326
327 self.fwd_decl_buf = object.dg.fwd_decl.toArrayList().allocatedSlice();
328 self.code_buf = object.code.toArrayList().allocatedSlice();
329 self.scratch_buf = object.dg.scratch.allocatedSlice();
672 rendered_decl.need_uavs = dg.uavs.move();
673 dg.ctype_deps.deinit(gpa);
330674 }
331675
332 codegen.genDecl(&object) catch |err| switch (err) {
333 error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, object.dg.error_msg.?)) {
334 error.CodegenFail => return,
335 error.OutOfMemory => |e| return e,
336 },
337 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
676 rendered_decl.fwd_decl = fwd_decl: {
677 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
678 defer c.string_bytes = aw.toArrayList();
679 const start = aw.written().len;
680 codegen.genDeclValueFwd(&dg, &aw.writer, .{
681 .name = .{ .constant = val },
682 .@"const" = true,
683 .@"threadlocal" = false,
684 .init_val = val,
685 }) catch |err| switch (err) {
686 error.AnalysisFail => {
687 @panic("TODO: CBE error.AnalysisFail on uav");
688 },
689 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
690 };
691 break :fwd_decl .{
692 .start = @intCast(start),
693 .len = @intCast(aw.written().len - start),
694 };
695 };
696
697 rendered_decl.code = code: {
698 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
699 defer c.string_bytes = aw.toArrayList();
700 const start = aw.written().len;
701 codegen.genDeclValue(&dg, &aw.writer, .{
702 .name = .{ .constant = val },
703 .@"const" = true,
704 .@"threadlocal" = false,
705 .init_val = val,
706 }) catch |err| switch (err) {
707 error.AnalysisFail => {
708 @panic("TODO: CBE error.AnalysisFail on uav");
709 },
710 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
711 };
712 break :code .{
713 .start = @intCast(start),
714 .len = @intCast(aw.written().len - start),
715 };
338716 };
339 gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.written());
340 gop.value_ptr.code = try self.addString(object.code.written());
341 try self.addUavsFromCodegen(&object.dg.uavs);
717
718 rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps);
342719}
343720
344pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
345 // The C backend does not have the ability to fix line numbers without re-generating
346 // the entire Decl.
347 _ = self;
721pub fn updateLineNumber(c: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) error{}!void {
722 // The C backend does not currently emit "#line" directives. Even if it did, it would not be
723 // capable of updating those line numbers without re-generating the entire declaration.
724 _ = c;
348725 _ = pt;
349726 _ = ti_id;
350727}
351728
352fn abiDefines(w: *std.Io.Writer, target: *const std.Target) !void {
353 switch (target.abi) {
354 .msvc, .itanium => try w.writeAll("#define ZIG_TARGET_ABI_MSVC\n"),
355 else => {},
356 }
357 try w.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{
358 target.cMaxIntAlignment(),
359 });
360}
361
362pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
363 _ = arena; // Has the same lifetime as the call to Compilation.update.
364
729pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
365730 const tracy = trace(@src());
366731 defer tracy.end();
367732
368733 const sub_prog_node = prog_node.start("Flush Module", 0);
369734 defer sub_prog_node.end();
370735
371 const comp = self.base.comp;
736 const comp = c.base.comp;
372737 const diags = &comp.link_diags;
373738 const gpa = comp.gpa;
374739 const io = comp.io;
375 const zcu = self.base.comp.zcu.?;
740 const zcu = c.base.comp.zcu.?;
376741 const ip = &zcu.intern_pool;
742 const target = zcu.getTarget();
377743 const pt: Zcu.PerThread = .activate(zcu, tid);
378744 defer pt.deactivate();
379745
746 // If it's somehow not made it into the pool, we need to generate the type `[:0]const u8` for
747 // error names.
748 const slice_const_u8_sentinel_0_pool_index = try c.type_pool.get(
749 pt,
750 .{ .c = c },
751 .slice_const_u8_sentinel_0_type,
752 );
753 try c.type_pool.flushPending(pt, .{ .c = c });
754
755 // Find the set of referenced NAVs; these are the ones we'll emit. It is important in this
756 // backend that we only emit referenced NAVs, because other ones may contain code from past
757 // incremental updates which is invalid C (due to e.g. types changing). Machine code backends
758 // don't have this problem because there are, of course, no type checking performed when you
759 // *execute* a binary!
760 var need_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty;
761 defer need_navs.deinit(gpa);
762 {
763 const unit_references = try zcu.resolveReferences();
764 for (c.navs.keys()) |nav| {
765 const nav_val = ip.getNav(nav).status.fully_resolved.val;
766 const check_unit: ?InternPool.AnalUnit = switch (ip.indexToKey(nav_val)) {
767 else => .wrap(.{ .nav_val = nav }),
768 .func => .wrap(.{ .func = nav_val }),
769 // TODO: this is a hack to deal with the fact that there's currently no good way to
770 // know which `extern`s are alive. This can and will break in certain patterns of
771 // incremental update. We kind of need to think a bit more about how the frontend
772 // actually represents `extern`, it's a bit awkward right now.
773 .@"extern" => null,
774 };
775 if (check_unit) |u| {
776 if (!unit_references.contains(u)) continue;
777 }
778 try need_navs.putNoClobber(gpa, nav, {});
779 }
780 }
781
782 // Using our knowledge of which NAVs are referenced, we now need to discover the set of UAVs and
783 // C types which are referenced (and hence must be emitted). As above, this is necessary to make
784 // sure we only emit valid C code.
785 //
786 // At the same time, we will discover the set of lazy functions which are referenced.
787
788 var need_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .empty;
789 defer need_uavs.deinit(gpa);
790
791 var need_types: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void) = .empty;
792 defer need_types.deinit(gpa);
793 var need_errunion_types: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void) = .empty;
794 defer need_errunion_types.deinit(gpa);
795 var need_aligned_types: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, u64) = .empty;
796 defer need_aligned_types.deinit(gpa);
797
798 var need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty;
799 defer need_tag_name_funcs.deinit(gpa);
800
801 var need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty;
802 defer need_never_tail_funcs.deinit(gpa);
803
804 var need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty;
805 defer need_never_inline_funcs.deinit(gpa);
806
807 // As mentioned above, we need this type for error names.
808 try need_types.put(gpa, slice_const_u8_sentinel_0_pool_index, {});
809
810 // Every exported NAV should have been discovered via `zcu.resolveReferences`...
811 for (c.exported_navs.keys()) |nav| assert(need_navs.contains(nav));
812 // ...but we *do* need to add exported UAVs to the set.
813 try need_uavs.ensureUnusedCapacity(gpa, c.exported_uavs.count());
814 for (c.exported_uavs.keys()) |uav| {
815 const gop = need_uavs.getOrPutAssumeCapacity(uav);
816 if (!gop.found_existing) gop.value_ptr.* = .none;
817 }
818
819 // For every referenced NAV, some UAVs, C types, and lazy functions may be referenced.
820 for (need_navs.keys()) |nav| {
821 const rendered = c.navs.getPtr(nav).?;
822 try mergeNeededCTypes(
823 c,
824 &need_types,
825 &need_errunion_types,
826 &need_aligned_types,
827 &rendered.ctype_deps,
828 );
829 try mergeNeededUavs(zcu, &need_uavs, &rendered.need_uavs);
830
831 try need_tag_name_funcs.ensureUnusedCapacity(gpa, rendered.need_tag_name_funcs.count());
832 for (rendered.need_tag_name_funcs.keys()) |enum_type| {
833 need_tag_name_funcs.putAssumeCapacity(enum_type, {});
834 }
835
836 try need_never_tail_funcs.ensureUnusedCapacity(gpa, rendered.need_never_tail_funcs.count());
837 for (rendered.need_never_tail_funcs.keys()) |fn_nav| {
838 need_never_tail_funcs.putAssumeCapacity(fn_nav, {});
839 }
840
841 try need_never_inline_funcs.ensureUnusedCapacity(gpa, rendered.need_never_inline_funcs.count());
842 for (rendered.need_never_inline_funcs.keys()) |fn_nav| {
843 need_never_inline_funcs.putAssumeCapacity(fn_nav, {});
844 }
845 }
846
847 // UAVs may reference other UAVs or C types.
380848 {
381 var i: usize = 0;
382 while (i < self.uavs.count()) : (i += 1) {
383 try self.updateUav(pt, i);
849 var index: usize = 0;
850 while (need_uavs.count() > index) : (index += 1) {
851 const val = need_uavs.keys()[index];
852 const rendered = c.uavs.getPtr(val).?;
853 try mergeNeededCTypes(
854 c,
855 &need_types,
856 &need_errunion_types,
857 &need_aligned_types,
858 &rendered.ctype_deps,
859 );
860 try mergeNeededUavs(zcu, &need_uavs, &rendered.need_uavs);
384861 }
385862 }
386863
387 // This code path happens exclusively with -ofmt=c. The flush logic for
388 // emit-h is in `flushEmitH` below.
864 // Finally, C types may reference other C types.
865 {
866 var index: usize = 0;
867 var errunion_index: usize = 0;
868 var aligned_index: usize = 0;
869 while (true) {
870 if (index < need_types.count()) {
871 const pool_index = need_types.keys()[index];
872 const rendered = &c.types.items[@intFromEnum(pool_index)];
873 try mergeNeededCTypes(
874 c,
875 &need_types,
876 &need_errunion_types,
877 &need_aligned_types,
878 &rendered.definition_deps, // we're tasked with emitting the *definition* of this type
879 );
880 index += 1;
881 continue;
882 }
389883
390 var f: Flush = .{
391 .ctype_pool = .empty,
392 .ctype_global_from_decl_map = .empty,
393 .ctypes = .empty,
884 if (errunion_index < need_errunion_types.count()) {
885 const payload_pool_index = need_errunion_types.keys()[errunion_index];
886 const rendered = &c.types.items[@intFromEnum(payload_pool_index)];
887 try mergeNeededCTypes(
888 c,
889 &need_types,
890 &need_errunion_types,
891 &need_aligned_types,
892 &rendered.deps, // the error union type requires emitting this type's *name*
893 );
894 errunion_index += 1;
895 continue;
896 }
394897
395 .lazy_ctype_pool = .empty,
396 .lazy_fns = .empty,
397 .lazy_fwd_decl = .empty,
398 .lazy_code = .empty,
898 if (aligned_index < need_aligned_types.count()) {
899 const pool_index = need_aligned_types.keys()[aligned_index];
900 const rendered = &c.types.items[@intFromEnum(pool_index)];
901 try mergeNeededCTypes(
902 c,
903 &need_types,
904 &need_errunion_types,
905 &need_aligned_types,
906 &rendered.deps, // an aligned typedef requires emitting this type's *name*
907 );
908 aligned_index += 1;
909 continue;
910 }
399911
400 .all_buffers = .empty,
401 .file_size = 0,
402 };
912 break;
913 }
914 }
915
916 // Now that we know which types are required, generate aligned typedefs. One buffer per aligned
917 // type, with *all* aligned typedefs for that type.
918 const aligned_type_strings = try arena.alloc([]const u8, need_aligned_types.count());
919 {
920 var aw: std.Io.Writer.Allocating = .init(gpa);
921 defer aw.deinit();
922 var unused_deps: codegen.CType.Dependencies = .empty;
923 defer unused_deps.deinit(gpa);
924 for (
925 need_aligned_types.keys(),
926 need_aligned_types.values(),
927 aligned_type_strings,
928 ) |pool_index, align_mask, *str_out| {
929 const ty: Type = .fromInterned(pool_index.val(&c.type_pool));
930 const has_layout = c.types.items[@intFromEnum(pool_index)].errunion_definition.len > 0;
931 for (0..@bitSizeOf(@TypeOf(align_mask))) |bit_index| {
932 switch (@as(u1, @truncate(align_mask >> @intCast(bit_index)))) {
933 0 => continue,
934 1 => {},
935 }
936 codegen.CType.render_defs.defineAligned(
937 ty,
938 .fromLog2Units(@intCast(bit_index)),
939 has_layout,
940 &unused_deps,
941 arena,
942 &aw.writer,
943 pt,
944 ) catch |err| switch (err) {
945 error.WriteFailed => return error.OutOfMemory,
946 error.OutOfMemory => |e| return e,
947 };
948 }
949 str_out.* = try arena.dupe(u8, aw.written());
950 aw.clearRetainingCapacity();
951 }
952 }
953
954 // We have discovered the full set of NAVs, UAVs, and types we need to emit, and will now begin
955 // to build the output buffer. Our strategy is to emit the C source in this order:
956 //
957 // * ABI defines and `#include "zig.h"`
958 // * Big-int type definitions
959 // * Other CType definitions (traversing the dependency graph to sort topologically)
960 // * Global assembly
961 // * UAV exports
962 // * NAV exports
963 // * UAV forward declarations
964 // * NAV forward declarations
965 // * Lazy declarations (error names; @tagName functions; never_tail/never_inline wrappers)
966 // * UAV definitions
967 // * NAV definitions
968 //
969 // Most of these sections are order-independent within themselves, with the exception of the
970 // type definitions, which must be ordered to avoid a struct/union from embedding a type which
971 // is currently incomplete.
972 //
973 // When emitting UAV forward declarations, if the UAV requires alignment, we must prefix it with
974 // an alignment annotation. We couldn't emit the alignment into the UAV's `RenderedDecl` because
975 // we couldn't have known the required alignment until now!
976
977 var f: Flush = .{ .all_buffers = .empty, .file_size = 0 };
403978 defer f.deinit(gpa);
404979
405 var abi_defines_aw: std.Io.Writer.Allocating = .init(gpa);
406 defer abi_defines_aw.deinit();
407 abiDefines(&abi_defines_aw.writer, zcu.getTarget()) catch |err| switch (err) {
408 error.WriteFailed => return error.OutOfMemory,
409 };
980 // We know exactly what we'll be emitting, so can reserve capacity for all of our buffers!
981
982 try f.all_buffers.ensureUnusedCapacity(gpa, 3 + // ABI defines and `#include "zig.h"`
983 1 + // Big-int type definitions
984 need_types.count() + // `RenderedType.fwd_decl` (worst-case)
985 need_types.count() + // `RenderedType.definition`
986 need_errunion_types.count() + // `RenderedType.errunion_fwd_decl` (worst-case)
987 need_errunion_types.count() + // `RenderedType.errunion_definition`
988 need_aligned_types.count() + // `aligned_type_strings`
989 1 + // Global assembly
990 c.exported_uavs.count() + // UAV export block
991 c.exported_navs.count() + // NAV export block
992 need_uavs.count() + // UAV forward declarations
993 need_navs.count() + // NAV forward declarations
994 1 + // Lazy declarations
995 need_uavs.count() * 3 + // UAV definitions ("static ", "zig_align(4)", "<definition body>")
996 need_navs.count() * 2); // NAV definitions ("static ", "<definition body>")
997
998 // ABI defines and `#include "zig.h"`
999 switch (target.abi) {
1000 .msvc, .itanium => f.appendBufAssumeCapacity("#define ZIG_TARGET_ABI_MSVC\n"),
1001 else => {},
1002 }
1003 f.appendBufAssumeCapacity(try std.fmt.allocPrint(
1004 arena,
1005 "#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n",
1006 .{target.cMaxIntAlignment()},
1007 ));
1008 f.appendBufAssumeCapacity(
1009 \\#include "zig.h"
1010 \\
1011 );
4101012
411 // Covers defines, zig.h, ctypes, asm, lazy fwd.
412 try f.all_buffers.ensureUnusedCapacity(gpa, 5);
1013 // Big-int type definitions
1014 var bigint_aw: std.Io.Writer.Allocating = .init(gpa);
1015 defer bigint_aw.deinit();
1016 for (c.bigint_types.keys()) |bigint| {
1017 codegen.CType.render_defs.defineBigInt(bigint, &bigint_aw.writer, zcu) catch |err| switch (err) {
1018 error.WriteFailed => return error.OutOfMemory,
1019 };
1020 }
1021 f.appendBufAssumeCapacity(bigint_aw.written());
4131022
414 f.appendBufAssumeCapacity(abi_defines_aw.written());
415 f.appendBufAssumeCapacity(zig_h);
1023 // CType definitions
1024 {
1025 var ft: FlushTypes = .{
1026 .c = c,
1027 .f = &f,
1028 .aligned_types = &need_aligned_types,
1029 .aligned_type_strings = aligned_type_strings,
1030 .status = .empty,
1031 .errunion_status = .empty,
1032 .aligned_status = .empty,
1033 };
1034 defer {
1035 ft.status.deinit(gpa);
1036 ft.errunion_status.deinit(gpa);
1037 ft.aligned_status.deinit(gpa);
1038 }
1039 try ft.status.ensureUnusedCapacity(gpa, need_types.count());
1040 try ft.errunion_status.ensureUnusedCapacity(gpa, need_errunion_types.count());
1041 try ft.aligned_status.ensureUnusedCapacity(gpa, need_aligned_types.count());
4161042
417 const ctypes_index = f.all_buffers.items.len;
418 f.all_buffers.items.len += 1;
1043 for (need_types.keys()) |pool_index| {
1044 ft.doType(pool_index);
1045 }
1046 for (need_errunion_types.keys()) |pool_index| {
1047 ft.doErrunionType(pool_index);
1048 }
1049 for (need_aligned_types.keys()) |pool_index| {
1050 ft.doAlignedTypeFwd(pool_index);
1051 }
1052 }
4191053
1054 // Global assembly
4201055 var asm_aw: std.Io.Writer.Allocating = .init(gpa);
4211056 defer asm_aw.deinit();
4221057 codegen.genGlobalAsm(zcu, &asm_aw.writer) catch |err| switch (err) {
......@@ -424,462 +1059,472 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P
4241059 };
4251060 f.appendBufAssumeCapacity(asm_aw.written());
4261061
427 const lazy_index = f.all_buffers.items.len;
428 f.all_buffers.items.len += 1;
1062 var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
1063 defer export_names.deinit(gpa);
1064 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));
1065 for (zcu.single_exports.values()) |export_index| {
1066 export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {});
1067 }
1068 for (zcu.multi_exports.values()) |info| {
1069 try export_names.ensureUnusedCapacity(gpa, info.len);
1070 for (zcu.all_exports.items[info.index..][0..info.len]) |@"export"| {
1071 export_names.putAssumeCapacity(@"export".opts.name, {});
1072 }
1073 }
4291074
430 try f.lazy_ctype_pool.init(gpa);
431 try self.flushErrDecls(pt, &f);
1075 // UAV export block
1076 for (c.exported_uavs.values()) |code| {
1077 f.appendBufAssumeCapacity(code.get(c));
1078 }
4321079
433 // Unlike other backends, the .c code we are emitting has order-dependent decls.
434 // `CType`s, forward decls, and non-functions first.
1080 // NAV export block
1081 for (c.exported_navs.values()) |code| {
1082 f.appendBufAssumeCapacity(code.get(c));
1083 }
4351084
436 {
437 var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
438 defer export_names.deinit(gpa);
439 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));
440 for (zcu.single_exports.values()) |export_index| {
441 export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {});
442 }
443 for (zcu.multi_exports.values()) |info| {
444 try export_names.ensureUnusedCapacity(gpa, info.len);
445 for (zcu.all_exports.items[info.index..][0..info.len]) |@"export"| {
446 export_names.putAssumeCapacity(@"export".opts.name, {});
447 }
1085 // UAV forward declarations
1086 for (need_uavs.keys()) |val| {
1087 if (c.exported_uavs.contains(val)) continue; // the export was the declaration
1088 const fwd_decl = c.uavs.getPtr(val).?.fwd_decl;
1089 f.appendBufAssumeCapacity(fwd_decl.get(c));
1090 }
1091
1092 // NAV forward declarations
1093 for (need_navs.keys()) |nav| {
1094 if (c.exported_navs.contains(nav)) continue; // the export was the declaration
1095 if (ip.getNav(nav).getExtern(ip)) |e| {
1096 if (export_names.contains(e.name)) continue;
4481097 }
1098 const fwd_decl = c.navs.getPtr(nav).?.fwd_decl;
1099 f.appendBufAssumeCapacity(fwd_decl.get(c));
1100 }
4491101
450 for (self.uavs.keys(), self.uavs.values()) |uav, *av_block| try self.flushAvBlock(
451 pt,
452 zcu.root_mod,
453 &f,
454 av_block,
455 self.exported_uavs.getPtr(uav),
456 export_names,
457 .none,
1102 // Lazy declarations
1103 var lazy_decls_aw: std.Io.Writer.Allocating = .init(gpa);
1104 defer lazy_decls_aw.deinit();
1105 {
1106 var lazy_dg: codegen.DeclGen = .{
1107 .gpa = gpa,
1108 .arena = arena,
1109 .pt = pt,
1110 .mod = pt.zcu.root_mod,
1111 .owner_nav = .none,
1112 .is_naked_fn = false,
1113 .expected_block = null,
1114 .error_msg = null,
1115 .ctype_deps = .empty,
1116 .uavs = .empty,
1117 };
1118 defer {
1119 assert(lazy_dg.uavs.count() == 0);
1120 lazy_dg.ctype_deps.deinit(gpa);
1121 }
1122 const slice_const_u8_sentinel_0_cty: codegen.CType = try .lower(
1123 .slice_const_u8_sentinel_0,
1124 &lazy_dg.ctype_deps,
1125 arena,
1126 zcu,
4581127 );
459
460 for (self.navs.keys(), self.navs.values()) |nav, *av_block| try self.flushAvBlock(
461 pt,
462 zcu.navFileScope(nav).mod.?,
463 &f,
464 av_block,
465 self.exported_navs.getPtr(nav),
466 export_names,
467 if (ip.getNav(nav).getExtern(ip) != null)
468 ip.getNav(nav).name.toOptional()
469 else
470 .none,
1128 const slice_const_u8_sentinel_0_name = try std.fmt.allocPrint(
1129 arena,
1130 "{f}",
1131 .{slice_const_u8_sentinel_0_cty.fmtTypeName(zcu)},
4711132 );
1133 codegen.genErrDecls(zcu, &lazy_decls_aw.writer, slice_const_u8_sentinel_0_name) catch |err| switch (err) {
1134 error.WriteFailed => return error.OutOfMemory,
1135 };
1136 for (need_tag_name_funcs.keys()) |enum_ty_ip| {
1137 const enum_ty: Type = .fromInterned(enum_ty_ip);
1138 const enum_cty: codegen.CType = try .lower(
1139 enum_ty,
1140 &lazy_dg.ctype_deps,
1141 arena,
1142 zcu,
1143 );
1144 codegen.genTagNameFn(
1145 zcu,
1146 &lazy_decls_aw.writer,
1147 slice_const_u8_sentinel_0_name,
1148 enum_ty,
1149 try std.fmt.allocPrint(arena, "{f}", .{enum_cty.fmtTypeName(zcu)}),
1150 ) catch |err| switch (err) {
1151 error.WriteFailed => return error.OutOfMemory,
1152 };
1153 }
1154 for (need_never_tail_funcs.keys()) |fn_nav| {
1155 codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_tail, &lazy_decls_aw.writer) catch |err| switch (err) {
1156 error.WriteFailed => return error.OutOfMemory,
1157 error.OutOfMemory => |e| return e,
1158 error.AnalysisFail => unreachable,
1159 };
1160 }
1161 for (need_never_inline_funcs.keys()) |fn_nav| {
1162 codegen.genLazyCallModifierFn(&lazy_dg, fn_nav, .never_inline, &lazy_decls_aw.writer) catch |err| switch (err) {
1163 error.WriteFailed => return error.OutOfMemory,
1164 error.OutOfMemory => |e| return e,
1165 error.AnalysisFail => unreachable,
1166 };
1167 }
4721168 }
473
474 {
475 // We need to flush lazy ctypes after flushing all decls but before flushing any decl ctypes.
476 // This ensures that every lazy CType.Index exactly matches the global CType.Index.
477 try f.ctype_pool.init(gpa);
478 try self.flushCTypes(zcu, &f, .flush, &f.lazy_ctype_pool);
479
480 for (self.uavs.keys(), self.uavs.values()) |uav, av_block| {
481 try self.flushCTypes(zcu, &f, .{ .uav = uav }, &av_block.ctype_pool);
1169 f.appendBufAssumeCapacity(lazy_decls_aw.written());
1170
1171 // UAV definitions
1172 for (need_uavs.keys(), need_uavs.values()) |val, overalign| {
1173 const code = c.uavs.getPtr(val).?.code;
1174 if (code.len == 0) continue;
1175 if (!c.exported_uavs.contains(val)) {
1176 f.appendBufAssumeCapacity("static ");
4821177 }
483
484 for (self.navs.keys(), self.navs.values()) |nav, av_block| {
485 try self.flushCTypes(zcu, &f, .{ .nav = nav }, &av_block.ctype_pool);
1178 if (overalign != .none) {
1179 // As long as `Alignment` isn't too big, it's reasonable to just generate all possible
1180 // alignment annotations statically into a LUT, which avoids allocating strings on this
1181 // path.
1182 comptime assert(@bitSizeOf(Alignment) < 8);
1183 const table_len = (1 << @bitSizeOf(Alignment)) - 1;
1184 const table: [table_len][]const u8 = comptime table: {
1185 @setEvalBranchQuota(16_000);
1186 var table: [table_len][]const u8 = undefined;
1187 for (&table, 0..) |*str, log2_align| {
1188 const byte_align = Alignment.fromLog2Units(log2_align).toByteUnits().?;
1189 str.* = std.fmt.comptimePrint("zig_align({d}) ", .{byte_align});
1190 }
1191 break :table table;
1192 };
1193 f.appendBufAssumeCapacity(table[overalign.toLog2Units()]);
4861194 }
1195 f.appendBufAssumeCapacity(code.get(c));
4871196 }
4881197
489 f.all_buffers.items[ctypes_index] = f.ctypes.items;
490 f.file_size += f.ctypes.items.len;
491
492 f.all_buffers.items[lazy_index] = f.lazy_fwd_decl.items;
493 f.file_size += f.lazy_fwd_decl.items.len;
494
495 // Now the code.
496 try f.all_buffers.ensureUnusedCapacity(gpa, 1 + (self.uavs.count() + self.navs.count()) * 2);
497 f.appendBufAssumeCapacity(f.lazy_code.items);
498 for (self.uavs.keys(), self.uavs.values()) |uav, av_block| f.appendCodeAssumeCapacity(
499 if (self.exported_uavs.contains(uav)) .default else switch (ip.indexToKey(uav)) {
500 .@"extern" => .zig_extern,
501 else => .static,
502 },
503 self.getString(av_block.code),
504 );
505 for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(storage: {
506 if (self.exported_navs.contains(nav)) break :storage .default;
507 if (ip.getNav(nav).getExtern(ip) != null) break :storage .zig_extern;
508 break :storage .static;
509 }, self.getString(av_block.code));
1198 // NAV definitions
1199 for (need_navs.keys()) |nav| {
1200 const code = c.navs.getPtr(nav).?.code;
1201 if (code.len == 0) continue;
1202 if (!c.exported_navs.contains(nav)) {
1203 const is_extern = ip.getNav(nav).getExtern(ip) != null;
1204 f.appendBufAssumeCapacity(if (is_extern) "zig_extern " else "static ");
1205 }
1206 f.appendBufAssumeCapacity(code.get(c));
1207 }
5101208
511 const file = self.base.file.?;
1209 // We've collected all of our buffers; it's now time to actually write the file!
1210 const file = c.base.file.?;
5121211 file.setLength(io, f.file_size) catch |err| return diags.fail("failed to allocate file: {t}", .{err});
5131212 var fw = file.writer(io, &.{});
5141213 var w = &fw.interface;
5151214 w.writeVecAll(f.all_buffers.items) catch |err| switch (err) {
5161215 error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{
517 std.fmt.alt(self.base.emit, .formatEscapeChar), @errorName(fw.err.?),
1216 std.fmt.alt(c.base.emit, .formatEscapeChar), @errorName(fw.err.?),
5181217 }),
5191218 };
5201219}
5211220
5221221const Flush = struct {
523 ctype_pool: codegen.CType.Pool,
524 ctype_global_from_decl_map: std.ArrayList(codegen.CType),
525 ctypes: std.ArrayList(u8),
526
527 lazy_ctype_pool: codegen.CType.Pool,
528 lazy_fns: LazyFns,
529 lazy_fwd_decl: std.ArrayList(u8),
530 lazy_code: std.ArrayList(u8),
531
5321222 /// We collect a list of buffers to write, and write them all at once with pwritev 😎
5331223 all_buffers: std.ArrayList([]const u8),
5341224 /// Keeps track of the total bytes of `all_buffers`.
5351225 file_size: u64,
5361226
537 const LazyFns = std.AutoHashMapUnmanaged(codegen.LazyFnKey, void);
538
5391227 fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void {
5401228 if (buf.len == 0) return;
5411229 f.all_buffers.appendAssumeCapacity(buf);
5421230 f.file_size += buf.len;
5431231 }
5441232
545 fn appendCodeAssumeCapacity(f: *Flush, storage: enum { default, zig_extern, static }, code: []const u8) void {
546 if (code.len == 0) return;
547 f.appendBufAssumeCapacity(switch (storage) {
548 .default => "\n",
549 .zig_extern => "\nzig_extern ",
550 .static => "\nstatic ",
551 });
552 f.appendBufAssumeCapacity(code);
553 }
554
5551233 fn deinit(f: *Flush, gpa: Allocator) void {
556 f.ctype_pool.deinit(gpa);
557 assert(f.ctype_global_from_decl_map.items.len == 0);
558 f.ctype_global_from_decl_map.deinit(gpa);
559 f.ctypes.deinit(gpa);
560 f.lazy_ctype_pool.deinit(gpa);
561 f.lazy_fns.deinit(gpa);
562 f.lazy_fwd_decl.deinit(gpa);
563 f.lazy_code.deinit(gpa);
5641234 f.all_buffers.deinit(gpa);
5651235 }
5661236};
5671237
568const FlushDeclError = error{
569 OutOfMemory,
570};
571
572fn flushCTypes(
573 self: *C,
574 zcu: *Zcu,
575 f: *Flush,
576 pass: codegen.DeclGen.Pass,
577 decl_ctype_pool: *const codegen.CType.Pool,
578) FlushDeclError!void {
579 const gpa = self.base.comp.gpa;
580 const global_ctype_pool = &f.ctype_pool;
581
582 const global_from_decl_map = &f.ctype_global_from_decl_map;
583 assert(global_from_decl_map.items.len == 0);
584 try global_from_decl_map.ensureTotalCapacity(gpa, decl_ctype_pool.items.len);
585 defer global_from_decl_map.clearRetainingCapacity();
586
587 var ctypes_aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &f.ctypes);
588 const ctypes_bw = &ctypes_aw.writer;
589 defer f.ctypes = ctypes_aw.toArrayList();
590
591 for (0..decl_ctype_pool.items.len) |decl_ctype_pool_index| {
592 const PoolAdapter = struct {
593 global_from_decl_map: []const codegen.CType,
594 pub fn eql(pool_adapter: @This(), decl_ctype: codegen.CType, global_ctype: codegen.CType) bool {
595 return if (decl_ctype.toPoolIndex()) |decl_pool_index|
596 decl_pool_index < pool_adapter.global_from_decl_map.len and
597 pool_adapter.global_from_decl_map[decl_pool_index].eql(global_ctype)
598 else
599 decl_ctype.index == global_ctype.index;
600 }
601 pub fn copy(pool_adapter: @This(), decl_ctype: codegen.CType) codegen.CType {
602 return if (decl_ctype.toPoolIndex()) |decl_pool_index|
603 pool_adapter.global_from_decl_map[decl_pool_index]
604 else
605 decl_ctype;
606 }
607 };
608 const decl_ctype = codegen.CType.fromPoolIndex(decl_ctype_pool_index);
609 const global_ctype, const found_existing = try global_ctype_pool.getOrPutAdapted(
610 gpa,
611 decl_ctype_pool,
612 decl_ctype,
613 PoolAdapter{ .global_from_decl_map = global_from_decl_map.items },
614 );
615 global_from_decl_map.appendAssumeCapacity(global_ctype);
616 codegen.genTypeDecl(
617 zcu,
618 ctypes_bw,
619 global_ctype_pool,
620 global_ctype,
621 pass,
622 decl_ctype_pool,
623 decl_ctype,
624 found_existing,
625 ) catch |err| switch (err) {
626 error.WriteFailed => return error.OutOfMemory,
627 };
628 }
629}
1238pub fn updateExports(
1239 c: *C,
1240 pt: Zcu.PerThread,
1241 exported: Zcu.Exported,
1242 export_indices: []const Zcu.Export.Index,
1243) Allocator.Error!void {
1244 const zcu = pt.zcu;
1245 const gpa = zcu.gpa;
6301246
631fn flushErrDecls(self: *C, pt: Zcu.PerThread, f: *Flush) FlushDeclError!void {
632 const gpa = self.base.comp.gpa;
1247 var arena: std.heap.ArenaAllocator = .init(gpa);
1248 defer arena.deinit();
6331249
634 var object: codegen.Object = .{
635 .dg = .{
636 .gpa = gpa,
637 .pt = pt,
638 .mod = pt.zcu.root_mod,
639 .error_msg = null,
640 .pass = .flush,
641 .is_naked_fn = false,
642 .expected_block = null,
643 .fwd_decl = undefined,
644 .ctype_pool = f.lazy_ctype_pool,
645 .scratch = .initBuffer(self.scratch_buf),
646 .uavs = .empty,
647 },
648 .code_header = undefined,
649 .code = undefined,
650 .indent_counter = 0,
1250 var dg: codegen.DeclGen = .{
1251 .gpa = gpa,
1252 .arena = arena.allocator(),
1253 .pt = pt,
1254 .mod = zcu.root_mod,
1255 .owner_nav = .none,
1256 .is_naked_fn = false,
1257 .expected_block = null,
1258 .error_msg = null,
1259 .ctype_deps = .empty,
1260 .uavs = .empty,
6511261 };
652 object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl);
653 object.code = .fromArrayList(gpa, &f.lazy_code);
6541262 defer {
655 object.dg.uavs.deinit(gpa);
656 f.lazy_ctype_pool = object.dg.ctype_pool.move();
657 f.lazy_ctype_pool.freeUnusedCapacity(gpa);
658
659 f.lazy_fwd_decl = object.dg.fwd_decl.toArrayList();
660 f.lazy_code = object.code.toArrayList();
661 self.scratch_buf = object.dg.scratch.allocatedSlice();
1263 assert(dg.uavs.count() == 0);
1264 dg.ctype_deps.deinit(gpa);
6621265 }
6631266
664 codegen.genErrDecls(&object) catch |err| switch (err) {
665 error.AnalysisFail => unreachable,
666 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
667 };
668
669 try self.addUavsFromCodegen(&object.dg.uavs);
670}
671
672fn flushLazyFn(
673 self: *C,
674 pt: Zcu.PerThread,
675 mod: *Module,
676 f: *Flush,
677 lazy_ctype_pool: *const codegen.CType.Pool,
678 lazy_fn: codegen.LazyFnMap.Entry,
679) FlushDeclError!void {
680 const gpa = self.base.comp.gpa;
681
682 var object: codegen.Object = .{
683 .dg = .{
684 .gpa = gpa,
685 .pt = pt,
686 .mod = mod,
687 .error_msg = null,
688 .pass = .flush,
689 .is_naked_fn = false,
690 .expected_block = null,
691 .fwd_decl = undefined,
692 .ctype_pool = f.lazy_ctype_pool,
693 .scratch = .initBuffer(self.scratch_buf),
694 .uavs = .empty,
695 },
696 .code_header = undefined,
697 .code = undefined,
698 .indent_counter = 0,
1267 const code: String = code: {
1268 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
1269 defer c.string_bytes = aw.toArrayList();
1270 const start = aw.written().len;
1271 codegen.genExports(&dg, &aw.writer, exported, export_indices) catch |err| switch (err) {
1272 error.WriteFailed => return error.OutOfMemory,
1273 error.OutOfMemory => |e| return e,
1274 };
1275 break :code .{
1276 .start = @intCast(start),
1277 .len = @intCast(aw.written().len - start),
1278 };
6991279 };
700 object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl);
701 object.code = .fromArrayList(gpa, &f.lazy_code);
702 defer {
703 // If this assert trips just handle the anon_decl_deps the same as
704 // `updateFunc()` does.
705 assert(object.dg.uavs.count() == 0);
706 f.lazy_ctype_pool = object.dg.ctype_pool.move();
707 f.lazy_ctype_pool.freeUnusedCapacity(gpa);
708
709 f.lazy_fwd_decl = object.dg.fwd_decl.toArrayList();
710 f.lazy_code = object.code.toArrayList();
711 self.scratch_buf = object.dg.scratch.allocatedSlice();
1280 switch (exported) {
1281 .nav => |nav| try c.exported_navs.put(gpa, nav, code),
1282 .uav => |uav| try c.exported_uavs.put(gpa, uav, code),
7121283 }
713
714 codegen.genLazyFn(&object, lazy_ctype_pool, lazy_fn) catch |err| switch (err) {
715 error.AnalysisFail => unreachable,
716 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
717 };
7181284}
7191285
720fn flushLazyFns(
1286pub fn deleteExport(
7211287 self: *C,
722 pt: Zcu.PerThread,
723 mod: *Module,
724 f: *Flush,
725 lazy_ctype_pool: *const codegen.CType.Pool,
726 lazy_fns: codegen.LazyFnMap,
727) FlushDeclError!void {
728 const gpa = self.base.comp.gpa;
729 try f.lazy_fns.ensureUnusedCapacity(gpa, @intCast(lazy_fns.count()));
730
731 var it = lazy_fns.iterator();
732 while (it.next()) |entry| {
733 const gop = f.lazy_fns.getOrPutAssumeCapacity(entry.key_ptr.*);
734 if (gop.found_existing) continue;
735 gop.value_ptr.* = {};
736 try self.flushLazyFn(pt, mod, f, lazy_ctype_pool, entry);
1288 exported: Zcu.Exported,
1289 _: InternPool.NullTerminatedString,
1290) void {
1291 switch (exported) {
1292 .nav => |nav| _ = self.exported_navs.swapRemove(nav),
1293 .uav => |uav| _ = self.exported_uavs.swapRemove(uav),
7371294 }
7381295}
7391296
740fn flushAvBlock(
741 self: *C,
742 pt: Zcu.PerThread,
743 mod: *Module,
744 f: *Flush,
745 av_block: *const AvBlock,
746 exported_block: ?*const ExportedBlock,
747 export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void),
748 extern_name: InternPool.OptionalNullTerminatedString,
749) FlushDeclError!void {
750 const gpa = self.base.comp.gpa;
751 try self.flushLazyFns(pt, mod, f, &av_block.ctype_pool, av_block.lazy_fns);
752 try f.all_buffers.ensureUnusedCapacity(gpa, 1);
753 // avoid emitting extern decls that are already exported
754 if (extern_name.unwrap()) |name| if (export_names.contains(name)) return;
755 f.appendBufAssumeCapacity(self.getString(if (exported_block) |exported|
756 exported.fwd_decl
757 else
758 av_block.fwd_decl));
759}
1297fn mergeNeededCTypes(
1298 c: *C,
1299 need_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void),
1300 need_errunion_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void),
1301 need_aligned_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, u64),
1302 deps: *const CTypeDependencies,
1303) Allocator.Error!void {
1304 const gpa = c.base.comp.gpa;
7601305
761pub fn flushEmitH(zcu: *Zcu) !void {
762 const tracy = trace(@src());
763 defer tracy.end();
1306 const resolved = deps.get(c);
7641307
765 if (true) return; // emit-h is regressed
1308 try need_types.ensureUnusedCapacity(gpa, resolved.type.len + resolved.type_fwd.len);
1309 try need_errunion_types.ensureUnusedCapacity(gpa, resolved.errunion_type.len + resolved.errunion_type_fwd.len);
1310 try need_aligned_types.ensureUnusedCapacity(gpa, resolved.aligned_type_fwd.len);
7661311
767 const emit_h = zcu.emit_h orelse return;
768 const io = zcu.comp.io;
1312 for (resolved.type) |index| need_types.putAssumeCapacity(index, {});
1313 for (resolved.type_fwd) |index| need_types.putAssumeCapacity(index, {});
7691314
770 // We collect a list of buffers to write, and write them all at once with pwritev 😎
771 const num_buffers = emit_h.decl_table.count() + 1;
772 var all_buffers = try std.array_list.Managed(std.posix.iovec_const).initCapacity(zcu.gpa, num_buffers);
773 defer all_buffers.deinit();
1315 for (resolved.errunion_type) |index| need_errunion_types.putAssumeCapacity(index, {});
1316 for (resolved.errunion_type_fwd) |index| need_errunion_types.putAssumeCapacity(index, {});
7741317
775 var file_size: u64 = zig_h.len;
776 if (zig_h.len != 0) {
777 all_buffers.appendAssumeCapacity(.{
778 .base = zig_h,
779 .len = zig_h.len,
780 });
1318 for (resolved.aligned_type_fwd, resolved.aligned_type_masks) |ty_index, align_mask| {
1319 const gop = need_aligned_types.getOrPutAssumeCapacity(ty_index);
1320 if (!gop.found_existing) gop.value_ptr.* = 0;
1321 gop.value_ptr.* |= align_mask;
7811322 }
1323}
7821324
783 for (emit_h.decl_table.keys()) |decl_index| {
784 const decl_emit_h = emit_h.declPtr(decl_index);
785 const buf = decl_emit_h.fwd_decl.items;
786 if (buf.len != 0) {
787 all_buffers.appendAssumeCapacity(.{
788 .base = buf.ptr,
789 .len = buf.len,
790 });
791 file_size += buf.len;
1325fn mergeNeededUavs(
1326 zcu: *const Zcu,
1327 global: *std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
1328 new: *const std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
1329) Allocator.Error!void {
1330 const gpa = zcu.comp.gpa;
1331
1332 try global.ensureUnusedCapacity(gpa, new.count());
1333 for (new.keys(), new.values()) |uav_val, need_align| {
1334 const gop = global.getOrPutAssumeCapacity(uav_val);
1335 if (!gop.found_existing) gop.value_ptr.* = .none;
1336
1337 if (need_align != .none) {
1338 const cur_align = switch (gop.value_ptr.*) {
1339 .none => Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu),
1340 else => |a| a,
1341 };
1342 if (need_align.compareStrict(.gt, cur_align)) {
1343 gop.value_ptr.* = need_align;
1344 }
7921345 }
7931346 }
794
795 const directory = emit_h.loc.directory orelse zcu.comp.local_cache_directory;
796 const file = try directory.handle.createFile(io, emit_h.loc.basename, .{
797 // We set the end position explicitly below; by not truncating the file, we possibly
798 // make it easier on the file system by doing 1 reallocation instead of two.
799 .truncate = false,
800 });
801 defer file.close(io);
802
803 try file.setLength(io, file_size);
804 try file.pwritevAll(all_buffers.items, 0);
8051347}
8061348
807pub fn updateExports(
808 self: *C,
1349fn addCTypeDependencies(
1350 c: *C,
8091351 pt: Zcu.PerThread,
810 exported: Zcu.Exported,
811 export_indices: []const Zcu.Export.Index,
812) !void {
813 const zcu = pt.zcu;
814 const gpa = zcu.gpa;
815 const mod, const pass: codegen.DeclGen.Pass, const decl_block, const exported_block = switch (exported) {
816 .nav => |nav| .{
817 zcu.navFileScope(nav).mod.?,
818 .{ .nav = nav },
819 self.navs.getPtr(nav).?,
820 (try self.exported_navs.getOrPut(gpa, nav)).value_ptr,
821 },
822 .uav => |uav| .{
823 zcu.root_mod,
824 .{ .uav = uav },
825 self.uavs.getPtr(uav).?,
826 (try self.exported_uavs.getOrPut(gpa, uav)).value_ptr,
827 },
828 };
829 const ctype_pool = &decl_block.ctype_pool;
830 var dg: codegen.DeclGen = .{
831 .gpa = gpa,
832 .pt = pt,
833 .mod = mod,
834 .error_msg = null,
835 .pass = pass,
836 .is_naked_fn = false,
837 .expected_block = null,
838 .fwd_decl = undefined,
839 .ctype_pool = decl_block.ctype_pool,
840 .scratch = .initBuffer(self.scratch_buf),
841 .uavs = .empty,
842 };
843 dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
844 defer {
845 assert(dg.uavs.count() == 0);
846 ctype_pool.* = dg.ctype_pool.move();
847 ctype_pool.freeUnusedCapacity(gpa);
1352 deps: *const codegen.CType.Dependencies,
1353) Allocator.Error!CTypeDependencies {
1354 const gpa = pt.zcu.comp.gpa;
1355
1356 try c.bigint_types.ensureUnusedCapacity(gpa, deps.bigint.count());
1357 for (deps.bigint.keys()) |bigint| c.bigint_types.putAssumeCapacity(bigint, {});
1358
1359 const type_start = c.type_dependencies.items.len;
1360 const errunion_type_start = type_start + deps.type.count();
1361 const type_fwd_start = errunion_type_start + deps.errunion_type.count();
1362 const errunion_type_fwd_start = type_fwd_start + deps.type_fwd.count();
1363 const aligned_type_fwd_start = errunion_type_fwd_start + deps.errunion_type_fwd.count();
1364 try c.type_dependencies.appendNTimes(gpa, undefined, deps.type.count() +
1365 deps.errunion_type.count() +
1366 deps.type_fwd.count() +
1367 deps.errunion_type_fwd.count() +
1368 deps.aligned_type_fwd.count());
1369
1370 const align_mask_start = c.align_dependency_masks.items.len;
1371 try c.align_dependency_masks.appendSlice(gpa, deps.aligned_type_fwd.values());
1372
1373 for (deps.type.keys(), type_start..) |ty, i| {
1374 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1375 c.type_dependencies.items[i] = pool_index;
1376 }
8481377
849 self.fwd_decl_buf = dg.fwd_decl.toArrayList().allocatedSlice();
850 self.scratch_buf = dg.scratch.allocatedSlice();
1378 for (deps.errunion_type.keys(), errunion_type_start..) |ty, i| {
1379 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1380 c.type_dependencies.items[i] = pool_index;
8511381 }
852 codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) {
853 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,
1382
1383 for (deps.type_fwd.keys(), type_fwd_start..) |ty, i| {
1384 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1385 c.type_dependencies.items[i] = pool_index;
1386 }
1387
1388 for (deps.errunion_type_fwd.keys(), errunion_type_fwd_start..) |ty, i| {
1389 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1390 c.type_dependencies.items[i] = pool_index;
1391 }
1392
1393 for (deps.aligned_type_fwd.keys(), aligned_type_fwd_start..) |ty, i| {
1394 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1395 c.type_dependencies.items[i] = pool_index;
1396 }
1397
1398 return .{
1399 .len = @intCast(deps.type.count()),
1400 .errunion_len = @intCast(deps.errunion_type.count()),
1401 .fwd_len = @intCast(deps.type_fwd.count()),
1402 .errunion_fwd_len = @intCast(deps.errunion_type_fwd.count()),
1403 .aligned_fwd_len = @intCast(deps.aligned_type_fwd.count()),
1404 .type_start = @intCast(type_start),
1405 .align_mask_start = @intCast(align_mask_start),
8541406 };
855 exported_block.* = .{ .fwd_decl = try self.addString(dg.fwd_decl.written()) };
8561407}
8571408
858pub fn deleteExport(
859 self: *C,
860 exported: Zcu.Exported,
861 _: InternPool.NullTerminatedString,
862) void {
863 switch (exported) {
864 .nav => |nav| _ = self.exported_navs.swapRemove(nav),
865 .uav => |uav| _ = self.exported_uavs.swapRemove(uav),
1409fn updateNewUavs(c: *C, pt: Zcu.PerThread, old_uavs_len: usize) Allocator.Error!void {
1410 const gpa = pt.zcu.comp.gpa;
1411 var index = old_uavs_len;
1412 while (index < c.uavs.count()) : (index += 1) {
1413 // `new_uavs` is UAVs discovered while lowering *this* UAV.
1414 const new_uavs: []const InternPool.Index = new: {
1415 c.uavs.lockPointers();
1416 defer c.uavs.unlockPointers();
1417 const val: Value = .fromInterned(c.uavs.keys()[index]);
1418 const rendered_decl = &c.uavs.values()[index];
1419 rendered_decl.* = .init;
1420 try c.updateUav(pt, val, rendered_decl);
1421 break :new rendered_decl.need_uavs.keys();
1422 };
1423 try c.uavs.ensureUnusedCapacity(gpa, new_uavs.len);
1424 for (new_uavs) |val| {
1425 const gop = c.uavs.getOrPutAssumeCapacity(val);
1426 if (!gop.found_existing) {
1427 assert(gop.index > index);
1428 }
1429 }
8661430 }
8671431}
8681432
869fn addUavsFromCodegen(c: *C, uavs: *const std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment)) Allocator.Error!void {
870 const gpa = c.base.comp.gpa;
871 try c.uavs.ensureUnusedCapacity(gpa, uavs.count());
872 try c.aligned_uavs.ensureUnusedCapacity(gpa, uavs.count());
873 for (uavs.keys(), uavs.values()) |uav_val, uav_align| {
874 {
875 const gop = c.uavs.getOrPutAssumeCapacity(uav_val);
876 if (!gop.found_existing) gop.value_ptr.* = .{};
1433const FlushTypes = struct {
1434 c: *C,
1435 f: *Flush,
1436
1437 aligned_types: *const std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, u64),
1438 aligned_type_strings: []const []const u8,
1439
1440 status: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, bool),
1441 errunion_status: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, bool),
1442 aligned_status: std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void),
1443
1444 fn processDeps(ft: *FlushTypes, deps: *const CTypeDependencies) void {
1445 const resolved = deps.get(ft.c);
1446 for (resolved.type) |pool_index| ft.doType(pool_index);
1447 for (resolved.type_fwd) |pool_index| ft.doTypeFwd(pool_index);
1448 for (resolved.errunion_type) |pool_index| ft.doErrunionType(pool_index);
1449 for (resolved.errunion_type_fwd) |pool_index| ft.doErrunionTypeFwd(pool_index);
1450 for (resolved.aligned_type_fwd) |pool_index| ft.doAlignedTypeFwd(pool_index);
1451 }
1452 fn processDepsAsFwd(ft: *FlushTypes, deps: *const CTypeDependencies) void {
1453 const resolved = deps.get(ft.c);
1454 for (resolved.type) |pool_index| ft.doTypeFwd(pool_index);
1455 for (resolved.type_fwd) |pool_index| ft.doTypeFwd(pool_index);
1456 for (resolved.errunion_type) |pool_index| ft.doErrunionTypeFwd(pool_index);
1457 for (resolved.errunion_type_fwd) |pool_index| ft.doErrunionTypeFwd(pool_index);
1458 for (resolved.aligned_type_fwd) |pool_index| ft.doAlignedTypeFwd(pool_index);
1459 }
1460
1461 fn doAlignedTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1462 const c = ft.c;
1463 if (ft.aligned_status.contains(pool_index)) return;
1464 if (ft.aligned_types.getIndex(pool_index)) |i| {
1465 const rendered = &c.types.items[@intFromEnum(pool_index)];
1466 ft.processDepsAsFwd(&rendered.deps);
1467 ft.f.appendBufAssumeCapacity(ft.aligned_type_strings[i]);
8771468 }
878 if (uav_align != .none) {
879 const gop = c.aligned_uavs.getOrPutAssumeCapacity(uav_val);
880 gop.value_ptr.* = if (gop.found_existing) max: {
881 break :max gop.value_ptr.*.maxStrict(uav_align);
882 } else uav_align;
1469 ft.aligned_status.putAssumeCapacity(pool_index, {});
1470 }
1471 fn doTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1472 const c = ft.c;
1473 if (ft.status.contains(pool_index)) return;
1474 const rendered = &c.types.items[@intFromEnum(pool_index)];
1475 if (rendered.fwd_decl.len > 0) {
1476 ft.f.appendBufAssumeCapacity(rendered.fwd_decl.get(c));
1477 ft.status.putAssumeCapacityNoClobber(pool_index, false);
1478 } else {
1479 ft.processDepsAsFwd(&rendered.definition_deps);
1480 const gop = ft.status.getOrPutAssumeCapacity(pool_index);
1481 if (!gop.found_existing) {
1482 gop.value_ptr.* = false;
1483 ft.f.appendBufAssumeCapacity(rendered.definition.get(c));
1484 }
8831485 }
8841486 }
885}
1487 fn doType(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1488 const c = ft.c;
1489 if (ft.status.get(pool_index)) |completed| {
1490 if (completed) return;
1491 }
1492 const rendered = &c.types.items[@intFromEnum(pool_index)];
1493 ft.processDeps(&rendered.definition_deps);
1494 if (rendered.fwd_decl.len == 0 and ft.status.contains(pool_index)) {
1495 // `doTypeFwd` already rendered the defintion, we just had to complete the type by
1496 // fully resolving its dependencies.
1497 } else if (rendered.definition.len > 0) {
1498 ft.f.appendBufAssumeCapacity(rendered.definition.get(c));
1499 } else if (!ft.status.contains(pool_index)) {
1500 // The type will never be completed, but it must be forward declared to avoid it being
1501 // declared in the wrong scope.
1502 ft.f.appendBufAssumeCapacity(rendered.fwd_decl.get(c));
1503 }
1504 ft.status.putAssumeCapacity(pool_index, true);
1505 }
1506 fn doErrunionTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1507 const c = ft.c;
1508 const gop = ft.errunion_status.getOrPutAssumeCapacity(pool_index);
1509 if (gop.found_existing) return;
1510 const rendered = &c.types.items[@intFromEnum(pool_index)];
1511 ft.f.appendBufAssumeCapacity(rendered.errunion_fwd_decl.get(c));
1512 gop.value_ptr.* = false;
1513 }
1514 fn doErrunionType(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
1515 const c = ft.c;
1516 if (ft.errunion_status.get(pool_index)) |completed| {
1517 if (completed) return;
1518 }
1519 const rendered = &c.types.items[@intFromEnum(pool_index)];
1520 ft.processDeps(&rendered.deps);
1521 if (rendered.errunion_definition.len > 0) {
1522 ft.f.appendBufAssumeCapacity(rendered.errunion_definition.get(c));
1523 } else {
1524 // The error union type will never be completed, but forward declare it to avoid the
1525 // type being first declared in a different scope.
1526 ft.f.appendBufAssumeCapacity(rendered.errunion_fwd_decl.get(c));
1527 }
1528 ft.errunion_status.putAssumeCapacity(pool_index, true);
1529 }
1530};
src/link/ConstPool.zig created+288
......@@ -0,0 +1,288 @@
1/// Helper type for debug information implementations (such as `link.Dwarf`) to help them emit
2/// information about comptime-known values (constants), including types.
3///
4/// Every constant with associated debug information is assigned an `Index` by calling `get`. The
5/// pool will track which container types do and do not have a resolved layout, as well as which
6/// constants in the pool depend on which types, and call into the implementation to emit debug
7/// information for a constant only when all information is available.
8///
9/// Indices into the pool are dense, and constants are never removed from the pool, so the debug
10/// info implementation can store information for each one with a simple `ArrayList`.
11///
12/// To use `ConstPool`, the debug info implementation is required to:
13/// * forward `updateContainerType` calls to its `ConstPool`
14/// * expose some callback functions---see functions in `User`
15/// * ensure that any `get` call is eventually followed by a `flushPending` call
16const ConstPool = @This();
17
18values: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
19pending: std.ArrayList(Index),
20complete_containers: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
21container_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, ContainerDepEntry.Index),
22container_dep_entries: std.ArrayList(ContainerDepEntry),
23
24pub const empty: ConstPool = .{
25 .values = .empty,
26 .pending = .empty,
27 .complete_containers = .empty,
28 .container_deps = .empty,
29 .container_dep_entries = .empty,
30};
31
32pub fn deinit(pool: *ConstPool, gpa: Allocator) void {
33 pool.values.deinit(gpa);
34 pool.pending.deinit(gpa);
35 pool.complete_containers.deinit(gpa);
36 pool.container_deps.deinit(gpa);
37 pool.container_dep_entries.deinit(gpa);
38}
39
40pub const Index = enum(u32) {
41 _,
42 pub fn val(i: Index, pool: *const ConstPool) InternPool.Index {
43 return pool.values.keys()[@intFromEnum(i)];
44 }
45};
46
47pub const User = union(enum) {
48 dwarf: *@import("Dwarf.zig"),
49 c: *@import("C.zig"),
50 llvm: @import("../codegen/llvm.zig").Object.Ptr,
51
52 /// Inform the debug info implementation that the new constant `val` was added to the pool at
53 /// the given index (which equals the current pool length) due to a `get` call. It is guaranteed
54 /// that there will eventually be a call to either `updateConst` or `updateConstIncomplete`
55 /// following the `addConst` call, to actually populate the constant's debug info.
56 fn addConst(
57 user: User,
58 pt: Zcu.PerThread,
59 index: Index,
60 val: InternPool.Index,
61 ) Allocator.Error!void {
62 switch (user) {
63 inline else => |impl| return impl.addConst(pt, index, val),
64 }
65 }
66
67 /// Tell the debug info implementation to emit information for the constant `val`, which is in
68 /// the pool at the given index. `val` is "complete", which means:
69 /// * If it is a type, its layout is known.
70 /// * Otherwise, the layout of its type is known.
71 fn updateConst(
72 user: User,
73 pt: Zcu.PerThread,
74 index: Index,
75 val: InternPool.Index,
76 ) Allocator.Error!void {
77 switch (user) {
78 inline else => |impl| return impl.updateConst(pt, index, val),
79 }
80 }
81
82 /// Tell the debug info implementation to emit information for the constant `val`, which is in
83 /// the pool at the given index. `val` is "incomplete", meaning the implementation cannot emit
84 /// full information for it (for instance, perhaps it is a struct type which was never actually
85 /// initialized so never had its layout resolved). Instead, the implementation must emit some
86 /// form of placeholder entry representing an incomplete/unknown constant.
87 fn updateConstIncomplete(
88 user: User,
89 pt: Zcu.PerThread,
90 index: Index,
91 val: InternPool.Index,
92 ) Allocator.Error!void {
93 switch (user) {
94 inline else => |impl| return impl.updateConstIncomplete(pt, index, val),
95 }
96 }
97};
98
99const ContainerDepEntry = extern struct {
100 next: ContainerDepEntry.Index.Optional,
101 depender: ConstPool.Index,
102 const Index = enum(u32) {
103 _,
104 const Optional = enum(u32) {
105 none = std.math.maxInt(u32),
106 _,
107 fn unwrap(o: Optional) ?ContainerDepEntry.Index {
108 return switch (o) {
109 .none => null,
110 else => @enumFromInt(@intFromEnum(o)),
111 };
112 }
113 };
114 fn toOptional(i: ContainerDepEntry.Index) Optional {
115 return @enumFromInt(@intFromEnum(i));
116 }
117 fn ptr(i: ContainerDepEntry.Index, pool: *ConstPool) *ContainerDepEntry {
118 return &pool.container_dep_entries.items[@intFromEnum(i)];
119 }
120 };
121};
122
123/// Calls to `link.File.updateContainerType` must be forwarded to this function so that the debug
124/// constant pool has up-to-date information about the resolution status of types.
125pub fn updateContainerType(
126 pool: *ConstPool,
127 pt: Zcu.PerThread,
128 user: User,
129 container_ty: InternPool.Index,
130 success: bool,
131) Allocator.Error!void {
132 if (success) {
133 const gpa = pt.zcu.comp.gpa;
134 try pool.complete_containers.put(gpa, container_ty, {});
135 } else {
136 _ = pool.complete_containers.fetchSwapRemove(container_ty);
137 }
138 var opt_dep = pool.container_deps.get(container_ty);
139 while (opt_dep) |dep| : (opt_dep = dep.ptr(pool).next.unwrap()) {
140 try pool.update(pt, user, dep.ptr(pool).depender);
141 }
142}
143
144/// After this is called, there may be a constant for which debug information (complete or not) has
145/// not yet been emitted, so the user must call `flushPending` at some point after this call.
146pub fn get(pool: *ConstPool, pt: Zcu.PerThread, user: User, val: InternPool.Index) Allocator.Error!ConstPool.Index {
147 const zcu = pt.zcu;
148 const ip = &zcu.intern_pool;
149 const gpa = zcu.comp.gpa;
150 const gop = try pool.values.getOrPut(gpa, val);
151 const index: ConstPool.Index = @enumFromInt(gop.index);
152 if (!gop.found_existing) {
153 const ty: Type = switch (ip.typeOf(val)) {
154 .type_type => if (ip.isUndef(val)) .type else .fromInterned(val),
155 else => |ty| .fromInterned(ty),
156 };
157 try pool.registerTypeDeps(index, ty, zcu);
158 try pool.pending.append(gpa, index);
159 try user.addConst(pt, index, val);
160 }
161 return index;
162}
163pub fn flushPending(pool: *ConstPool, pt: Zcu.PerThread, user: User) Allocator.Error!void {
164 while (pool.pending.pop()) |pending_ty| {
165 try pool.update(pt, user, pending_ty);
166 }
167}
168
169fn update(pool: *ConstPool, pt: Zcu.PerThread, user: User, index: ConstPool.Index) Allocator.Error!void {
170 const zcu = pt.zcu;
171 const ip = &zcu.intern_pool;
172 const val = index.val(pool);
173 const ty: Type = switch (ip.typeOf(val)) {
174 .type_type => if (ip.isUndef(val)) .type else .fromInterned(val),
175 else => |ty| .fromInterned(ty),
176 };
177 if (pool.checkType(ty, zcu)) {
178 try user.updateConst(pt, index, val);
179 } else {
180 try user.updateConstIncomplete(pt, index, val);
181 }
182}
183fn checkType(pool: *const ConstPool, ty: Type, zcu: *const Zcu) bool {
184 if (ty.isGenericPoison()) return true;
185 return switch (ty.zigTypeTag(zcu)) {
186 .type,
187 .void,
188 .bool,
189 .noreturn,
190 .int,
191 .float,
192 .pointer,
193 .comptime_float,
194 .comptime_int,
195 .undefined,
196 .null,
197 .error_set,
198 .@"opaque",
199 .frame,
200 .@"anyframe",
201 .enum_literal,
202 => true,
203
204 .array, .vector => pool.checkType(ty.childType(zcu), zcu),
205 .optional => pool.checkType(ty.optionalChild(zcu), zcu),
206 .error_union => pool.checkType(ty.errorUnionPayload(zcu), zcu),
207 .@"fn" => {
208 const ip = &zcu.intern_pool;
209 const func = ip.indexToKey(ty.toIntern()).func_type;
210 for (func.param_types.get(ip)) |param_ty_ip| {
211 if (!pool.checkType(.fromInterned(param_ty_ip), zcu)) return false;
212 }
213 return pool.checkType(.fromInterned(func.return_type), zcu);
214 },
215 .@"struct" => if (ty.isTuple(zcu)) {
216 for (0..ty.structFieldCount(zcu)) |field_index| {
217 if (!pool.checkType(ty.fieldType(field_index, zcu), zcu)) return false;
218 }
219 return true;
220 } else {
221 return pool.complete_containers.contains(ty.toIntern());
222 },
223 .@"union", .@"enum" => {
224 return pool.complete_containers.contains(ty.toIntern());
225 },
226 };
227}
228fn registerTypeDeps(pool: *ConstPool, root: Index, ty: Type, zcu: *const Zcu) Allocator.Error!void {
229 if (ty.isGenericPoison()) return;
230 switch (ty.zigTypeTag(zcu)) {
231 .type,
232 .void,
233 .bool,
234 .noreturn,
235 .int,
236 .float,
237 .pointer,
238 .comptime_float,
239 .comptime_int,
240 .undefined,
241 .null,
242 .error_set,
243 .@"opaque",
244 .frame,
245 .@"anyframe",
246 .enum_literal,
247 => {},
248
249 .array, .vector => try pool.registerTypeDeps(root, ty.childType(zcu), zcu),
250 .optional => try pool.registerTypeDeps(root, ty.optionalChild(zcu), zcu),
251 .error_union => try pool.registerTypeDeps(root, ty.errorUnionPayload(zcu), zcu),
252 .@"fn" => {
253 const ip = &zcu.intern_pool;
254 const func = ip.indexToKey(ty.toIntern()).func_type;
255 for (func.param_types.get(ip)) |param_ty_ip| {
256 try pool.registerTypeDeps(root, .fromInterned(param_ty_ip), zcu);
257 }
258 try pool.registerTypeDeps(root, .fromInterned(func.return_type), zcu);
259 },
260 .@"struct", .@"union", .@"enum" => if (ty.isTuple(zcu)) {
261 for (0..ty.structFieldCount(zcu)) |field_index| {
262 try pool.registerTypeDeps(root, ty.fieldType(field_index, zcu), zcu);
263 }
264 } else {
265 // `ty` is a container; register the dependency.
266
267 const gpa = zcu.comp.gpa;
268 try pool.container_deps.ensureUnusedCapacity(gpa, 1);
269 try pool.container_dep_entries.ensureUnusedCapacity(gpa, 1);
270 errdefer comptime unreachable;
271
272 const gop = pool.container_deps.getOrPutAssumeCapacity(ty.toIntern());
273 const entry: ContainerDepEntry.Index = @enumFromInt(pool.container_dep_entries.items.len);
274 pool.container_dep_entries.appendAssumeCapacity(.{
275 .next = if (gop.found_existing) gop.value_ptr.toOptional() else .none,
276 .depender = root,
277 });
278 gop.value_ptr.* = entry;
279 },
280 }
281}
282
283const std = @import("std");
284const Allocator = std.mem.Allocator;
285
286const InternPool = @import("../InternPool.zig");
287const Type = @import("../Type.zig");
288const Zcu = @import("../Zcu.zig");
src/link/DebugConstPool.zig deleted-290
......@@ -1,290 +0,0 @@
1/// Helper type for debug information implementations (such as `link.Dwarf`) to help them emit
2/// information about comptime-known values (constants), including types.
3///
4/// Every constant with associated debug information is assigned an `Index` by calling `get`. The
5/// pool will track which container types do and do not have a resolved layout, as well as which
6/// constants in the pool depend on which types, and call into the implementation to emit debug
7/// information for a constant only when all information is available.
8///
9/// Indices into the pool are dense, and constants are never removed from the pool, so the debug
10/// info implementation can store information for each one with a simple `ArrayList`.
11///
12/// To use `DebugConstPool`, the debug info implementation is required to:
13/// * forward `updateContainerType` calls to its `DebugConstPool`
14/// * expose some callback functions---see functions in `DebugInfo`
15/// * ensure that any `get` call is eventually followed by a `flushPending` call
16///
17/// TODO: everything in this file should have the error set 'Allocator.Error', but right now the
18/// self-hosted linkers can return all kinds of crap for some reason. This needs fixing.
19const DebugConstPool = @This();
20
21values: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
22pending: std.ArrayList(Index),
23complete_containers: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
24container_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, ContainerDepEntry.Index),
25container_dep_entries: std.ArrayList(ContainerDepEntry),
26
27pub const empty: DebugConstPool = .{
28 .values = .empty,
29 .pending = .empty,
30 .complete_containers = .empty,
31 .container_deps = .empty,
32 .container_dep_entries = .empty,
33};
34
35pub fn deinit(pool: *DebugConstPool, gpa: Allocator) void {
36 pool.values.deinit(gpa);
37 pool.pending.deinit(gpa);
38 pool.complete_containers.deinit(gpa);
39 pool.container_deps.deinit(gpa);
40 pool.container_dep_entries.deinit(gpa);
41}
42
43pub const Index = enum(u32) {
44 _,
45 pub fn val(i: Index, pool: *const DebugConstPool) InternPool.Index {
46 return pool.values.keys()[@intFromEnum(i)];
47 }
48};
49
50pub const DebugInfo = union(enum) {
51 dwarf: *@import("Dwarf.zig"),
52 llvm: @import("../codegen/llvm.zig").Object.Ptr,
53
54 /// Inform the debug info implementation that the new constant `val` was added to the pool at
55 /// the given index (which equals the current pool length) due to a `get` call. It is guaranteed
56 /// that there will eventually be a call to either `updateConst` or `updateConstIncomplete`
57 /// following the `addConst` call, to actually populate the constant's debug info.
58 fn addConst(
59 di: DebugInfo,
60 pt: Zcu.PerThread,
61 index: Index,
62 val: InternPool.Index,
63 ) !void {
64 switch (di) {
65 inline else => |impl| return impl.addConst(pt, index, val),
66 }
67 }
68
69 /// Tell the debug info implementation to emit information for the constant `val`, which is in
70 /// the pool at the given index. `val` is "complete", which means:
71 /// * If it is a type, its layout is known.
72 /// * Otherwise, the layout of its type is known.
73 fn updateConst(
74 di: DebugInfo,
75 pt: Zcu.PerThread,
76 index: Index,
77 val: InternPool.Index,
78 ) !void {
79 switch (di) {
80 inline else => |impl| return impl.updateConst(pt, index, val),
81 }
82 }
83
84 /// Tell the debug info implementation to emit information for the constant `val`, which is in
85 /// the pool at the given index. `val` is "incomplete", meaning the implementation cannot emit
86 /// full information for it (for instance, perhaps it is a struct type which was never actually
87 /// initialized so never had its layout resolved). Instead, the implementation must emit some
88 /// form of placeholder entry representing an incomplete/unknown constant.
89 fn updateConstIncomplete(
90 di: DebugInfo,
91 pt: Zcu.PerThread,
92 index: Index,
93 val: InternPool.Index,
94 ) !void {
95 switch (di) {
96 inline else => |impl| return impl.updateConstIncomplete(pt, index, val),
97 }
98 }
99};
100
101const ContainerDepEntry = extern struct {
102 next: ContainerDepEntry.Index.Optional,
103 depender: DebugConstPool.Index,
104 const Index = enum(u32) {
105 _,
106 const Optional = enum(u32) {
107 none = std.math.maxInt(u32),
108 _,
109 fn unwrap(o: Optional) ?ContainerDepEntry.Index {
110 return switch (o) {
111 .none => null,
112 else => @enumFromInt(@intFromEnum(o)),
113 };
114 }
115 };
116 fn toOptional(i: ContainerDepEntry.Index) Optional {
117 return @enumFromInt(@intFromEnum(i));
118 }
119 fn ptr(i: ContainerDepEntry.Index, pool: *DebugConstPool) *ContainerDepEntry {
120 return &pool.container_dep_entries.items[@intFromEnum(i)];
121 }
122 };
123};
124
125/// Calls to `link.File.updateContainerType` must be forwarded to this function so that the debug
126/// constant pool has up-to-date information about the resolution status of types.
127pub fn updateContainerType(
128 pool: *DebugConstPool,
129 pt: Zcu.PerThread,
130 di: DebugInfo,
131 container_ty: InternPool.Index,
132 success: bool,
133) !void {
134 if (success) {
135 const gpa = pt.zcu.comp.gpa;
136 try pool.complete_containers.put(gpa, container_ty, {});
137 } else {
138 _ = pool.complete_containers.fetchSwapRemove(container_ty);
139 }
140 var opt_dep = pool.container_deps.get(container_ty);
141 while (opt_dep) |dep| : (opt_dep = dep.ptr(pool).next.unwrap()) {
142 try pool.update(pt, di, dep.ptr(pool).depender);
143 }
144}
145
146/// After this is called, there may be a constant for which debug information (complete or not) has
147/// not yet been emitted, so the user must call `flushPending` at some point after this call.
148pub fn get(pool: *DebugConstPool, pt: Zcu.PerThread, di: DebugInfo, val: InternPool.Index) !DebugConstPool.Index {
149 const zcu = pt.zcu;
150 const ip = &zcu.intern_pool;
151 const gpa = zcu.comp.gpa;
152 const gop = try pool.values.getOrPut(gpa, val);
153 const index: DebugConstPool.Index = @enumFromInt(gop.index);
154 if (!gop.found_existing) {
155 const ty: Type = switch (ip.typeOf(val)) {
156 .type_type => if (ip.isUndef(val)) .type else .fromInterned(val),
157 else => |ty| .fromInterned(ty),
158 };
159 try pool.registerTypeDeps(index, ty, zcu);
160 try pool.pending.append(gpa, index);
161 try di.addConst(pt, index, val);
162 }
163 return index;
164}
165pub fn flushPending(pool: *DebugConstPool, pt: Zcu.PerThread, di: DebugInfo) !void {
166 while (pool.pending.pop()) |pending_ty| {
167 try pool.update(pt, di, pending_ty);
168 }
169}
170
171fn update(pool: *DebugConstPool, pt: Zcu.PerThread, di: DebugInfo, index: DebugConstPool.Index) !void {
172 const zcu = pt.zcu;
173 const ip = &zcu.intern_pool;
174 const val = index.val(pool);
175 const ty: Type = switch (ip.typeOf(val)) {
176 .type_type => if (ip.isUndef(val)) .type else .fromInterned(val),
177 else => |ty| .fromInterned(ty),
178 };
179 if (pool.checkType(ty, zcu)) {
180 try di.updateConst(pt, index, val);
181 } else {
182 try di.updateConstIncomplete(pt, index, val);
183 }
184}
185fn checkType(pool: *const DebugConstPool, ty: Type, zcu: *const Zcu) bool {
186 if (ty.isGenericPoison()) return true;
187 return switch (ty.zigTypeTag(zcu)) {
188 .type,
189 .void,
190 .bool,
191 .noreturn,
192 .int,
193 .float,
194 .pointer,
195 .comptime_float,
196 .comptime_int,
197 .undefined,
198 .null,
199 .error_set,
200 .@"opaque",
201 .frame,
202 .@"anyframe",
203 .enum_literal,
204 => true,
205
206 .array, .vector => pool.checkType(ty.childType(zcu), zcu),
207 .optional => pool.checkType(ty.optionalChild(zcu), zcu),
208 .error_union => pool.checkType(ty.errorUnionPayload(zcu), zcu),
209 .@"fn" => {
210 const ip = &zcu.intern_pool;
211 const func = ip.indexToKey(ty.toIntern()).func_type;
212 for (func.param_types.get(ip)) |param_ty_ip| {
213 if (!pool.checkType(.fromInterned(param_ty_ip), zcu)) return false;
214 }
215 return pool.checkType(.fromInterned(func.return_type), zcu);
216 },
217 .@"struct" => if (ty.isTuple(zcu)) {
218 for (0..ty.structFieldCount(zcu)) |field_index| {
219 if (!pool.checkType(ty.fieldType(field_index, zcu), zcu)) return false;
220 }
221 return true;
222 } else {
223 return pool.complete_containers.contains(ty.toIntern());
224 },
225 .@"union", .@"enum" => {
226 return pool.complete_containers.contains(ty.toIntern());
227 },
228 };
229}
230fn registerTypeDeps(pool: *DebugConstPool, root: Index, ty: Type, zcu: *const Zcu) Allocator.Error!void {
231 if (ty.isGenericPoison()) return;
232 switch (ty.zigTypeTag(zcu)) {
233 .type,
234 .void,
235 .bool,
236 .noreturn,
237 .int,
238 .float,
239 .pointer,
240 .comptime_float,
241 .comptime_int,
242 .undefined,
243 .null,
244 .error_set,
245 .@"opaque",
246 .frame,
247 .@"anyframe",
248 .enum_literal,
249 => {},
250
251 .array, .vector => try pool.registerTypeDeps(root, ty.childType(zcu), zcu),
252 .optional => try pool.registerTypeDeps(root, ty.optionalChild(zcu), zcu),
253 .error_union => try pool.registerTypeDeps(root, ty.errorUnionPayload(zcu), zcu),
254 .@"fn" => {
255 const ip = &zcu.intern_pool;
256 const func = ip.indexToKey(ty.toIntern()).func_type;
257 for (func.param_types.get(ip)) |param_ty_ip| {
258 try pool.registerTypeDeps(root, .fromInterned(param_ty_ip), zcu);
259 }
260 try pool.registerTypeDeps(root, .fromInterned(func.return_type), zcu);
261 },
262 .@"struct", .@"union", .@"enum" => if (ty.isTuple(zcu)) {
263 for (0..ty.structFieldCount(zcu)) |field_index| {
264 try pool.registerTypeDeps(root, ty.fieldType(field_index, zcu), zcu);
265 }
266 } else {
267 // `ty` is a container; register the dependency.
268
269 const gpa = zcu.comp.gpa;
270 try pool.container_deps.ensureUnusedCapacity(gpa, 1);
271 try pool.container_dep_entries.ensureUnusedCapacity(gpa, 1);
272 errdefer comptime unreachable;
273
274 const gop = pool.container_deps.getOrPutAssumeCapacity(ty.toIntern());
275 const entry: ContainerDepEntry.Index = @enumFromInt(pool.container_dep_entries.items.len);
276 pool.container_dep_entries.appendAssumeCapacity(.{
277 .next = if (gop.found_existing) gop.value_ptr.toOptional() else .none,
278 .depender = root,
279 });
280 gop.value_ptr.* = entry;
281 },
282 }
283}
284
285const std = @import("std");
286const Allocator = std.mem.Allocator;
287
288const InternPool = @import("../InternPool.zig");
289const Type = @import("../Type.zig");
290const Zcu = @import("../Zcu.zig");
src/link/Dwarf.zig+27-10
......@@ -18,7 +18,6 @@ const codegen = @import("../codegen.zig");
1818const dev = @import("../dev.zig");
1919const link = @import("../link.zig");
2020const target_info = @import("../target.zig");
21const DebugConstPool = link.DebugConstPool;
2221
2322gpa: Allocator,
2423bin_file: *link.File,
......@@ -26,10 +25,10 @@ format: DW.Format,
2625endian: std.builtin.Endian,
2726address_size: AddressSize,
2827
29const_pool: DebugConstPool,
28const_pool: link.ConstPool,
3029
3130mods: std.AutoArrayHashMapUnmanaged(*Module, ModInfo),
32/// Indices are `DebugConstPool.Index`.
31/// Indices are `link.ConstPool.Index`.
3332values: std.ArrayList(struct { Unit.Index, Entry.Index }),
3433navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Entry.Index),
3534decls: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, Entry.Index),
......@@ -1038,7 +1037,7 @@ const Entry = struct {
10381037 const zcu = dwarf.bin_file.comp.zcu.?;
10391038 const ip = &zcu.intern_pool;
10401039 for (0.., dwarf.values.items) |raw_index, unit_and_entry| {
1041 const index: DebugConstPool.Index = @enumFromInt(raw_index);
1040 const index: link.ConstPool.Index = @enumFromInt(raw_index);
10421041 const val = index.val(&dwarf.const_pool);
10431042 const val_unit, const val_entry = unit_and_entry;
10441043 if (sec.getUnit(val_unit) == unit and unit.getEntry(val_entry) == entry)
......@@ -3291,8 +3290,14 @@ pub fn updateContainerType(
32913290) !void {
32923291 try dwarf.const_pool.updateContainerType(pt, .{ .dwarf = dwarf }, ty, success);
32933292}
3294/// Should only be called by the `DebugConstPool` implementation.
3295pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: DebugConstPool.Index, val: InternPool.Index) !void {
3293/// Should only be called by the `link.ConstPool` implementation.
3294pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void {
3295 addConstInner(dwarf, pt, index, val) catch |err| switch (err) {
3296 error.OutOfMemory => |e| return e,
3297 else => |e| std.debug.panic("DWARF TODO: '{t}' while registering constant\n", .{e}),
3298 };
3299}
3300fn addConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) !void {
32963301 const zcu = pt.zcu;
32973302 const ip = &zcu.intern_pool;
32983303
......@@ -3321,11 +3326,17 @@ pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: DebugConstPool.Index, v
33213326 assert(@intFromEnum(index) == dwarf.values.items.len);
33223327 try dwarf.values.append(dwarf.gpa, .{ unit, entry });
33233328}
3324/// Should only be called by the `DebugConstPool` implementation.
3329/// Should only be called by the `link.ConstPool` implementation.
33253330///
33263331/// Emits a "dummy" DIE for the given comptime-only value (which may be a type). For types, this is
33273332/// an opaque type. Otherwise, it is an undefined value of the value's type.
3328pub fn updateConstIncomplete(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: DebugConstPool.Index, value_index: InternPool.Index) !void {
3333pub fn updateConstIncomplete(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) Allocator.Error!void {
3334 updateConstIncompleteInner(dwarf, pt, debug_const_index, value_index) catch |err| switch (err) {
3335 error.OutOfMemory => |e| return e,
3336 else => |e| std.debug.panic("DWARF TODO: '{t}' while updating incomplete constant\n", .{e}),
3337 };
3338}
3339fn updateConstIncompleteInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) !void {
33293340 const zcu = pt.zcu;
33303341
33313342 const val: Value = .fromInterned(value_index);
......@@ -3380,10 +3391,16 @@ pub fn updateConstIncomplete(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index
33803391 try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written());
33813392 try dwarf.debug_loclists.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_loclists.written());
33823393}
3383/// Should only be called by the `DebugConstPool` implementation.
3394/// Should only be called by the `link.ConstPool` implementation.
33843395///
33853396/// Emits a DIE for the given comptime-only value (which may be a type).
3386pub fn updateConst(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: DebugConstPool.Index, value_index: InternPool.Index) !void {
3397pub fn updateConst(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) Allocator.Error!void {
3398 updateConstInner(dwarf, pt, debug_const_index, value_index) catch |err| switch (err) {
3399 error.OutOfMemory => |e| return e,
3400 else => |e| std.debug.panic("DWARF TODO: '{t}' while updating constant\n", .{e}),
3401 };
3402}
3403fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.ConstPool.Index, value_index: InternPool.Index) !void {
33873404 const zcu = pt.zcu;
33883405 const ip = &zcu.intern_pool;
33893406
src/link/Elf.zig-11
......@@ -1716,19 +1716,8 @@ pub fn updateContainerType(
17161716 if (build_options.skip_non_native and builtin.object_format != .elf) {
17171717 @panic("Attempted to compile for object format that was disabled by build configuration");
17181718 }
1719 const zcu = pt.zcu;
1720 const gpa = zcu.gpa;
17211719 return self.zigObjectPtr().?.updateContainerType(pt, ty, success) catch |err| switch (err) {
17221720 error.OutOfMemory => return error.OutOfMemory,
1723 else => |e| {
1724 try zcu.failed_types.putNoClobber(gpa, ty, try Zcu.ErrorMsg.create(
1725 gpa,
1726 zcu.typeSrcLoc(ty),
1727 "failed to update container type: {s}",
1728 .{@errorName(e)},
1729 ));
1730 return error.TypeFailureReported;
1731 },
17321721 };
17331722}
17341723