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 @@...@@ -259,7 +259,7 @@
259#endif259#endif
260260
261#if zig_has_attribute(packed) || defined(zig_tinyc)261#if zig_has_attribute(packed) || defined(zig_tinyc)
262#define zig_packed(definition) __attribute__((packed)) definition262#define zig_packed(definition) definition __attribute__((packed))
263#elif defined(zig_msvc)263#elif defined(zig_msvc)
264#define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack())264#define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack())
265#else265#else
src/Compilation.zig-3
...@@ -3382,9 +3382,6 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel...@@ -3382,9 +3382,6 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id) (Io.Cancel
3382 error.OutOfMemory, error.Canceled => |e| return e,3382 error.OutOfMemory, error.Canceled => |e| return e,
3383 };3383 };
3384 }3384 }
3385 if (comp.zcu) |zcu| {
3386 try link.File.C.flushEmitH(zcu);
3387 }
3388}3385}
33893386
3390/// This function is called by the frontend before flush(). It communicates that3387/// 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 {...@@ -3403,7 +3403,7 @@ pub const LoadedStructType = struct {
3403 /// Iterates over non-comptime fields in the order they are laid out in memory at runtime.3403 /// Iterates over non-comptime fields in the order they are laid out in memory at runtime.
3404 /// May or may not include zero-bit fields.3404 /// May or may not include zero-bit fields.
3405 /// Asserts the struct is not packed.3405 /// 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 {
3407 switch (s.layout) {3407 switch (s.layout) {
3408 .auto => {3408 .auto => {
3409 const ro = std.mem.sliceTo(s.field_runtime_order.get(ip), .omitted);3409 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 {...@@ -789,7 +789,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
789/// Determines whether a function type has runtime bits, i.e. whether a789/// Determines whether a function type has runtime bits, i.e. whether a
790/// function with this type can exist at runtime.790/// function with this type can exist at runtime.
791/// Asserts that `ty` is a function type.791/// 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 {
793 assertHasLayout(fn_ty, zcu);793 assertHasLayout(fn_ty, zcu);
794 const fn_info = zcu.typeToFunc(fn_ty).?;794 const fn_info = zcu.typeToFunc(fn_ty).?;
795 if (fn_info.comptime_bits != 0) return false;795 if (fn_info.comptime_bits != 0) return false;
...@@ -830,7 +830,7 @@ pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *Zcu) bool {...@@ -830,7 +830,7 @@ pub fn fnHasRuntimeBits(fn_ty: Type, zcu: *Zcu) bool {
830}830}
831831
832/// Like `hasRuntimeBits`, but also returns `true` for runtime functions.832/// 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 {
834 switch (ty.zigTypeTag(zcu)) {834 switch (ty.zigTypeTag(zcu)) {
835 .@"fn" => return ty.fnHasRuntimeBits(zcu),835 .@"fn" => return ty.fnHasRuntimeBits(zcu),
836 else => return ty.hasRuntimeBits(zcu),836 else => return ty.hasRuntimeBits(zcu),
src/Value.zig+4-4
...@@ -151,7 +151,7 @@ pub fn intFromEnum(val: Value, zcu: *const Zcu) Value {...@@ -151,7 +151,7 @@ pub fn intFromEnum(val: Value, zcu: *const Zcu) Value {
151}151}
152152
153/// Asserts that `val` is an integer.153/// 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 {
155 if (val.getUnsignedInt(zcu)) |x| {155 if (val.getUnsignedInt(zcu)) |x| {
156 return BigIntMutable.init(&space.limbs, x).toConst();156 return BigIntMutable.init(&space.limbs, x).toConst();
157 }157 }
...@@ -669,7 +669,7 @@ pub fn floatCast(val: Value, dest_ty: Type, pt: Zcu.PerThread) !Value {...@@ -669,7 +669,7 @@ pub fn floatCast(val: Value, dest_ty: Type, pt: Zcu.PerThread) !Value {
669}669}
670670
671/// Asserts the value is comparable. Supports comparisons between heterogeneous types.671/// 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 {
673 if (lhs.pointerNav(zcu)) |lhs_nav| {673 if (lhs.pointerNav(zcu)) |lhs_nav| {
674 if (rhs.pointerNav(zcu)) |rhs_nav| {674 if (rhs.pointerNav(zcu)) |rhs_nav| {
675 switch (op) {675 switch (op) {
...@@ -695,7 +695,7 @@ pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu:...@@ -695,7 +695,7 @@ pub fn compareHetero(lhs: Value, op: std.math.CompareOperator, rhs: Value, zcu:
695 return order(lhs, rhs, zcu).compare(op);695 return order(lhs, rhs, zcu).compare(op);
696}696}
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 {
699 if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) {699 if (lhs.isFloat(zcu) or rhs.isFloat(zcu)) {
700 const lhs_f128 = lhs.toFloat(f128, zcu);700 const lhs_f128 = lhs.toFloat(f128, zcu);
701 const rhs_f128 = rhs.toFloat(f128, zcu);701 const rhs_f128 = rhs.toFloat(f128, zcu);
...@@ -805,7 +805,7 @@ pub fn canMutateComptimeVarState(val: Value, zcu: *Zcu) bool {...@@ -805,7 +805,7 @@ pub fn canMutateComptimeVarState(val: Value, zcu: *Zcu) bool {
805/// Gets the `Nav` referenced by this pointer. If the pointer does not point805/// Gets the `Nav` referenced by this pointer. If the pointer does not point
806/// to a `Nav`, or if it points to some part of one (like a field or element),806/// to a `Nav`, or if it points to some part of one (like a field or element),
807/// returns null.807/// returns null.
808pub fn pointerNav(val: Value, zcu: *Zcu) ?InternPool.Nav.Index {808pub fn pointerNav(val: Value, zcu: *const Zcu) ?InternPool.Nav.Index {
809 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {809 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
810 // TODO: these 3 cases are weird; these aren't pointer values!810 // TODO: these 3 cases are weird; these aren't pointer values!
811 .variable => |v| v.owner_nav,811 .variable => |v| v.owner_nav,
src/Zcu.zig+2-2
...@@ -4113,13 +4113,13 @@ pub const ResolvedReference = struct {...@@ -4113,13 +4113,13 @@ pub const ResolvedReference = struct {
4113/// If an `AnalUnit` is not in the returned map, it is unreferenced.4113/// If an `AnalUnit` is not in the returned map, it is unreferenced.
4114/// The returned hashmap is owned by the `Zcu`, so should not be freed by the caller.4114/// The returned hashmap is owned by the `Zcu`, so should not be freed by the caller.
4115/// This hashmap is cached, so repeated calls to this function are cheap.4115/// 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) {
4117 if (zcu.resolved_references == null) {4117 if (zcu.resolved_references == null) {
4118 zcu.resolved_references = try zcu.resolveReferencesInner();4118 zcu.resolved_references = try zcu.resolveReferencesInner();
4119 }4119 }
4120 return &zcu.resolved_references.?;4120 return &zcu.resolved_references.?;
4121}4121}
4122fn resolveReferencesInner(zcu: *Zcu) !std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) {4122fn resolveReferencesInner(zcu: *Zcu) Allocator.Error!std.AutoArrayHashMapUnmanaged(AnalUnit, ?ResolvedReference) {
4123 const gpa = zcu.gpa;4123 const gpa = zcu.gpa;
4124 const comp = zcu.comp;4124 const comp = zcu.comp;
4125 const ip = &zcu.intern_pool;4125 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 {...@@ -50,32 +50,39 @@ pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
50/// * The types used, so declarations can be emitted in `flush`50/// * The types used, so declarations can be emitted in `flush`
51/// * The lazy functions used, so definitions can be emitted in `flush`51/// * The lazy functions used, so definitions can be emitted in `flush`
52pub const Mir = struct {52pub 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,
53 /// This map contains all the UAVs we saw generating this function.57 /// This map contains all the UAVs we saw generating this function.
54 /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields.58 /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields.
55 /// Key is the value of the UAV; value is the UAV's alignment, or59 /// Key is the value of the UAV; value is the UAV's alignment, or
56 /// `.none` for natural alignment. The specified alignment is never60 /// `.none` for natural alignment. The specified alignment is never
57 /// less than the natural alignment.61 /// less than the natural alignment.
58 uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),62 need_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
59 // These remaining fields are essentially just an owned version of `link.C.AvBlock`.63 ctype_deps: CType.Dependencies,
60 code_header: []u8,64 /// Key is an enum type for which we need a generated `@tagName` function.
61 code: []u8,65 need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
62 fwd_decl: []u8,66 /// Key is a function Nav for which we need a generated `zig_never_tail` wrapper.
63 ctype_pool: CType.Pool,67 need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
64 lazy_fns: LazyFnMap,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
66 pub fn deinit(mir: *Mir, gpa: Allocator) void {71 pub fn deinit(mir: *Mir, gpa: Allocator) void {
67 mir.uavs.deinit(gpa);72 gpa.free(mir.fwd_decl);
68 gpa.free(mir.code_header);73 gpa.free(mir.code_header);
69 gpa.free(mir.code);74 gpa.free(mir.code);
70 gpa.free(mir.fwd_decl);75 mir.need_uavs.deinit(gpa);
71 mir.ctype_pool.deinit(gpa);76 mir.ctype_deps.deinit(gpa);
72 mir.lazy_fns.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);
73 }80 }
74};81};
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
80pub const CValue = union(enum) {87pub const CValue = union(enum) {
81 none: void,88 none: void,
...@@ -87,8 +94,6 @@ pub const CValue = union(enum) {...@@ -87,8 +94,6 @@ pub const CValue = union(enum) {
87 constant: Value,94 constant: Value,
88 /// Index into the parameters95 /// Index into the parameters
89 arg: usize,96 arg: usize,
90 /// The array field of a parameter
91 arg_array: usize,
92 /// Index into a tuple's fields97 /// Index into a tuple's fields
93 field: usize,98 field: usize,
94 /// By-value99 /// By-value
...@@ -100,8 +105,6 @@ pub const CValue = union(enum) {...@@ -100,8 +105,6 @@ pub const CValue = union(enum) {
100 identifier: []const u8,105 identifier: []const u8,
101 /// Rendered as "payload." followed by as identifier (using fmtIdent)106 /// Rendered as "payload." followed by as identifier (using fmtIdent)
102 payload_identifier: []const u8,107 payload_identifier: []const u8,
103 /// Rendered with fmtCTypePoolString
104 ctype_pool_string: CType.Pool.String,
105108
106 fn eql(lhs: CValue, rhs: CValue) bool {109 fn eql(lhs: CValue, rhs: CValue) bool {
107 return switch (lhs) {110 return switch (lhs) {
...@@ -122,10 +125,6 @@ pub const CValue = union(enum) {...@@ -122,10 +125,6 @@ pub const CValue = union(enum) {
122 .arg => |rhs_arg_index| lhs_arg_index == rhs_arg_index,125 .arg => |rhs_arg_index| lhs_arg_index == rhs_arg_index,
123 else => false,126 else => false,
124 },127 },
125 .arg_array => |lhs_arg_index| switch (rhs) {
126 .arg_array => |rhs_arg_index| lhs_arg_index == rhs_arg_index,
127 else => false,
128 },
129 .field => |lhs_field_index| switch (rhs) {128 .field => |lhs_field_index| switch (rhs) {
130 .field => |rhs_field_index| lhs_field_index == rhs_field_index,129 .field => |rhs_field_index| lhs_field_index == rhs_field_index,
131 else => false,130 else => false,
...@@ -150,10 +149,6 @@ pub const CValue = union(enum) {...@@ -150,10 +149,6 @@ pub const CValue = union(enum) {
150 .payload_identifier => |rhs_id| std.mem.eql(u8, lhs_id, rhs_id),149 .payload_identifier => |rhs_id| std.mem.eql(u8, lhs_id, rhs_id),
151 else => false,150 else => false,
152 },151 },
153 .ctype_pool_string => |lhs_str| switch (rhs) {
154 .ctype_pool_string => |rhs_str| lhs_str.index == rhs_str.index,
155 else => false,
156 },
157 };152 };
158 }153 }
159};154};
...@@ -163,53 +158,24 @@ const BlockData = struct {...@@ -163,53 +158,24 @@ const BlockData = struct {
163 result: CValue,158 result: CValue,
164};159};
165160
166pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue);161const LocalType = struct {
167162 type: Type,
168pub const LazyFnKey = union(enum) {163 alignment: Alignment,
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 }
188};164};
189165
190const LocalIndex = u16;166const LocalIndex = u16;
191const LocalType = struct { ctype: CType, alignas: CType.AlignAs };
192const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void);167const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void);
193const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList);168const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList);
194169
195const ValueRenderLocation = enum {170const ValueRenderLocation = enum {
196 FunctionArgument,171 initializer,
197 Initializer,172 static_initializer,
198 StaticInitializer,173 other,
199 Other,
200174
201 fn isInitializer(loc: ValueRenderLocation) bool {175 fn isInitializer(loc: ValueRenderLocation) bool {
202 return switch (loc) {176 return switch (loc) {
203 .Initializer, .StaticInitializer => true,177 .initializer, .static_initializer => true,
204 else => false,178 .other => 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,
213 };179 };
214 }180 }
215};181};
...@@ -334,16 +300,31 @@ const reserved_idents = std.StaticStringMap(void).initComptime(.{...@@ -334,16 +300,31 @@ const reserved_idents = std.StaticStringMap(void).initComptime(.{
334});300});
335301
336fn isReservedIdent(ident: []const u8) bool {302fn isReservedIdent(ident: []const u8) bool {
337 if (ident.len >= 2 and ident[0] == '_') { // C language303 // C language
304 if (ident.len >= 2 and ident[0] == '_') {
338 switch (ident[1]) {305 switch (ident[1]) {
339 'A'...'Z', '_' => return true,306 'A'...'Z', '_' => return true,
340 else => return false,307 else => {},
341 }308 }
342 } else if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or309 }
310
311 // windows.h
312 if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or
343 mem.startsWith(u8, ident, "DUMMYUNIONNAME"))313 mem.startsWith(u8, ident, "DUMMYUNIONNAME"))
344 { // windows.h314 {
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 {
345 return true;324 return true;
346 } else return reserved_idents.has(ident);325 }
326
327 return reserved_idents.has(ident);
347}328}
348329
349fn formatIdentSolo(ident: []const u8, w: *Writer) Writer.Error!void {330fn 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...@@ -361,7 +342,7 @@ fn formatIdentOptions(ident: []const u8, w: *Writer, solo: bool) Writer.Error!vo
361 for (ident, 0..) |c, i| {342 for (ident, 0..) |c, i| {
362 switch (c) {343 switch (c) {
363 'a'...'z', 'A'...'Z', '_' => try w.writeByte(c),344 'a'...'z', 'A'...'Z', '_' => try w.writeByte(c),
364 '.' => try w.writeByte('_'),345 '.', ' ' => try w.writeByte('_'),
365 '0'...'9' => if (i == 0) {346 '0'...'9' => if (i == 0) {
366 try w.print("_{x:2}", .{c});347 try w.print("_{x:2}", .{c});
367 } else {348 } else {
...@@ -380,29 +361,6 @@ pub fn fmtIdentUnsolo(ident: []const u8) std.fmt.Alt([]const u8, formatIdentUnso...@@ -380,29 +361,6 @@ pub fn fmtIdentUnsolo(ident: []const u8) std.fmt.Alt([]const u8, formatIdentUnso
380 return .{ .data = ident };361 return .{ .data = ident };
381}362}
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
406// Returns true if `formatIdent` would make any edits to ident.364// Returns true if `formatIdent` would make any edits to ident.
407// This must be kept in sync with `formatIdent`.365// This must be kept in sync with `formatIdent`.
408pub fn isMangledIdent(ident: []const u8, solo: bool) bool {366pub fn isMangledIdent(ident: []const u8, solo: bool) bool {
...@@ -417,21 +375,26 @@ pub fn isMangledIdent(ident: []const u8, solo: bool) bool {...@@ -417,21 +375,26 @@ pub fn isMangledIdent(ident: []const u8, solo: bool) bool {
417 return false;375 return false;
418}376}
419377
420/// This data is available when outputting .c code for a `InternPool.Index`378/// This data is available when rendering C source code for an interned function.
421/// that corresponds to `func`.
422/// It is not available when generating .h file.
423pub const Function = struct {379pub const Function = struct {
424 air: Air,380 air: Air,
425 liveness: Air.Liveness,381 liveness: Air.Liveness,
426 value_map: CValueMap,382 value_map: std.AutoHashMap(Air.Inst.Ref, CValue),
427 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,383 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,
428 next_arg_index: u32 = 0,384 next_arg_index: u32 = 0,
429 next_block_index: u32 = 0,385 next_block_index: u32 = 0,
430 object: Object,386 dg: DeclGen,
431 lazy_fns: LazyFnMap,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),
432 func_index: InternPool.Index,395 func_index: InternPool.Index,
433 /// All the locals, to be emitted at the top of the function.396 /// All the locals, to be emitted at the top of the function.
434 locals: std.ArrayList(Local) = .empty,397 locals: std.ArrayList(LocalType) = .empty,
435 /// Which locals are available for reuse, based on Type.398 /// Which locals are available for reuse, based on Type.
436 free_locals_map: LocalsMap = .{},399 free_locals_map: LocalsMap = .{},
437 /// Locals which will not be freed by Liveness. This is used after a400 /// Locals which will not be freed by Liveness. This is used after a
...@@ -445,37 +408,41 @@ pub const Function = struct {...@@ -445,37 +408,41 @@ pub const Function = struct {
445 /// for the switch cond. Dispatches should set this local to the new cond.408 /// for the switch cond. Dispatches should set this local to the new cond.
446 loop_switch_conds: std.AutoHashMapUnmanaged(Air.Inst.Index, LocalIndex) = .empty,409 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
448 fn resolveInst(f: *Function, ref: Air.Inst.Ref) !CValue {435 fn resolveInst(f: *Function, ref: Air.Inst.Ref) !CValue {
449 const gop = try f.value_map.getOrPut(ref);436 const gop = try f.value_map.getOrPut(ref);
450 if (gop.found_existing) return gop.value_ptr.*;437 if (!gop.found_existing) {
451438 const val = try f.air.value(ref, f.dg.pt);
452 const pt = f.object.dg.pt;439 gop.value_ptr.* = .{ .constant = val.? };
453 const zcu = pt.zcu;440 }
454 const val = (try f.air.value(ref, pt)).?;441 return gop.value_ptr.*;
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;
475 }442 }
476443
477 fn wantSafety(f: *Function) bool {444 fn wantSafety(f: *Function) bool {
478 return switch (f.object.dg.pt.zcu.optimizeMode()) {445 return switch (f.dg.pt.zcu.optimizeMode()) {
479 .Debug, .ReleaseSafe => true,446 .Debug, .ReleaseSafe => true,
480 .ReleaseFast, .ReleaseSmall => false,447 .ReleaseFast, .ReleaseSmall => false,
481 };448 };
...@@ -485,18 +452,16 @@ pub const Function = struct {...@@ -485,18 +452,16 @@ pub const Function = struct {
485 /// those which go into `allocs`. This function does not add the resulting local into `allocs`;452 /// those which go into `allocs`. This function does not add the resulting local into `allocs`;
486 /// that responsibility lies with the caller.453 /// that responsibility lies with the caller.
487 fn allocLocalValue(f: *Function, local_type: LocalType) !CValue {454 fn allocLocalValue(f: *Function, local_type: LocalType) !CValue {
488 try f.locals.ensureUnusedCapacity(f.object.dg.gpa, 1);455 try f.locals.ensureUnusedCapacity(f.dg.gpa, 1);
489 defer f.locals.appendAssumeCapacity(.{456 const index = f.locals.items.len;
490 .ctype = local_type.ctype,457 f.locals.appendAssumeCapacity(local_type);
491 .flags = .{ .alignas = local_type.alignas },458 return .{ .new_local = @intCast(index) };
492 });
493 return .{ .new_local = @intCast(f.locals.items.len) };
494 }459 }
495460
496 fn allocLocal(f: *Function, inst: ?Air.Inst.Index, ty: Type) !CValue {461 fn allocLocal(f: *Function, inst: ?Air.Inst.Index, ty: Type) !CValue {
497 return f.allocAlignedLocal(inst, .{462 return f.allocAlignedLocal(inst, .{
498 .ctype = try f.ctypeFromType(ty, .complete),463 .type = ty,
499 .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(f.object.dg.pt.zcu)),464 .alignment = .none,
500 });465 });
501 }466 }
502467
...@@ -524,11 +489,10 @@ pub const Function = struct {...@@ -524,11 +489,10 @@ pub const Function = struct {
524 .none => unreachable,489 .none => unreachable,
525 .new_local, .local => |i| try w.print("t{d}", .{i}),490 .new_local, .local => |i| try w.print("t{d}", .{i}),
526 .local_ref => |i| try w.print("&t{d}", .{i}),491 .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),
528 .arg => |i| try w.print("a{d}", .{i}),493 .arg => |i| try w.print("a{d}", .{i}),
529 .arg_array => |i| try f.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }),494 .undef => |ty| try f.dg.renderUndefValue(w, ty, location),
530 .undef => |ty| try f.object.dg.renderUndefValue(w, ty, location),495 else => try f.dg.writeCValue(w, c_value),
531 else => try f.object.dg.writeCValue(w, c_value),
532 }496 }
533 }497 }
534498
...@@ -537,17 +501,12 @@ pub const Function = struct {...@@ -537,17 +501,12 @@ pub const Function = struct {
537 .none => unreachable,501 .none => unreachable,
538 .new_local, .local, .constant => {502 .new_local, .local, .constant => {
539 try w.writeAll("(*");503 try w.writeAll("(*");
540 try f.writeCValue(w, c_value, .Other);504 try f.writeCValue(w, c_value, .other);
541 try w.writeByte(')');505 try w.writeByte(')');
542 },506 },
543 .local_ref => |i| try w.print("t{d}", .{i}),507 .local_ref => |i| try w.print("t{d}", .{i}),
544 .arg => |i| try w.print("(*a{d})", .{i}),508 .arg => |i| try w.print("(*a{d})", .{i}),
545 .arg_array => |i| {509 else => try f.dg.writeCValueDeref(w, c_value),
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),
551 }510 }
552 }511 }
553512
...@@ -558,119 +517,77 @@ pub const Function = struct {...@@ -558,119 +517,77 @@ pub const Function = struct {
558 member: CValue,517 member: CValue,
559 ) Error!void {518 ) Error!void {
560 switch (c_value) {519 switch (c_value) {
561 .new_local, .local, .local_ref, .constant, .arg, .arg_array => {520 .new_local, .local, .local_ref, .constant, .arg => {
562 try f.writeCValue(w, c_value, .Other);521 try f.writeCValue(w, c_value, .other);
563 try w.writeByte('.');522 try w.writeByte('.');
564 try f.writeCValue(w, member, .Other);523 try f.writeCValue(w, member, .other);
565 },524 },
566 else => return f.object.dg.writeCValueMember(w, c_value, member),525 else => return f.dg.writeCValueMember(w, c_value, member),
567 }526 }
568 }527 }
569528
570 fn writeCValueDerefMember(f: *Function, w: *Writer, c_value: CValue, member: CValue) !void {529 fn writeCValueDerefMember(f: *Function, w: *Writer, c_value: CValue, member: CValue) !void {
571 switch (c_value) {530 switch (c_value) {
572 .new_local, .local, .arg, .arg_array => {531 .new_local, .local, .arg => {
573 try f.writeCValue(w, c_value, .Other);532 try f.writeCValue(w, c_value, .other);
574 try w.writeAll("->");533 try w.writeAll("->");
575 },534 },
576 .constant => {535 .constant => {
577 try w.writeByte('(');536 try w.writeByte('(');
578 try f.writeCValue(w, c_value, .Other);537 try f.writeCValue(w, c_value, .other);
579 try w.writeAll(")->");538 try w.writeAll(")->");
580 },539 },
581 .local_ref => {540 .local_ref => {
582 try f.writeCValueDeref(w, c_value);541 try f.writeCValueDeref(w, c_value);
583 try w.writeByte('.');542 try w.writeByte('.');
584 },543 },
585 else => return f.object.dg.writeCValueDerefMember(w, c_value, member),544 else => return f.dg.writeCValueDerefMember(w, c_value, member),
586 }545 }
587 try f.writeCValue(w, member, .Other);546 try f.writeCValue(w, member, .other);
588 }547 }
589548
590 fn fail(f: *Function, comptime format: []const u8, args: anytype) Error {549 fn fail(f: *Function, comptime format: []const u8, args: anytype) Error {
591 return f.object.dg.fail(format, args);550 return f.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);
604 }551 }
605552
606 fn renderCType(f: *Function, w: *Writer, ctype: CType) !void {553 fn renderType(f: *Function, w: *Writer, ty: Type) !void {
607 return f.object.dg.renderCType(w, ctype);554 return f.dg.renderType(w, ty);
608 }555 }
609556
610 fn renderIntCast(f: *Function, w: *Writer, dest_ty: Type, src: CValue, v: Vectorize, src_ty: Type, location: ValueRenderLocation) !void {557 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);
612 }559 }
613560
614 fn fmtIntLiteralDec(f: *Function, val: Value) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) {561 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);
616 }563 }
617564
618 fn fmtIntLiteralHex(f: *Function, val: Value) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) {565 fn fmtIntLiteralHex(f: *Function, val: Value) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) {
619 return f.object.dg.fmtIntLiteralHex(val, .Other);566 return f.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).?;
652 }567 }
653568
654 pub fn deinit(f: *Function) void {569 pub fn deinit(f: *Function) void {
655 const gpa = f.object.dg.gpa;570 const gpa = f.dg.gpa;
656 f.allocs.deinit(gpa);571 f.allocs.deinit(gpa);
657 f.locals.deinit(gpa);572 f.locals.deinit(gpa);
658 deinitFreeLocalsMap(gpa, &f.free_locals_map);573 deinitFreeLocalsMap(gpa, &f.free_locals_map);
659 f.blocks.deinit(gpa);574 f.blocks.deinit(gpa);
660 f.value_map.deinit();575 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);
662 f.loop_switch_conds.deinit(gpa);579 f.loop_switch_conds.deinit(gpa);
663 }580 }
664581
665 fn typeOf(f: *Function, inst: Air.Inst.Ref) Type {582 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);
667 }584 }
668585
669 fn typeOfIndex(f: *Function, inst: Air.Inst.Index) Type {586 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);
671 }588 }
672589
673 fn copyCValue(f: *Function, ctype: CType, dst: CValue, src: CValue) !void {590 fn copyCValue(f: *Function, dst: CValue, src: CValue) !void {
674 switch (dst) {591 switch (dst) {
675 .new_local, .local => |dst_local_index| switch (src) {592 .new_local, .local => |dst_local_index| switch (src) {
676 .new_local, .local => |src_local_index| if (dst_local_index == src_local_index) return,593 .new_local, .local => |src_local_index| if (dst_local_index == src_local_index) return,
...@@ -678,12 +595,12 @@ pub const Function = struct {...@@ -678,12 +595,12 @@ pub const Function = struct {
678 },595 },
679 else => {},596 else => {},
680 }597 }
681 const w = &f.object.code.writer;598 const w = &f.code.writer;
682 const a = try Assignment.start(f, w, ctype);599 try f.writeCValue(w, dst, .other);
683 try f.writeCValue(w, dst, .Other);600 try w.writeAll(" = ");
684 try a.assign(f, w);601 try f.writeCValue(w, src, .other);
685 try f.writeCValue(w, src, .Other);602 try w.writeByte(';');
686 try a.end(f, w);603 try f.newline();
687 }604 }
688605
689 fn moveCValue(f: *Function, inst: Air.Inst.Index, ty: Type, src: CValue) !CValue {606 fn moveCValue(f: *Function, inst: Air.Inst.Index, ty: Type, src: CValue) !CValue {
...@@ -694,7 +611,7 @@ pub const Function = struct {...@@ -694,7 +611,7 @@ pub const Function = struct {
694 else => {611 else => {
695 try freeCValue(f, inst, src);612 try freeCValue(f, inst, src);
696 const dst = try f.allocLocal(inst, ty);613 const dst = try f.allocLocal(inst, ty);
697 try f.copyCValue(try f.ctypeFromType(ty, .complete), dst, src);614 try f.copyCValue(dst, src);
698 return dst;615 return dst;
699 },616 },
700 }617 }
...@@ -708,51 +625,17 @@ pub const Function = struct {...@@ -708,51 +625,17 @@ pub const Function = struct {
708 }625 }
709};626};
710627
711/// This data is available when outputting .c code for a `Zcu`.628/// This data is available when rendering *any* C source code (function or otherwise).
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.
745pub const DeclGen = struct {629pub const DeclGen = struct {
746 gpa: Allocator,630 gpa: Allocator,
631 arena: Allocator,
747 pt: Zcu.PerThread,632 pt: Zcu.PerThread,
748 mod: *Module,633 mod: *Module,
749 pass: Pass,634 owner_nav: InternPool.Nav.Index.Optional,
750 is_naked_fn: bool,635 is_naked_fn: bool,
751 expected_block: ?u32,636 expected_block: ?u32,
752 fwd_decl: Writer.Allocating,
753 error_msg: ?*Zcu.ErrorMsg,637 error_msg: ?*Zcu.ErrorMsg,
754 ctype_pool: CType.Pool,638 ctype_deps: CType.Dependencies,
755 scratch: std.ArrayList(u32),
756 /// This map contains all the UAVs we saw generating this function.639 /// This map contains all the UAVs we saw generating this function.
757 /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields.640 /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields.
758 /// Key is the value of the UAV; value is the UAV's alignment, or641 /// Key is the value of the UAV; value is the UAV's alignment, or
...@@ -760,16 +643,10 @@ pub const DeclGen = struct {...@@ -760,16 +643,10 @@ pub const DeclGen = struct {
760 /// less than the natural alignment.643 /// less than the natural alignment.
761 uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),644 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
769 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) Error {646 fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) Error {
770 @branchHint(.cold);647 @branchHint(.cold);
771 const zcu = dg.pt.zcu;648 const zcu = dg.pt.zcu;
772 const src_loc = zcu.navSrcLoc(dg.pass.nav);649 const src_loc = zcu.navSrcLoc(dg.owner_nav.unwrap().?);
773 dg.error_msg = try Zcu.ErrorMsg.create(dg.gpa, src_loc, format, args);650 dg.error_msg = try Zcu.ErrorMsg.create(dg.gpa, src_loc, format, args);
774 return error.AnalysisFail;651 return error.AnalysisFail;
775 }652 }
...@@ -783,14 +660,13 @@ pub const DeclGen = struct {...@@ -783,14 +660,13 @@ pub const DeclGen = struct {
783 const pt = dg.pt;660 const pt = dg.pt;
784 const zcu = pt.zcu;661 const zcu = pt.zcu;
785 const ip = &zcu.intern_pool;662 const ip = &zcu.intern_pool;
786 const ctype_pool = &dg.ctype_pool;
787 const uav_val = Value.fromInterned(uav.val);663 const uav_val = Value.fromInterned(uav.val);
788 const uav_ty = uav_val.typeOf(zcu);664 const uav_ty = uav_val.typeOf(zcu);
789665
790 // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.666 // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
791 const ptr_ty: Type = .fromInterned(uav.orig_ty);667 const ptr_ty: Type = .fromInterned(uav.orig_ty);
792 if (ptr_ty.isPtrAtRuntime(zcu) and !uav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) {668 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);
794 }670 }
795671
796 // Chase function values in order to be able to reference the original function.672 // Chase function values in order to be able to reference the original function.
...@@ -805,14 +681,12 @@ pub const DeclGen = struct {...@@ -805,14 +681,12 @@ pub const DeclGen = struct {
805 // them). The analysis until now should ensure that the C function681 // them). The analysis until now should ensure that the C function
806 // pointers are compatible. If they are not, then there is a bug682 // pointers are compatible. If they are not, then there is a bug
807 // somewhere and we should let the C compiler tell us about it.683 // somewhere and we should let the C compiler tell us about it.
808 const ptr_ctype = try dg.ctypeFromType(ptr_ty, .complete);684 const elem_ty = ptr_ty.childType(zcu);
809 const elem_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;685 const need_cast = elem_ty.toIntern() != uav_ty.toIntern() and
810 const uav_ctype = try dg.ctypeFromType(uav_ty, .complete);686 elem_ty.zigTypeTag(zcu) != .@"fn" or uav_ty.zigTypeTag(zcu) != .@"fn";
811 const need_cast = !elem_ctype.eql(uav_ctype) and
812 (elem_ctype.info(ctype_pool) != .function or uav_ctype.info(ctype_pool) != .function);
813 if (need_cast) {687 if (need_cast) {
814 try w.writeAll("((");688 try w.writeAll("((");
815 try dg.renderCType(w, ptr_ctype);689 try dg.renderType(w, ptr_ty);
816 try w.writeByte(')');690 try w.writeByte(')');
817 }691 }
818 try w.writeByte('&');692 try w.writeByte('&');
...@@ -842,11 +716,9 @@ pub const DeclGen = struct {...@@ -842,11 +716,9 @@ pub const DeclGen = struct {
842 nav_index: InternPool.Nav.Index,716 nav_index: InternPool.Nav.Index,
843 location: ValueRenderLocation,717 location: ValueRenderLocation,
844 ) Error!void {718 ) Error!void {
845 _ = location;
846 const pt = dg.pt;719 const pt = dg.pt;
847 const zcu = pt.zcu;720 const zcu = pt.zcu;
848 const ip = &zcu.intern_pool;721 const ip = &zcu.intern_pool;
849 const ctype_pool = &dg.ctype_pool;
850722
851 // Chase function values in order to be able to reference the original function.723 // Chase function values in order to be able to reference the original function.
852 const owner_nav = switch (ip.getNav(nav_index).status) {724 const owner_nav = switch (ip.getNav(nav_index).status) {
...@@ -863,25 +735,23 @@ pub const DeclGen = struct {...@@ -863,25 +735,23 @@ pub const DeclGen = struct {
863 const nav_ty: Type = .fromInterned(ip.getNav(owner_nav).typeOf(ip));735 const nav_ty: Type = .fromInterned(ip.getNav(owner_nav).typeOf(ip));
864 const ptr_ty = try pt.navPtrType(owner_nav);736 const ptr_ty = try pt.navPtrType(owner_nav);
865 if (!nav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) {737 if (!nav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) {
866 return dg.writeCValue(w, .{ .undef = ptr_ty });738 return dg.renderUndefValue(w, ptr_ty, location);
867 }739 }
868740
869 // We shouldn't cast C function pointers as this is UB (when you call741 // We shouldn't cast C function pointers as this is UB (when you call
870 // them). The analysis until now should ensure that the C function742 // them). The analysis until now should ensure that the C function
871 // pointers are compatible. If they are not, then there is a bug743 // pointers are compatible. If they are not, then there is a bug
872 // somewhere and we should let the C compiler tell us about it.744 // somewhere and we should let the C compiler tell us about it.
873 const ctype = try dg.ctypeFromType(ptr_ty, .complete);745 const elem_ty = ptr_ty.childType(zcu);
874 const elem_ctype = ctype.info(ctype_pool).pointer.elem_ctype;746 const need_cast = elem_ty.toIntern() != nav_ty.toIntern() and
875 const nav_ctype = try dg.ctypeFromType(nav_ty, .complete);747 elem_ty.zigTypeTag(zcu) != .@"fn" or nav_ty.zigTypeTag(zcu) != .@"fn";
876 const need_cast = !elem_ctype.eql(nav_ctype) and
877 (elem_ctype.info(ctype_pool) != .function or nav_ctype.info(ctype_pool) != .function);
878 if (need_cast) {748 if (need_cast) {
879 try w.writeAll("((");749 try w.writeAll("((");
880 try dg.renderCType(w, ctype);750 try dg.renderType(w, ptr_ty);
881 try w.writeByte(')');751 try w.writeByte(')');
882 }752 }
883 try w.writeByte('&');753 try w.writeByte('&');
884 try dg.renderNavName(w, owner_nav);754 try renderNavName(w, owner_nav, ip);
885 if (need_cast) try w.writeByte(')');755 if (need_cast) try w.writeByte(')');
886 }756 }
887757
...@@ -896,11 +766,10 @@ pub const DeclGen = struct {...@@ -896,11 +766,10 @@ pub const DeclGen = struct {
896 switch (derivation) {766 switch (derivation) {
897 .comptime_alloc_ptr, .comptime_field_ptr => unreachable,767 .comptime_alloc_ptr, .comptime_field_ptr => unreachable,
898 .int => |int| {768 .int => |int| {
899 const ptr_ctype = try dg.ctypeFromType(int.ptr_ty, .complete);
900 const addr_val = try pt.intValue(.usize, int.addr);769 const addr_val = try pt.intValue(.usize, int.addr);
901 try w.writeByte('(');770 try w.writeByte('(');
902 try dg.renderCType(w, ptr_ctype);771 try dg.renderType(w, int.ptr_ty);
903 try w.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .Other)});772 try w.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .other)});
904 },773 },
905774
906 .nav_ptr => |nav| try dg.renderNav(w, nav, location),775 .nav_ptr => |nav| try dg.renderNav(w, nav, location),
...@@ -915,14 +784,10 @@ pub const DeclGen = struct {...@@ -915,14 +784,10 @@ pub const DeclGen = struct {
915 .field_ptr => |field| {784 .field_ptr => |field| {
916 const parent_ptr_ty = try field.parent.ptrType(pt);785 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
921 switch (fieldLocation(parent_ptr_ty, field.result_ptr_ty, field.field_idx, zcu)) {787 switch (fieldLocation(parent_ptr_ty, field.result_ptr_ty, field.field_idx, zcu)) {
922 .begin => {788 .begin => {
923 const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete);
924 try w.writeByte('(');789 try w.writeByte('(');
925 try dg.renderCType(w, ptr_ctype);790 try dg.renderType(w, field.result_ptr_ty);
926 try w.writeByte(')');791 try w.writeByte(')');
927 try dg.renderPointer(w, field.parent.*, location);792 try dg.renderPointer(w, field.parent.*, location);
928 },793 },
...@@ -933,51 +798,40 @@ pub const DeclGen = struct {...@@ -933,51 +798,40 @@ pub const DeclGen = struct {
933 try dg.writeCValue(w, name);798 try dg.writeCValue(w, name);
934 },799 },
935 .byte_offset => |byte_offset| {800 .byte_offset => |byte_offset| {
936 const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete);
937 try w.writeByte('(');801 try w.writeByte('(');
938 try dg.renderCType(w, ptr_ctype);802 try dg.renderType(w, field.result_ptr_ty);
939 try w.writeByte(')');803 try w.writeByte(')');
940 const offset_val = try pt.intValue(.usize, byte_offset);804 const offset_val = try pt.intValue(.usize, byte_offset);
941 try w.writeAll("((char *)");805 try w.writeAll("((char *)");
942 try dg.renderPointer(w, field.parent.*, location);806 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)});
944 },808 },
945 }809 }
946 },810 },
947811
948 .elem_ptr => |elem| if (!(try elem.parent.ptrType(pt)).childType(zcu).hasRuntimeBits(zcu)) {812 .elem_ptr => |elem| if (!(try elem.parent.ptrType(pt)).childType(zcu).hasRuntimeBits(zcu)) {
949 // Element type is zero-bit, so lowers to `void`. The index is irrelevant; just cast the pointer.813 // 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);
951 try w.writeByte('(');814 try w.writeByte('(');
952 try dg.renderCType(w, ptr_ctype);815 try dg.renderType(w, elem.result_ptr_ty);
953 try w.writeByte(')');816 try w.writeByte(')');
954 try dg.renderPointer(w, elem.parent.*, location);817 try dg.renderPointer(w, elem.parent.*, location);
955 } else {818 } else {
956 const index_val = try pt.intValue(.usize, elem.elem_idx);819 const index_val = try pt.intValue(.usize, elem.elem_idx);
957 // We want to do pointer arithmetic on a pointer to the element type.820 try w.writeByte('(');
958 // We might have a pointer-to-array. In this case, we must cast first.821 // We want to do pointer arithmetic on a pointer to the element type, but the parent
959 const result_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete);822 // might be a pointer-to-array, in which case we must cast it.
960 const parent_ctype = try dg.ctypeFromType(try elem.parent.ptrType(pt), .complete);823 if (elem.result_ptr_ty.toIntern() != (try elem.parent.ptrType(pt)).toIntern()) {
961 if (result_ctype.eql(parent_ctype)) {
962 // The pointer already has an appropriate type - just do the arithmetic.
963 try w.writeByte('(');824 try w.writeByte('(');
964 try dg.renderPointer(w, elem.parent.*, location);825 try dg.renderType(w, elem.result_ptr_ty);
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);
971 try w.writeByte(')');826 try w.writeByte(')');
972 try dg.renderPointer(w, elem.parent.*, location);
973 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)});
974 }827 }
828 try dg.renderPointer(w, elem.parent.*, location);
829 try w.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .other)});
975 },830 },
976831
977 .offset_and_cast => |oac| {832 .offset_and_cast => |oac| {
978 const ptr_ctype = try dg.ctypeFromType(oac.new_ptr_ty, .complete);
979 try w.writeByte('(');833 try w.writeByte('(');
980 try dg.renderCType(w, ptr_ctype);834 try dg.renderType(w, oac.new_ptr_ty);
981 try w.writeByte(')');835 try w.writeByte(')');
982 if (oac.byte_offset == 0) {836 if (oac.byte_offset == 0) {
983 try dg.renderPointer(w, oac.parent.*, location);837 try dg.renderPointer(w, oac.parent.*, location);
...@@ -985,14 +839,40 @@ pub const DeclGen = struct {...@@ -985,14 +839,40 @@ pub const DeclGen = struct {
985 const offset_val = try pt.intValue(.usize, oac.byte_offset);839 const offset_val = try pt.intValue(.usize, oac.byte_offset);
986 try w.writeAll("((char *)");840 try w.writeAll("((char *)");
987 try dg.renderPointer(w, oac.parent.*, location);841 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)});
989 }843 }
990 },844 },
991 }845 }
992 }846 }
993847
994 fn renderErrorName(dg: *DeclGen, w: *Writer, err_name: InternPool.NullTerminatedString) !void {848 fn renderValueAsLvalue(
995 try w.print("zig_error_{f}", .{fmtIdentUnsolo(err_name.toSlice(&dg.pt.zcu.intern_pool))});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);
996 }876 }
997877
998 fn renderValue(878 fn renderValue(
...@@ -1005,16 +885,13 @@ pub const DeclGen = struct {...@@ -1005,16 +885,13 @@ pub const DeclGen = struct {
1005 const zcu = pt.zcu;885 const zcu = pt.zcu;
1006 const ip = &zcu.intern_pool;886 const ip = &zcu.intern_pool;
1007 const target = &dg.mod.resolved_target.result;887 const target = &dg.mod.resolved_target.result;
1008 const ctype_pool = &dg.ctype_pool;
1009888
1010 const initializer_type: ValueRenderLocation = switch (location) {889 const initializer_type: ValueRenderLocation = switch (location) {
1011 .StaticInitializer => .StaticInitializer,890 .static_initializer => .static_initializer,
1012 else => .Initializer,891 else => .initializer,
1013 };892 };
1014893
1015 const ty = val.typeOf(zcu);894 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());
1018 switch (ip.indexToKey(val.toIntern())) {895 switch (ip.indexToKey(val.toIntern())) {
1019 // types, not values896 // types, not values
1020 .int_type,897 .int_type,
...@@ -1037,7 +914,7 @@ pub const DeclGen = struct {...@@ -1037,7 +914,7 @@ pub const DeclGen = struct {
1037 .memoized_call,914 .memoized_call,
1038 => unreachable,915 => unreachable,
1039916
1040 .undef => unreachable, // handled above917 .undef => try dg.renderUndefValue(w, ty, location),
1041 .simple_value => |simple_value| switch (simple_value) {918 .simple_value => |simple_value| switch (simple_value) {
1042 // non-runtime values919 // non-runtime values
1043 .void => unreachable,920 .void => unreachable,
...@@ -1053,46 +930,28 @@ pub const DeclGen = struct {...@@ -1053,46 +930,28 @@ pub const DeclGen = struct {
1053 .enum_literal,930 .enum_literal,
1054 => unreachable, // non-runtime values931 => unreachable, // non-runtime values
1055 .int => try w.print("{f}", .{try dg.fmtIntLiteralDec(val, location)}),932 .int => try w.print("{f}", .{try dg.fmtIntLiteralDec(val, location)}),
1056 .err => |err| try dg.renderErrorName(w, err.name),933 .err => |err| try renderErrorName(w, err.name.toSlice(ip)),
1057 .error_union => |error_union| switch (ctype.info(ctype_pool)) {934 .error_union => |error_union| {
1058 .basic => switch (error_union.val) {935 if (!location.isInitializer()) {
1059 .err_name => |err_name| try dg.renderErrorName(w, err_name),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)),
1060 .payload => try w.writeByte('0'),943 .payload => try w.writeByte('0'),
1061 },944 }
1062 .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable,945 if (ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {
1063 .aggregate => |aggregate| {946 try w.writeAll(", .payload = ");
1064 if (!location.isInitializer()) {947 switch (error_union.val) {
1065 try w.writeByte('(');948 .err_name => try dg.renderUndefValue(w, ty.errorUnionPayload(zcu), initializer_type),
1066 try dg.renderCType(w, ctype);949 .payload => |payload| try dg.renderValue(w, .fromInterned(payload), initializer_type),
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 }
1091 }950 }
1092 try w.writeByte('}');951 }
1093 },952 try w.writeAll(" }");
1094 },953 },
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),
1096 .float => {955 .float => {
1097 const bits = ty.floatBits(target);956 const bits = ty.floatBits(target);
1098 const f128_val = val.toFloat(f128, zcu);957 const f128_val = val.toFloat(f128, zcu);
...@@ -1143,7 +1002,7 @@ pub const DeclGen = struct {...@@ -1143,7 +1002,7 @@ pub const DeclGen = struct {
1143 else1002 else
1144 unreachable;1003 unreachable;
11451004
1146 if (location == .StaticInitializer) {1005 if (location == .static_initializer) {
1147 if (!std.math.isNan(f128_val) and std.math.isSignalNan(f128_val))1006 if (!std.math.isNan(f128_val) and std.math.isSignalNan(f128_val))
1148 return dg.fail("TODO: C backend: implement nans rendering in static initializers", .{});1007 return dg.fail("TODO: C backend: implement nans rendering in static initializers", .{});
11491008
...@@ -1154,9 +1013,11 @@ pub const DeclGen = struct {...@@ -1154,9 +1013,11 @@ pub const DeclGen = struct {
1154 // return dg.fail("Only quiet nans are supported in global variable initializers", .{});1013 // return dg.fail("Only quiet nans are supported in global variable initializers", .{});
1155 }1014 }
11561015
1157 try w.writeAll("zig_");1016 if (location == .static_initializer) {
1158 try w.writeAll(if (location == .StaticInitializer) "init" else "make");1017 try w.writeAll("zig_init_special_");
1159 try w.writeAll("_special_");1018 } else {
1019 try w.writeAll("zig_make_special_");
1020 }
1160 try dg.renderTypeForBuiltinFnName(w, ty);1021 try dg.renderTypeForBuiltinFnName(w, ty);
1161 try w.writeByte('(');1022 try w.writeByte('(');
1162 if (std.math.signbit(f128_val)) try w.writeByte('-');1023 if (std.math.signbit(f128_val)) try w.writeByte('-');
...@@ -1183,105 +1044,85 @@ pub const DeclGen = struct {...@@ -1183,105 +1044,85 @@ pub const DeclGen = struct {
1183 if (!empty) try w.writeByte(')');1044 if (!empty) try w.writeByte(')');
1184 },1045 },
1185 .slice => |slice| {1046 .slice => |slice| {
1186 const aggregate = ctype.info(ctype_pool).aggregate;
1187 if (!location.isInitializer()) {1047 if (!location.isInitializer()) {
1188 try w.writeByte('(');1048 try w.writeByte('(');
1189 try dg.renderCType(w, ctype);1049 try dg.renderType(w, ty);
1190 try w.writeByte(')');1050 try w.writeByte(')');
1191 }1051 }
1192 try w.writeByte('{');1052 try w.writeByte('{');
1193 for (0..aggregate.fields.len) |field_index| {1053 try dg.renderValue(w, .fromInterned(slice.ptr), initializer_type);
1194 if (field_index > 0) try w.writeByte(',');1054 try w.writeByte(',');
1195 try dg.renderValue(w, Value.fromInterned(1055 try dg.renderValue(w, .fromInterned(slice.len), initializer_type);
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 }
1203 try w.writeByte('}');1056 try w.writeByte('}');
1204 },1057 },
1205 .ptr => {1058 .ptr => {
1206 var arena = std.heap.ArenaAllocator.init(zcu.gpa);1059 const derivation = try val.pointerDerivation(dg.arena, pt, null);
1207 defer arena.deinit();1060 try w.writeByte('(');
1208 const derivation = try val.pointerDerivation(arena.allocator(), pt, null);
1209 try dg.renderPointer(w, derivation, location);1061 try dg.renderPointer(w, derivation, location);
1062 try w.writeByte(')');
1210 },1063 },
1211 .opt => |opt| switch (ctype.info(ctype_pool)) {1064 .opt => |opt| switch (CType.classifyOptional(ty, zcu)) {
1212 .basic => if (ctype.isBool()) try w.writeAll(switch (opt.val) {1065 .npv_payload => unreachable, // opv optional
1213 .none => "true",1066 .opv_payload => {
1214 else => "false",1067 if (!location.isInitializer()) {
1215 }) else switch (opt.val) {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) {
1216 .none => try w.writeByte('0'),1078 .none => try w.writeByte('0'),
1217 else => |payload| switch (ip.indexToKey(payload)) {1079 else => |payload_val| try dg.renderValue(w, .fromInterned(payload_val), location),
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 },
1226 },1080 },
1227 .pointer => switch (opt.val) {1081 .ptr_like => switch (opt.val) {
1228 .none => try w.writeAll("NULL"),1082 .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),
1230 },1084 },
1231 .aligned, .array, .vector, .fwd_decl, .function => unreachable,1085 .slice_like => switch (opt.val) {
1232 .aggregate => |aggregate| {1086 .none => {
1233 switch (opt.val) {1087 if (!location.isInitializer()) {
1234 .none => {},1088 try w.writeByte('(');
1235 else => |payload| switch (aggregate.fields.at(0, ctype_pool).name.index) {1089 try dg.renderType(w, ty);
1236 .is_null, .payload => {},1090 try w.writeByte(')');
1237 .ptr, .len => return dg.renderValue(1091 }
1238 w,1092 try w.writeAll("{NULL,");
1239 Value.fromInterned(payload),1093 try dg.renderUndefValue(w, .usize, initializer_type);
1240 location,1094 try w.writeByte('}');
1241 ),1095 },
1242 else => unreachable,1096 else => |payload_val| try dg.renderValue(w, .fromInterned(payload_val), location),
1243 },1097 },
1244 }1098 .@"struct" => {
1245 if (!location.isInitializer()) {1099 if (!location.isInitializer()) {
1246 try w.writeByte('(');1100 try w.writeByte('(');
1247 try dg.renderCType(w, ctype);1101 try dg.renderType(w, ty);
1248 try w.writeByte(')');1102 try w.writeByte(')');
1249 }1103 }
1250 try w.writeByte('{');1104 switch (opt.val) {
1251 for (0..aggregate.fields.len) |field_index| {1105 .none => {
1252 if (field_index > 0) try w.writeByte(',');1106 try w.writeAll("{ .is_null = true, .payload = ");
1253 switch (aggregate.fields.at(field_index, ctype_pool).name.index) {1107 try dg.renderUndefValue(w, ty.optionalChild(zcu), initializer_type);
1254 .is_null => try w.writeAll(switch (opt.val) {1108 try w.writeAll(" }");
1255 .none => "true",1109 },
1256 else => "false",1110 else => |payload_val| {
1257 }),1111 try w.writeAll("{ .is_null = false, .payload = ");
1258 .payload => switch (opt.val) {1112 try dg.renderValue(w, .fromInterned(payload_val), initializer_type);
1259 .none => try dg.renderUndefValue(1113 try w.writeAll(" }");
1260 w,1114 },
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 }
1274 }1115 }
1275 try w.writeByte('}');
1276 },1116 },
1277 },1117 },
1278 .aggregate => switch (ip.indexToKey(ty.toIntern())) {1118 .aggregate => switch (ip.indexToKey(ty.toIntern())) {
1279 .array_type, .vector_type => {1119 .array_type, .vector_type => {
1280 if (location == .FunctionArgument) {1120 if (!location.isInitializer()) {
1281 try w.writeByte('(');1121 try w.writeByte('(');
1282 try dg.renderCType(w, ctype);1122 try dg.renderType(w, ty);
1283 try w.writeByte(')');1123 try w.writeByte(')');
1284 }1124 }
1125 try w.writeByte('{');
1285 const ai = ty.arrayInfo(zcu);1126 const ai = ty.arrayInfo(zcu);
1286 if (ai.elem_type.eql(.u8, zcu)) {1127 if (ai.elem_type.eql(.u8, zcu)) {
1287 var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu)));1128 var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu)));
...@@ -1314,11 +1155,12 @@ pub const DeclGen = struct {...@@ -1314,11 +1155,12 @@ pub const DeclGen = struct {
1314 }1155 }
1315 try w.writeByte('}');1156 try w.writeByte('}');
1316 }1157 }
1158 try w.writeByte('}');
1317 },1159 },
1318 .tuple_type => |tuple| {1160 .tuple_type => |tuple| {
1319 if (!location.isInitializer()) {1161 if (!location.isInitializer()) {
1320 try w.writeByte('(');1162 try w.writeByte('(');
1321 try dg.renderCType(w, ctype);1163 try dg.renderType(w, ty);
1322 try w.writeByte(')');1164 try w.writeByte(')');
1323 }1165 }
13241166
...@@ -1354,7 +1196,7 @@ pub const DeclGen = struct {...@@ -1354,7 +1196,7 @@ pub const DeclGen = struct {
13541196
1355 if (!location.isInitializer()) {1197 if (!location.isInitializer()) {
1356 try w.writeByte('(');1198 try w.writeByte('(');
1357 try dg.renderCType(w, ctype);1199 try dg.renderType(w, ty);
1358 try w.writeByte(')');1200 try w.writeByte(')');
1359 }1201 }
13601202
...@@ -1385,69 +1227,60 @@ pub const DeclGen = struct {...@@ -1385,69 +1227,60 @@ pub const DeclGen = struct {
1385 .un => |un| {1227 .un => |un| {
1386 const loaded_union = ip.loadUnionType(ty.toIntern());1228 const loaded_union = ip.loadUnionType(ty.toIntern());
1387 if (un.tag == .none) {1229 if (un.tag == .none) {
1388 const backing_ty = try ty.externUnionBackingType(pt);
1389 assert(loaded_union.layout == .@"extern");1230 assert(loaded_union.layout == .@"extern");
1390 if (location == .StaticInitializer) {1231 if (location == .static_initializer) {
1391 return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});1232 return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});
1392 }1233 }
13931234
1394 const ptr_ty = try pt.singleConstPtrType(ty);1235 const ptr_ty = try pt.singleConstPtrType(ty);
1395 try w.writeAll("*((");1236 try w.writeAll("*(");
1396 try dg.renderType(w, ptr_ty);1237 try dg.renderType(w, ptr_ty);
1397 try w.writeAll(")(");1238 try w.writeAll(")&");
1398 try dg.renderType(w, backing_ty);1239 // We need an lvalue for '&'.
1399 try w.writeAll("){");1240 try dg.renderValueAsLvalue(w, .fromInterned(un.val));
1400 try dg.renderValue(w, Value.fromInterned(un.val), location);
1401 try w.writeAll("})");
1402 } else {1241 } else {
1403 if (!location.isInitializer()) {1242 if (!location.isInitializer()) {
1404 try w.writeByte('(');1243 try w.writeByte('(');
1405 try dg.renderCType(w, ctype);1244 try dg.renderType(w, ty);
1406 try w.writeByte(')');1245 try w.writeByte(')');
1407 }1246 }
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)).?;1255 if (loaded_union.layout == .auto) try w.writeByte('{');
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];
14121256
1413 const has_tag = loaded_union.has_runtime_tag;1257 if (loaded_union.has_runtime_tag) {
1414 if (has_tag) try w.writeByte('{');1258 try w.writeAll(" .tag = ");
1415 const aggregate = ctype.info(ctype_pool).aggregate;1259 try dg.renderValue(w, .fromInterned(un.tag), initializer_type);
1416 for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| {1260 try w.writeAll(", .payload = ");
1417 if (outer_field_index > 0) try w.writeByte(',');1261 }
1418 switch (if (has_tag)1262
1419 aggregate.fields.at(outer_field_index, ctype_pool).name.index1263 const enum_tag_ty: Type = .fromInterned(loaded_union.enum_tag_type);
1420 else1264 const active_field_index = enum_tag_ty.enumTagFieldIndex(.fromInterned(un.tag), zcu).?;
1421 .payload) {1265 const active_field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[active_field_index]);
1422 .tag => try dg.renderValue(1266 if (active_field_ty.hasRuntimeBits(zcu)) {
1423 w,1267 const active_field_name = enum_tag_ty.enumFieldName(active_field_index, zcu);
1424 Value.fromInterned(un.tag),1268 try w.print("{{ .{f} = ", .{fmtIdentSolo(active_field_name.toSlice(ip))});
1425 initializer_type,1269 try dg.renderValue(w, .fromInterned(un.val), initializer_type);
1426 ),1270 try w.writeAll(" }");
1427 .payload => {1271 } else {
1428 try w.writeByte('{');1272 const first_field_ty: Type = for (loaded_union.field_types.get(ip)) |field_ty_ip| {
1429 if (field_ty.hasRuntimeBits(zcu)) {1273 const field_ty: Type = .fromInterned(field_ty_ip);
1430 try w.print(" .{f} = ", .{fmtIdentSolo(field_name.toSlice(ip))});1274 if (!field_ty.hasRuntimeBits(pt.zcu)) continue;
1431 try dg.renderValue(1275 break field_ty;
1432 w,1276 } else unreachable;
1433 Value.fromInterned(un.val),1277 try w.writeByte('{');
1434 initializer_type,1278 try dg.renderUndefValue(w, first_field_ty, initializer_type);
1435 );1279 try w.writeByte('}');
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 }
1449 }1280 }
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('}');
1451 }1284 }
1452 },1285 },
1453 }1286 }
...@@ -1463,11 +1296,10 @@ pub const DeclGen = struct {...@@ -1463,11 +1296,10 @@ pub const DeclGen = struct {
1463 const zcu = pt.zcu;1296 const zcu = pt.zcu;
1464 const ip = &zcu.intern_pool;1297 const ip = &zcu.intern_pool;
1465 const target = &dg.mod.resolved_target.result;1298 const target = &dg.mod.resolved_target.result;
1466 const ctype_pool = &dg.ctype_pool;
14671299
1468 const initializer_type: ValueRenderLocation = switch (location) {1300 const initializer_type: ValueRenderLocation = switch (location) {
1469 .StaticInitializer => .StaticInitializer,1301 .static_initializer => .static_initializer,
1470 else => .Initializer,1302 else => .initializer,
1471 };1303 };
14721304
1473 const safety_on = switch (zcu.optimizeMode()) {1305 const safety_on = switch (zcu.optimizeMode()) {
...@@ -1475,7 +1307,6 @@ pub const DeclGen = struct {...@@ -1475,7 +1307,6 @@ pub const DeclGen = struct {
1475 .ReleaseFast, .ReleaseSmall => false,1307 .ReleaseFast, .ReleaseSmall => false,
1476 };1308 };
14771309
1478 const ctype = try dg.ctypeFromType(ty, location.toCTypeKind());
1479 switch (ty.toIntern()) {1310 switch (ty.toIntern()) {
1480 .c_longdouble_type,1311 .c_longdouble_type,
1481 .f16_type,1312 .f16_type,
...@@ -1500,76 +1331,109 @@ pub const DeclGen = struct {...@@ -1500,76 +1331,109 @@ pub const DeclGen = struct {
1500 else => unreachable,1331 else => unreachable,
1501 }1332 }
1502 try w.writeAll(", ");1333 try w.writeAll(", ");
1503 try dg.renderUndefValue(w, repr_ty, .FunctionArgument);1334 try dg.renderUndefValue(w, repr_ty, .other);
1504 return w.writeByte(')');1335 return w.writeByte(')');
1505 },1336 },
1506 .bool_type => try w.writeAll(if (safety_on) "0xaa" else "false"),1337 .bool_type => try w.writeAll(if (safety_on) "0xaa" else "false"),
1507 else => switch (ip.indexToKey(ty.toIntern())) {1338 else => switch (ip.indexToKey(ty.toIntern())) {
1508 .simple_type,1339 .simple_type, // anyerror, c_char (etc), usize, isize
1509 .int_type,1340 .int_type,
1510 .enum_type,1341 .enum_type,
1511 .error_set_type,1342 .error_set_type,
1512 .inferred_error_set_type,1343 .inferred_error_set_type,
1513 => return w.print("{f}", .{1344 => switch (CType.classifyInt(ty, zcu)) {
1514 try dg.fmtIntLiteralHex(try pt.undefValue(ty), location),1345 .void => unreachable, // opv
1515 }),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 },
1516 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {1387 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
1517 .one, .many, .c => {1388 .one, .many, .c => {
1518 try w.writeAll("((");1389 try w.writeAll("((");
1519 try dg.renderCType(w, ctype);1390 try dg.renderType(w, ty);
1520 return w.print("){f})", .{1391 try w.writeByte(')');
1521 try dg.fmtIntLiteralHex(.undef_usize, .Other),1392 try dg.renderUndefValue(w, .usize, location);
1522 });1393 try w.writeByte(')');
1523 },1394 },
1524 .slice => {1395 .slice => {
1525 if (!location.isInitializer()) {1396 if (!location.isInitializer()) {
1526 try w.writeByte('(');1397 try w.writeByte('(');
1527 try dg.renderCType(w, ctype);1398 try dg.renderType(w, ty);
1528 try w.writeByte(')');1399 try w.writeByte(')');
1529 }1400 }
15301401
1531 try w.writeAll("{(");1402 try w.writeByte('{');
1532 const ptr_ty = ty.slicePtrFieldType(zcu);1403 try dg.renderUndefValue(w, ty.slicePtrFieldType(zcu), initializer_type);
1533 try dg.renderType(w, ptr_ty);1404 try w.writeByte(',');
1534 return w.print("){f}, {0f}}}", .{1405 try dg.renderUndefValue(w, .usize, initializer_type);
1535 try dg.fmtIntLiteralHex(.undef_usize, .Other),1406 try w.writeByte('}');
1536 });
1537 },1407 },
1538 },1408 },
1539 .opt_type => |child_type| switch (ctype.info(ctype_pool)) {1409 .opt_type => |child_type| switch (CType.classifyOptional(ty, zcu)) {
1540 .basic, .pointer => try dg.renderUndefValue(1410 .npv_payload => unreachable, // opv optional
1541 w,1411
1542 .fromInterned(if (ctype.isBool()) .bool_type else child_type),1412 .error_set,
1543 location,1413 .ptr_like,
1544 ),1414 .slice_like,
1545 .aligned, .array, .vector, .fwd_decl, .function => unreachable,1415 => try dg.renderUndefValue(w, .fromInterned(child_type), location),
1546 .aggregate => |aggregate| {1416
1547 switch (aggregate.fields.at(0, ctype_pool).name.index) {1417 .opv_payload => {
1548 .is_null, .payload => {},
1549 .ptr, .len => return dg.renderUndefValue(
1550 w,
1551 .fromInterned(child_type),
1552 location,
1553 ),
1554 else => unreachable,
1555 }
1556 if (!location.isInitializer()) {1418 if (!location.isInitializer()) {
1557 try w.writeByte('(');1419 try w.writeByte('(');
1558 try dg.renderCType(w, ctype);1420 try dg.renderType(w, ty);
1559 try w.writeByte(')');1421 try w.writeByte(')');
1560 }1422 }
1561 try w.writeByte('{');1423 try w.writeAll(if (safety_on) "{.is_null=0xaa}" else "{.is_null=false}");
1562 for (0..aggregate.fields.len) |field_index| {1424 },
1563 if (field_index > 0) try w.writeByte(',');1425
1564 try dg.renderUndefValue(w, .fromInterned(1426 .@"struct" => {
1565 switch (aggregate.fields.at(field_index, ctype_pool).name.index) {1427 if (!location.isInitializer()) {
1566 .is_null => .bool_type,1428 try w.writeByte('(');
1567 .payload => child_type,1429 try dg.renderType(w, ty);
1568 else => unreachable,1430 try w.writeByte(')');
1569 },
1570 ), initializer_type);
1571 }1431 }
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(" }");
1573 },1437 },
1574 },1438 },
1575 .struct_type => {1439 .struct_type => {
...@@ -1578,10 +1442,9 @@ pub const DeclGen = struct {...@@ -1578,10 +1442,9 @@ pub const DeclGen = struct {
1578 .auto, .@"extern" => {1442 .auto, .@"extern" => {
1579 if (!location.isInitializer()) {1443 if (!location.isInitializer()) {
1580 try w.writeByte('(');1444 try w.writeByte('(');
1581 try dg.renderCType(w, ctype);1445 try dg.renderType(w, ty);
1582 try w.writeByte(')');1446 try w.writeByte(')');
1583 }1447 }
1584
1585 try w.writeByte('{');1448 try w.writeByte('{');
1586 var field_it = loaded_struct.iterateRuntimeOrder(ip);1449 var field_it = loaded_struct.iterateRuntimeOrder(ip);
1587 var need_comma = false;1450 var need_comma = false;
...@@ -1601,7 +1464,7 @@ pub const DeclGen = struct {...@@ -1601,7 +1464,7 @@ pub const DeclGen = struct {
1601 .tuple_type => |tuple_info| {1464 .tuple_type => |tuple_info| {
1602 if (!location.isInitializer()) {1465 if (!location.isInitializer()) {
1603 try w.writeByte('(');1466 try w.writeByte('(');
1604 try dg.renderCType(w, ctype);1467 try dg.renderType(w, ty);
1605 try w.writeByte(')');1468 try w.writeByte(')');
1606 }1469 }
16071470
...@@ -1624,80 +1487,61 @@ pub const DeclGen = struct {...@@ -1624,80 +1487,61 @@ pub const DeclGen = struct {
1624 .auto, .@"extern" => {1487 .auto, .@"extern" => {
1625 if (!location.isInitializer()) {1488 if (!location.isInitializer()) {
1626 try w.writeByte('(');1489 try w.writeByte('(');
1627 try dg.renderCType(w, ctype);1490 try dg.renderType(w, ty);
1628 try w.writeByte(')');1491 try w.writeByte(')');
1629 }1492 }
16301493
1631 const has_tag = loaded_union.has_runtime_tag;1494 const first_field_ty: Type = for (loaded_union.field_types.get(ip)) |field_ty_ip| {
1632 if (has_tag) try w.writeByte('{');1495 const field_ty: Type = .fromInterned(field_ty_ip);
1633 const aggregate = ctype.info(ctype_pool).aggregate;1496 if (!field_ty.hasRuntimeBits(pt.zcu)) continue;
1634 for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| {1497 break field_ty;
1635 if (outer_field_index > 0) try w.writeByte(',');1498 } else {
1636 switch (if (has_tag)1499 assert(loaded_union.has_runtime_tag); // otherwise it does not have runtime bits
1637 aggregate.fields.at(outer_field_index, ctype_pool).name.index1500 try w.writeAll("{ .tag = ");
1638 else1501 try dg.renderUndefValue(w, .fromInterned(loaded_union.enum_tag_type), initializer_type);
1639 .payload) {1502 try w.writeAll(" }");
1640 .tag => try dg.renderUndefValue(1503 return;
1641 w,1504 };
1642 .fromInterned(loaded_union.enum_tag_type),1505
1643 initializer_type,1506 if (loaded_union.layout == .auto) try w.writeByte('{');
1644 ),1507
1645 .payload => {1508 if (loaded_union.has_runtime_tag) {
1646 try w.writeByte('{');1509 try w.writeAll(" .tag = ");
1647 for (0..loaded_union.field_types.len) |inner_field_index| {1510 try dg.renderUndefValue(w, .fromInterned(loaded_union.enum_tag_type), initializer_type);
1648 const inner_field_ty: Type = .fromInterned(1511 try w.writeAll(", .payload = ");
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 }
1663 }1512 }
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('}');
1665 },1520 },
1666 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),1521 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
1667 }1522 }
1668 },1523 },
1669 .error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) {1524 .error_union_type => |error_union| {
1670 .basic => try dg.renderUndefValue(1525 if (!location.isInitializer()) {
1671 w,1526 try w.writeByte('(');
1672 .fromInterned(error_union_type.error_set_type),1527 try dg.renderType(w, ty);
1673 location,1528 try w.writeByte(')');
1674 ),1529 }
1675 .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable,1530 try w.writeAll("{ .error = ");
1676 .aggregate => |aggregate| {1531 try dg.renderUndefValue(w, .fromInterned(error_union.error_set_type), initializer_type);
1677 if (!location.isInitializer()) {1532 if (Type.fromInterned(error_union.payload_type).hasRuntimeBits(zcu)) {
1678 try w.writeByte('(');1533 try w.writeAll(", .payload = ");
1679 try dg.renderCType(w, ctype);1534 try dg.renderUndefValue(w, .fromInterned(error_union.payload_type), initializer_type);
1680 try w.writeByte(')');1535 }
1681 }1536 try w.writeAll(" }");
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 },
1699 },1537 },
1700 .array_type, .vector_type => {1538 .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('{');
1701 const ai = ty.arrayInfo(zcu);1545 const ai = ty.arrayInfo(zcu);
1702 if (ai.elem_type.eql(.u8, zcu)) {1546 if (ai.elem_type.eql(.u8, zcu)) {
1703 var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu)));1547 var literal: StringLiteral = .init(w, @intCast(ty.arrayLenIncludingSentinel(zcu)));
...@@ -1708,14 +1552,8 @@ pub const DeclGen = struct {...@@ -1708,14 +1552,8 @@ pub const DeclGen = struct {
1708 const s_u8: u8 = @intCast(s.toUnsignedInt(zcu));1552 const s_u8: u8 = @intCast(s.toUnsignedInt(zcu));
1709 if (s_u8 != 0) try literal.writeChar(s_u8);1553 if (s_u8 != 0) try literal.writeChar(s_u8);
1710 }1554 }
1711 return literal.end();1555 try literal.end();
1712 } else {1556 } else {
1713 if (!location.isInitializer()) {
1714 try w.writeByte('(');
1715 try dg.renderCType(w, ctype);
1716 try w.writeByte(')');
1717 }
1718
1719 try w.writeByte('{');1557 try w.writeByte('{');
1720 var index: u64 = 0;1558 var index: u64 = 0;
1721 while (index < ai.len) : (index += 1) {1559 while (index < ai.len) : (index += 1) {
...@@ -1726,8 +1564,9 @@ pub const DeclGen = struct {...@@ -1726,8 +1564,9 @@ pub const DeclGen = struct {
1726 if (index > 0) try w.writeAll(", ");1564 if (index > 0) try w.writeAll(", ");
1727 try dg.renderValue(w, s, location);1565 try dg.renderValue(w, s, location);
1728 }1566 }
1729 return w.writeByte('}');1567 try w.writeByte('}');
1730 }1568 }
1569 try w.writeByte('}');
1731 },1570 },
1732 .anyframe_type,1571 .anyframe_type,
1733 .opaque_type,1572 .opaque_type,
...@@ -1762,10 +1601,11 @@ pub const DeclGen = struct {...@@ -1762,10 +1601,11 @@ pub const DeclGen = struct {
1762 w: *Writer,1601 w: *Writer,
1763 fn_val: Value,1602 fn_val: Value,
1764 fn_align: InternPool.Alignment,1603 fn_align: InternPool.Alignment,
1765 kind: CType.Kind,1604 kind: enum { forward_decl, definition },
1766 name: union(enum) {1605 name: union(enum) {
1767 nav: InternPool.Nav.Index,1606 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,
1769 @"export": struct {1609 @"export": struct {
1770 main_name: InternPool.NullTerminatedString,1610 main_name: InternPool.NullTerminatedString,
1771 extern_name: InternPool.NullTerminatedString,1611 extern_name: InternPool.NullTerminatedString,
...@@ -1776,14 +1616,12 @@ pub const DeclGen = struct {...@@ -1776,14 +1616,12 @@ pub const DeclGen = struct {
1776 const ip = &zcu.intern_pool;1616 const ip = &zcu.intern_pool;
17771617
1778 const fn_ty = fn_val.typeOf(zcu);1618 const fn_ty = fn_val.typeOf(zcu);
1779 const fn_ctype = try dg.ctypeFromType(fn_ty, kind);
17801619
1781 const fn_info = zcu.typeToFunc(fn_ty).?;1620 const fn_info = zcu.typeToFunc(fn_ty).?;
1782 if (fn_info.cc == .naked) {1621 if (fn_info.cc == .naked) {
1783 switch (kind) {1622 switch (kind) {
1784 .forward => try w.writeAll("zig_naked_decl "),1623 .forward_decl => try w.writeAll("zig_naked_decl "),
1785 .complete => try w.writeAll("zig_naked "),1624 .definition => try w.writeAll("zig_naked "),
1786 else => unreachable,
1787 }1625 }
1788 }1626 }
17891627
...@@ -1793,45 +1631,63 @@ pub const DeclGen = struct {...@@ -1793,45 +1631,63 @@ pub const DeclGen = struct {
1793 if (func_analysis.branch_hint == .cold)1631 if (func_analysis.branch_hint == .cold)
1794 try w.writeAll("zig_cold ");1632 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)
1797 try w.writeAll("zig_no_builtin ");1635 try w.writeAll("zig_no_builtin ");
1798 }1636 }
17991637
1800 if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn ");1638 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)});
1804 if (toCallingConvention(fn_info.cc, zcu)) |call_conv| {1651 if (toCallingConvention(fn_info.cc, zcu)) |call_conv| {
1805 try w.print("{f}zig_callconv({s})", .{ trailing, call_conv });1652 try w.print("zig_callconv({s}) ", .{call_conv});
1806 trailing = .maybe_space;
1807 }1653 }
1808
1809 try w.print("{f}", .{trailing});
1810 switch (name) {1654 switch (name) {
1811 .nav => |nav| try dg.renderNavName(w, nav),1655 .nav => |nav| try renderNavName(w, nav, ip),
1812 .fmt_ctype_pool_string => |fmt| try w.print("{f}", .{fmt}),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 }),
1813 .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}),1662 .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}),
1814 }1663 }
18151664 {
1816 try renderTypeSuffix(1665 try w.writeByte('(');
1817 dg.pass,1666 var c_param_index: u32 = 0;
1818 &dg.ctype_pool,1667 for (fn_info.param_types.get(ip)) |param_ty_ip| {
1819 zcu,1668 const param_ty: Type = .fromInterned(param_ty_ip);
1820 w,1669 if (!param_ty.hasRuntimeBits(zcu)) continue;
1821 fn_ctype,1670 if (c_param_index != 0) try w.writeAll(", ");
1822 .suffix,1671 try dg.renderTypeAndName(w, param_ty, .{ .arg = c_param_index }, .{
1823 CQualifiers.init(.{ .@"const" = switch (kind) {1672 .@"const" = kind == .definition,
1824 .forward => false,1673 }, .none);
1825 .complete => true,1674 c_param_index += 1;
1826 else => unreachable,1675 }
1827 } }),1676 if (fn_info.is_var_args) {
1828 );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
1830 switch (kind) {1686 switch (kind) {
1831 .forward => {1687 .forward_decl => {
1832 if (fn_align.toByteUnits()) |a| try w.print(" zig_align_fn({})", .{a});1688 if (fn_align.toByteUnits()) |a| try w.print(" zig_align_fn({})", .{a});
1833 switch (name) {1689 switch (name) {
1834 .nav, .fmt_ctype_pool_string => {},1690 .nav, .nav_never_tail, .nav_never_inline => {},
1835 .@"export" => |@"export"| {1691 .@"export" => |@"export"| {
1836 const extern_name = @"export".extern_name.toSlice(ip);1692 const extern_name = @"export".extern_name.toSlice(ip);
1837 const is_mangled = isMangledIdent(extern_name, true);1693 const is_mangled = isMangledIdent(extern_name, true);
...@@ -1855,38 +1711,16 @@ pub const DeclGen = struct {...@@ -1855,38 +1711,16 @@ pub const DeclGen = struct {
1855 },1711 },
1856 }1712 }
1857 },1713 },
1858 .complete => {},1714 .definition => {},
1859 else => unreachable,
1860 }1715 }
1861 }1716 }
18621717
1863 fn ctypeFromType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType {1718 /// Renders the C lowering of the given Zig type to `w`. This renders the type name---to render
1864 defer std.debug.assert(dg.scratch.items.len == 0);1719 /// a declarator with this type, see instead `renderTypeAndName`.
1865 return dg.ctype_pool.fromType(dg.gpa, &dg.scratch, ty, dg.pt, dg.mod, kind);1720 fn renderType(dg: *DeclGen, w: *Writer, ty: Type) (Writer.Error || Allocator.Error)!void {
1866 }1721 const zcu = dg.pt.zcu;
18671722 const cty: CType = try .lower(ty, &dg.ctype_deps, dg.arena, zcu);
1868 fn byteSize(dg: *DeclGen, ctype: CType) u64 {1723 try w.print("{f}", .{cty.fmtTypeName(zcu)});
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, .{});
1890 }1724 }
18911725
1892 const IntCastContext = union(enum) {1726 const IntCastContext = union(enum) {
...@@ -1990,7 +1824,7 @@ pub const DeclGen = struct {...@@ -1990,7 +1824,7 @@ pub const DeclGen = struct {
1990 try w.writeAll("zig_lo_");1824 try w.writeAll("zig_lo_");
1991 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);1825 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
1992 try w.writeByte('(');1826 try w.writeByte('(');
1993 try context.writeValue(dg, w, .FunctionArgument);1827 try context.writeValue(dg, w, .other);
1994 try w.writeByte(')');1828 try w.writeByte(')');
1995 } else if (dest_bits > 64 and src_bits <= 64) {1829 } else if (dest_bits > 64 and src_bits <= 64) {
1996 try w.writeAll("zig_make_");1830 try w.writeAll("zig_make_");
...@@ -2001,7 +1835,7 @@ pub const DeclGen = struct {...@@ -2001,7 +1835,7 @@ pub const DeclGen = struct {
2001 try dg.renderType(w, src_eff_ty);1835 try dg.renderType(w, src_eff_ty);
2002 try w.writeByte(')');1836 try w.writeByte(')');
2003 }1837 }
2004 try context.writeValue(dg, w, .FunctionArgument);1838 try context.writeValue(dg, w, .other);
2005 try w.writeByte(')');1839 try w.writeByte(')');
2006 } else {1840 } else {
2007 assert(!src_is_ptr);1841 assert(!src_is_ptr);
...@@ -2010,23 +1844,16 @@ pub const DeclGen = struct {...@@ -2010,23 +1844,16 @@ pub const DeclGen = struct {
2010 try w.writeAll("(zig_hi_");1844 try w.writeAll("(zig_hi_");
2011 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);1845 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
2012 try w.writeByte('(');1846 try w.writeByte('(');
2013 try context.writeValue(dg, w, .FunctionArgument);1847 try context.writeValue(dg, w, .other);
2014 try w.writeAll("), zig_lo_");1848 try w.writeAll("), zig_lo_");
2015 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);1849 try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
2016 try w.writeByte('(');1850 try w.writeByte('(');
2017 try context.writeValue(dg, w, .FunctionArgument);1851 try context.writeValue(dg, w, .other);
2018 try w.writeAll("))");1852 try w.writeAll("))");
2019 }1853 }
2020 }1854 }
20211855
2022 /// Renders a type and name in field declaration/definition format.1856 /// Renders to `w` a C declarator whose type is the C lowering of the given Zig type.
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 ///
2030 fn renderTypeAndName(1857 fn renderTypeAndName(
2031 dg: *DeclGen,1858 dg: *DeclGen,
2032 w: *Writer,1859 w: *Writer,
...@@ -2034,73 +1861,47 @@ pub const DeclGen = struct {...@@ -2034,73 +1861,47 @@ pub const DeclGen = struct {
2034 name: CValue,1861 name: CValue,
2035 qualifiers: CQualifiers,1862 qualifiers: CQualifiers,
2036 alignment: Alignment,1863 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,
2058 ) !void {1864 ) !void {
2059 const zcu = dg.pt.zcu;1865 const zcu = dg.pt.zcu;
2060 switch (alignas.abiOrder()) {1866 const ip = &zcu.intern_pool;
2061 .lt => try w.print("zig_under_align({}) ", .{alignas.toByteUnits()}),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().?}),
2062 .eq => {},1871 .eq => {},
2063 .gt => try w.print("zig_align({}) ", .{alignas.toByteUnits()}),1872 .gt => try w.print("zig_align({d}) ", .{alignment.toByteUnits().?}),
2064 }1873 };
20651874 if (qualifiers.@"const") try w.writeAll("const ");
2066 try w.print("{f}", .{1875 if (qualifiers.@"volatile") try w.writeAll("volatile ");
2067 try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, ctype, .suffix, qualifiers),1876 if (qualifiers.restrict) try w.writeAll("restrict ");
2068 });1877 switch (name) {
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) {
2076 .new_local, .local => |i| try w.print("t{d}", .{i}),1878 .new_local, .local => |i| try w.print("t{d}", .{i}),
1879 .arg => |i| try w.print("a{d}", .{i}),
2077 .constant => |uav| try renderUavName(w, uav),1880 .constant => |uav| try renderUavName(w, uav),
2078 .nav => |nav| try dg.renderNavName(w, nav),1881 .nav => |nav| try renderNavName(w, nav, ip),
2079 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),1882 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
2080 else => unreachable,1883 else => unreachable,
2081 }1884 }
1885 try w.print("{f}", .{cty.fmtDeclaratorSuffix(zcu)});
2082 }1886 }
20831887
2084 fn writeCValue(dg: *DeclGen, w: *Writer, c_value: CValue) Error!void {1888 fn writeCValue(dg: *DeclGen, w: *Writer, c_value: CValue) Error!void {
2085 switch (c_value) {1889 switch (c_value) {
2086 .none, .new_local, .local, .local_ref => unreachable,1890 .none, .new_local, .local, .local_ref => unreachable,
2087 .constant => |uav| try renderUavName(w, uav),1891 .constant => |uav| try renderUavName(w, uav),
2088 .arg, .arg_array => unreachable,1892 .arg => unreachable,
2089 .field => |i| try w.print("f{d}", .{i}),1893 .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),
2091 .nav_ref => |nav| {1895 .nav_ref => |nav| {
2092 try w.writeByte('&');1896 try w.writeByte('&');
2093 try dg.renderNavName(w, nav);1897 try renderNavName(w, nav, &dg.pt.zcu.intern_pool);
2094 },1898 },
2095 .undef => |ty| try dg.renderUndefValue(w, ty, .Other),1899 .undef => |ty| try dg.renderUndefValue(w, ty, .other),
2096 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),1900 .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
2097 .payload_identifier => |ident| try w.print("{f}.{f}", .{1901 .payload_identifier => |ident| try w.print("{f}.{f}", .{
2098 fmtIdentSolo("payload"),1902 fmtIdentSolo("payload"),
2099 fmtIdentSolo(ident),1903 fmtIdentSolo(ident),
2100 }),1904 }),
2101 .ctype_pool_string => |string| try w.print("{f}", .{
2102 fmtCTypePoolString(string, &dg.ctype_pool, true),
2103 }),
2104 }1905 }
2105 }1906 }
21061907
...@@ -2112,16 +1913,14 @@ pub const DeclGen = struct {...@@ -2112,16 +1913,14 @@ pub const DeclGen = struct {
2112 .local_ref,1913 .local_ref,
2113 .constant,1914 .constant,
2114 .arg,1915 .arg,
2115 .arg_array,
2116 .ctype_pool_string,
2117 => unreachable,1916 => unreachable,
2118 .field => |i| try w.print("f{d}", .{i}),1917 .field => |i| try w.print("f{d}", .{i}),
2119 .nav => |nav| {1918 .nav => |nav| {
2120 try w.writeAll("(*");1919 try w.writeAll("(*");
2121 try dg.renderNavName(w, nav);1920 try renderNavName(w, nav, &dg.pt.zcu.intern_pool);
2122 try w.writeByte(')');1921 try w.writeByte(')');
2123 },1922 },
2124 .nav_ref => |nav| try dg.renderNavName(w, nav),1923 .nav_ref => |nav| try renderNavName(w, nav, &dg.pt.zcu.intern_pool),
2125 .undef => unreachable,1924 .undef => unreachable,
2126 .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}),1925 .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}),
2127 .payload_identifier => |ident| try w.print("(*{f}.{f})", .{1926 .payload_identifier => |ident| try w.print("(*{f}.{f})", .{
...@@ -2157,8 +1956,6 @@ pub const DeclGen = struct {...@@ -2157,8 +1956,6 @@ pub const DeclGen = struct {
2157 .field,1956 .field,
2158 .undef,1957 .undef,
2159 .arg,1958 .arg,
2160 .arg_array,
2161 .ctype_pool_string,
2162 => unreachable,1959 => unreachable,
2163 .nav, .identifier, .payload_identifier => {1960 .nav, .identifier, .payload_identifier => {
2164 try dg.writeCValue(w, c_value);1961 try dg.writeCValue(w, c_value);
...@@ -2172,101 +1969,36 @@ pub const DeclGen = struct {...@@ -2172,101 +1969,36 @@ pub const DeclGen = struct {
2172 try dg.writeCValue(w, member);1969 try dg.writeCValue(w, member);
2173 }1970 }
21741971
2175 fn renderFwdDecl(1972 fn renderTypeForBuiltinFnName(dg: *DeclGen, w: *Writer, ty: Type) !void {
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 {
2185 const zcu = dg.pt.zcu;1973 const zcu = dg.pt.zcu;
2186 const ip = &zcu.intern_pool;1974 switch (ty.zigTypeTag(zcu)) {
2187 const nav = ip.getNav(nav_index);1975 .bool => return w.writeAll("u8"),
2188 const fwd = &dg.fwd_decl.writer;1976 .float => return w.print("f{d}", .{ty.floatBits(zcu.getTarget())}),
2189 try fwd.writeAll(switch (flags.linkage) {1977 else => {},
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?", .{}),
2197 }1978 }
2198 switch (flags.linkage) {1979 if (ty.isPtrAtRuntime(zcu)) {
2199 .internal => {},1980 return w.print("p{d}", .{zcu.getTarget().ptrBitWidth()});
2200 .strong, .weak, .link_once => try fwd.print("zig_visibility({s}) ", .{@tagName(flags.visibility)}),
2201 }1981 }
2202 if (flags.is_threadlocal and !dg.mod.single_threaded) try fwd.writeAll("zig_threadlocal ");1982 switch (CType.classifyInt(ty, zcu)) {
2203 try dg.renderTypeAndName(1983 .void => unreachable, // opv
2204 fwd,1984 .small => try w.print("{c}{d}", .{
2205 .fromInterned(nav.typeOf(ip)),1985 signAbbrev(ty.intInfo(zcu).signedness),
2206 .{ .nav = nav_index },1986 ty.abiSize(zcu) * 8,
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,
2255 }),1987 }),
2256 .array => try w.writeAll("big"),1988 .big => try w.writeAll("big"),
2257 }1989 }
2258 }1990 }
22591991
2260 fn renderBuiltinInfo(dg: *DeclGen, w: *Writer, ty: Type, info: BuiltinInfo) !void {1992 fn renderBuiltinInfo(dg: *DeclGen, w: *Writer, ty: Type, info: BuiltinInfo) !void {
2261 const ctype = try dg.ctypeFromType(ty, .complete);1993 const pt = dg.pt;
2262 const is_big = ctype.info(&dg.ctype_pool) == .array;1994 const zcu = pt.zcu;
1995
1996 const is_big = lowersToBigInt(ty, zcu);
2263 switch (info) {1997 switch (info) {
2264 .none => if (!is_big) return,1998 .none => if (!is_big) return,
2265 .bits => {},1999 .bits => {},
2266 }2000 }
22672001
2268 const pt = dg.pt;
2269 const zcu = pt.zcu;
2270 const int_info: std.builtin.Type.Int = if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else .{2002 const int_info: std.builtin.Type.Int = if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else .{
2271 .signedness = .unsigned,2003 .signedness = .unsigned,
2272 .bits = @intCast(ty.bitSize(zcu)),2004 .bits = @intCast(ty.bitSize(zcu)),
...@@ -2275,7 +2007,7 @@ pub const DeclGen = struct {...@@ -2275,7 +2007,7 @@ pub const DeclGen = struct {
2275 if (is_big) try w.print(", {}", .{int_info.signedness == .signed});2007 if (is_big) try w.print(", {}", .{int_info.signedness == .signed});
2276 try w.print(", {f}", .{try dg.fmtIntLiteralDec(2008 try w.print(", {f}", .{try dg.fmtIntLiteralDec(
2277 try pt.intValue(if (is_big) .u16 else .u8, int_info.bits),2009 try pt.intValue(if (is_big) .u16 else .u8, int_info.bits),
2278 .FunctionArgument,2010 .other,
2279 )});2011 )});
2280 }2012 }
22812013
...@@ -2286,15 +2018,13 @@ pub const DeclGen = struct {...@@ -2286,15 +2018,13 @@ pub const DeclGen = struct {
2286 base: u8,2018 base: u8,
2287 case: std.fmt.Case,2019 case: std.fmt.Case,
2288 ) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) {2020 ) !std.fmt.Alt(FormatIntLiteralContext, formatIntLiteral) {
2289 const zcu = dg.pt.zcu;2021 // If there's a bigint type involved, mark a dependency on it.
2290 const kind = loc.toCTypeKind();2022 const cty: CType = try .lower(val.typeOf(dg.pt.zcu), &dg.ctype_deps, dg.arena, dg.pt.zcu);
2291 const ty = val.typeOf(zcu);
2292 return .{ .data = .{2023 return .{ .data = .{
2293 .dg = dg,2024 .dg = dg,
2294 .int_info = ty.intInfo(zcu),2025 .loc = loc,
2295 .kind = kind,
2296 .ctype = try dg.ctypeFromType(ty, kind),
2297 .val = val,2026 .val = val,
2027 .cty = cty,
2298 .base = base,2028 .base = base,
2299 .case = case,2029 .case = case,
2300 } };2030 } };
...@@ -2317,339 +2047,11 @@ pub const DeclGen = struct {...@@ -2317,339 +2047,11 @@ pub const DeclGen = struct {
2317 }2047 }
2318};2048};
23192049
2320const CTypeFix = enum { prefix, suffix };2050const CQualifiers = packed struct {
2321const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict });2051 @"const": bool = false,
2322const Const = CQualifiers.init(.{ .@"const" = true });2052 @"volatile": bool = false,
2323const RenderCTypeTrailing = enum {2053 restrict: bool = false,
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 }
2333};2054};
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
2654pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {2056pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {
2655 for (zcu.global_assembly.values()) |asm_source| {2057 for (zcu.global_assembly.values()) |asm_source| {
...@@ -2657,200 +2059,128 @@ pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {...@@ -2657,200 +2059,128 @@ pub fn genGlobalAsm(zcu: *Zcu, w: *Writer) !void {
2657 }2059 }
2658}2060}
26592061
2660pub fn genErrDecls(o: *Object) Error!void {2062pub fn genErrDecls(
2661 const pt = o.dg.pt;2063 zcu: *const Zcu,
2662 const zcu = pt.zcu;2064 w: *Writer,
2065 slice_const_u8_sentinel_0_type_name: []const u8,
2066) Writer.Error!void {
2663 const ip = &zcu.intern_pool;2067 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
2668 const names = ip.global_error_set.getNamesFromMainThread();2069 const names = ip.global_error_set.getNamesFromMainThread();
2070 // Don't generate an invalid empty enum if the global error set is empty!
2669 if (names.len > 0) {2071 if (names.len > 0) {
2670 try w.writeAll("enum {");2072 try w.writeAll("enum {\n");
2671 o.indent();
2672 try o.newline();
2673 for (names, 1..) |name_nts, value| {2073 for (names, 1..) |name_nts, value| {
2674 const name = name_nts.toSlice(ip);2074 try w.writeByte(' ');
2675 max_name_len = @max(name.len, max_name_len);2075 try renderErrorName(w, name_nts.toSlice(ip));
2676 const err_val = try pt.intern(.{ .err = .{2076 try w.print(" = {d}u,\n", .{value});
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();
2683 }2077 }
2684 try o.outdent();2078 try w.writeAll("};\n");
2685 try w.writeAll("};");2079 }
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 } });
27082080
2709 try w.writeAll("static ");2081 for (names) |name_nts| {
2710 try o.dg.renderTypeAndName(2082 const name = name_nts.toSlice(ip);
2711 w,2083 try w.print(
2712 name_ty,2084 "static uint8_t const zig_errorName_{f}[] = {f};\n",
2713 .{ .identifier = identifier },2085 .{ fmtIdentUnsolo(name), fmtStringLiteral(name, 0) },
2714 Const,
2715 .none,
2716 .complete,
2717 );2086 );
2718 try w.writeAll(" = ");
2719 try o.dg.renderValue(w, Value.fromInterned(name_val), .StaticInitializer);
2720 try w.writeByte(';');
2721 try o.newline();
2722 }2087 }
27232088
2724 const name_array_ty = try pt.arrayType(.{2089 try w.print(
2725 .len = 1 + names.len,2090 "static {s} const zig_errorName[{d}] = {{",
2726 .child = .slice_const_u8_sentinel_0_type,2091 .{ slice_const_u8_sentinel_0_type_name, names.len },
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,
2737 );2092 );
2738 try w.writeAll(" = {");2093 if (names.len > 0) try w.writeByte('\n');
2739 for (names, 1..) |name_nts, val| {2094 for (names) |name_nts| {
2740 const name = name_nts.toSlice(ip);2095 const name = name_nts.toSlice(ip);
2741 if (val > 1) try w.writeAll(", ");2096 try w.print(
2742 try w.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{2097 " {{zig_errorName_{f},{d}}},\n",
2743 fmtIdentUnsolo(name),2098 .{ fmtIdentUnsolo(name), name.len },
2744 try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, name.len), .StaticInitializer),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,
2745 });2143 });
2746 }2144 }
2747 try w.writeAll("};");2145 try w.writeAll(
2748 try o.newline();2146 \\ }
2147 \\ zig_unreachable();
2148 \\}
2149 \\
2150 );
2749}2151}
27502152
2751pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFnMap.Entry) Error!void {2153pub fn genLazyCallModifierFn(
2752 const pt = o.dg.pt;2154 dg: *DeclGen,
2753 const zcu = pt.zcu;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;
2754 const ip = &zcu.intern_pool;2160 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}: {{", .{2162 const fn_val = zcu.navValue(fn_nav);
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");
28322163
2833 try w.print("zig_{s} ", .{@tagName(key)});2164 try w.print("static zig_{t} ", .{kind});
2834 try o.dg.renderFunctionSignature(w, fn_val, .none, .complete, .{2165 try dg.renderFunctionSignature(w, fn_val, .none, .definition, switch (kind) {
2835 .fmt_ctype_pool_string = fn_name,2166 .never_tail => .{ .nav_never_tail = fn_nav },
2836 });2167 .never_inline => .{ .nav_never_inline = fn_nav },
2837 try w.writeAll(" {");2168 });
2838 o.indent();2169 try w.writeAll(" {\n return ");
2839 try o.newline();2170 try renderNavName(w, fn_nav, ip);
2840 try w.writeAll("return ");2171 try w.writeByte('(');
2841 try o.dg.renderNavName(w, fn_nav_index);2172 {
2842 try w.writeByte('(');2173 const func_type = ip.indexToKey(fn_val.typeOf(zcu).toIntern()).func_type;
2843 for (0..fn_info.param_ctypes.len) |arg| {2174 var c_param_index: u32 = 0;
2844 if (arg > 0) try w.writeAll(", ");2175 for (func_type.param_types.get(ip)) |param_ty_ip| {
2845 try w.print("a{d}", .{arg});2176 const param_ty: Type = .fromInterned(param_ty_ip);
2846 }2177 if (!param_ty.hasRuntimeBits(zcu)) continue;
2847 try w.writeAll(");");2178 if (c_param_index != 0) try w.writeAll(", ");
2848 try o.newline();2179 try w.print("a{d}", .{c_param_index});
2849 try o.outdent();2180 c_param_index += 1;
2850 try w.writeByte('}');2181 }
2851 try o.newline();
2852 },
2853 }2182 }
2183 try w.writeAll(");\n}\n");
2854}2184}
28552185
2856pub fn generate(2186pub fn generate(
...@@ -2869,110 +2199,109 @@ pub fn generate(...@@ -2869,110 +2199,109 @@ pub fn generate(
28692199
2870 const func = zcu.funcInfo(func_index);2200 const func = zcu.funcInfo(func_index);
28712201
2202 var arena: std.heap.ArenaAllocator = .init(gpa);
2203 defer arena.deinit();
2204
2872 var function: Function = .{2205 var function: Function = .{
2873 .value_map = .init(gpa),2206 .value_map = .init(gpa),
2874 .air = air.*,2207 .air = air.*,
2875 .liveness = liveness.*.?,2208 .liveness = liveness.*.?,
2876 .func_index = func_index,2209 .func_index = func_index,
2877 .object = .{2210 .dg = .{
2878 .dg = .{2211 .gpa = gpa,
2879 .gpa = gpa,2212 .arena = arena.allocator(),
2880 .pt = pt,2213 .pt = pt,
2881 .mod = zcu.navFileScope(func.owner_nav).mod.?,2214 .mod = zcu.navFileScope(func.owner_nav).mod.?,
2882 .error_msg = null,2215 .error_msg = null,
2883 .pass = .{ .nav = func.owner_nav },2216 .owner_nav = func.owner_nav.toOptional(),
2884 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,2217 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
2885 .expected_block = null,2218 .expected_block = null,
2886 .fwd_decl = .init(gpa),2219 .ctype_deps = .empty,
2887 .ctype_pool = .empty,2220 .uavs = .empty,
2888 .scratch = .empty,
2889 .uavs = .empty,
2890 },
2891 .code_header = .init(gpa),
2892 .code = .init(gpa),
2893 .indent_counter = 0,
2894 },2221 },
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,
2896 };2227 };
2897 defer {2228 defer {
2898 function.object.code_header.deinit();2229 function.code.deinit();
2899 function.object.code.deinit();2230 function.dg.ctype_deps.deinit(gpa);
2900 function.object.dg.fwd_decl.deinit();2231 function.dg.uavs.deinit(gpa);
2901 function.object.dg.ctype_pool.deinit(gpa);
2902 function.object.dg.scratch.deinit(gpa);
2903 function.object.dg.uavs.deinit(gpa);
2904 function.deinit();2232 function.deinit();
2905 }2233 }
2906 try function.object.dg.ctype_pool.init(gpa);
29072234
2908 genFunc(&function) catch |err| switch (err) {2235 var fwd_decl: Writer.Allocating = .init(gpa);
2909 error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?),2236 defer fwd_decl.deinit();
2910 error.OutOfMemory => return error.OutOfMemory,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.?),
2911 error.WriteFailed => return error.OutOfMemory,2243 error.WriteFailed => return error.OutOfMemory,
2244 error.OutOfMemory => |e| return e,
2912 };2245 };
29132246
2914 var mir: Mir = .{2247 var mir: Mir = .{
2915 .uavs = .empty,
2916 .code = &.{},
2917 .code_header = &.{},
2918 .fwd_decl = &.{},2248 .fwd_decl = &.{},
2919 .ctype_pool = .empty,2249 .code_header = &.{},
2920 .lazy_fns = .empty,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(),
2921 };2256 };
2922 errdefer mir.deinit(gpa);2257 errdefer mir.deinit(gpa);
2923 mir.uavs = function.object.dg.uavs.move();2258 mir.fwd_decl = try fwd_decl.toOwnedSlice();
2924 mir.code_header = try function.object.code_header.toOwnedSlice();2259 mir.code_header = try code_header.toOwnedSlice();
2925 mir.code = try function.object.code.toOwnedSlice();2260 mir.code = try function.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();
2929 return mir;2261 return mir;
2930}2262}
29312263
2932pub fn genFunc(f: *Function) Error!void {2264pub fn genFunc(f: *Function, fwd_decl_writer: *Writer, header_writer: *Writer) Error!void {
2933 const tracy = trace(@src());2265 const tracy = trace(@src());
2934 defer tracy.end();2266 defer tracy.end();
29352267
2936 const o = &f.object;2268 const zcu = f.dg.pt.zcu;
2937 const zcu = o.dg.pt.zcu;
2938 const ip = &zcu.intern_pool;2269 const ip = &zcu.intern_pool;
2939 const gpa = o.dg.gpa;2270 const gpa = f.dg.gpa;
2940 const nav_index = o.dg.pass.nav;2271 const nav_index = f.dg.owner_nav.unwrap().?;
2941 const nav_val = zcu.navValue(nav_index);2272 const nav_val = zcu.navValue(nav_index);
2942 const nav = ip.getNav(nav_index);2273 const nav = ip.getNav(nav_index);
29432274
2944 const fwd = &o.dg.fwd_decl.writer;2275 try fwd_decl_writer.writeAll("static ");
2945 try fwd.writeAll("static ");2276 try f.dg.renderFunctionSignature(
2946 try o.dg.renderFunctionSignature(2277 fwd_decl_writer,
2947 fwd,
2948 nav_val,2278 nav_val,
2949 nav.status.fully_resolved.alignment,2279 nav.status.fully_resolved.alignment,
2950 .forward,2280 .forward_decl,
2951 .{ .nav = nav_index },2281 .{ .nav = nav_index },
2952 );2282 );
2953 try fwd.writeAll(";\n");2283 try fwd_decl_writer.writeAll(";\n");
29542284
2955 const ch = &o.code_header.writer;
2956 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|2285 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|
2957 try ch.print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)});2286 try header_writer.print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)});
2958 try o.dg.renderFunctionSignature(2287 try f.dg.renderFunctionSignature(
2959 ch,2288 header_writer,
2960 nav_val,2289 nav_val,
2961 .none,2290 .none,
2962 .complete,2291 .definition,
2963 .{ .nav = nav_index },2292 .{ .nav = nav_index },
2964 );2293 );
2965 try ch.writeAll(" {\n ");2294 try header_writer.writeAll(" {\n ");
29662295
2967 f.free_locals_map.clearRetainingCapacity();2296 f.free_locals_map.clearRetainingCapacity();
29682297
2969 const main_body = f.air.getMainBody();2298 const main_body = f.air.getMainBody();
2970 o.indent();2299 f.indent();
2971 try genBodyResolveState(f, undefined, &.{}, main_body, true);2300 try genBodyResolveState(f, undefined, &.{}, main_body, true);
2972 try o.outdent();2301 try f.outdent();
2973 try o.code.writer.writeByte('}');2302 try f.code.writer.writeByte('}');
2974 try o.newline();2303 try f.newline();
2975 if (o.dg.expected_block) |_|2304 if (f.dg.expected_block) |_|
2976 return f.fail("runtime code not allowed in naked function", .{});2305 return f.fail("runtime code not allowed in naked function", .{});
29772306
2978 // Take advantage of the free_locals map to bucket locals per type. All2307 // Take advantage of the free_locals map to bucket locals per type. All
...@@ -2986,155 +2315,204 @@ pub fn genFunc(f: *Function) Error!void {...@@ -2986,155 +2315,204 @@ pub fn genFunc(f: *Function) Error!void {
2986 if (!should_emit) continue;2315 if (!should_emit) continue;
2987 const local = f.locals.items[local_index];2316 const local = f.locals.items[local_index];
2988 log.debug("inserting local {d} into free_locals", .{local_index});2317 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);
2990 if (!gop.found_existing) gop.value_ptr.* = .{};2319 if (!gop.found_existing) gop.value_ptr.* = .{};
2991 try gop.value_ptr.putNoClobber(gpa, local_index, {});2320 try gop.value_ptr.putNoClobber(gpa, local_index, {});
2992 }2321 }
29932322
2994 const SortContext = struct {2323 const SortContext = struct {
2324 zcu: *const Zcu,
2995 keys: []const LocalType,2325 keys: []const LocalType,
29962326
2997 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {2327 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
2998 const lhs_ty = ctx.keys[lhs_index];2328 const lhs = ctx.keys[lhs_index];
2999 const rhs_ty = ctx.keys[rhs_index];2329 const rhs = ctx.keys[rhs_index];
3000 return lhs_ty.alignas.order(rhs_ty.alignas).compare(.gt);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);
3001 }2339 }
3002 };2340 };
3003 free_locals.sort(SortContext{ .keys = free_locals.keys() });2341 free_locals.sort(SortContext{
2342 .zcu = zcu,
2343 .keys = free_locals.keys(),
2344 });
30042345
3005 for (free_locals.values()) |list| {2346 for (free_locals.values()) |list| {
3006 for (list.keys()) |local_index| {2347 for (list.keys()) |local_index| {
3007 const local = f.locals.items[local_index];2348 const local = f.locals.items[local_index];
3008 try o.dg.renderCTypeAndName(ch, local.ctype, .{ .local = local_index }, .{}, local.flags.alignas);2349 try f.dg.renderTypeAndName(header_writer, local.type, .{ .local = local_index }, .{}, local.alignment);
3009 try ch.writeAll(";\n ");2350 try header_writer.writeAll(";\n ");
3010 }2351 }
3011 }2352 }
3012}2353}
30132354
3014pub fn genDecl(o: *Object) Error!void {2355pub fn genDecl(dg: *DeclGen, w: *Writer) Error!void {
3015 const tracy = trace(@src());2356 const tracy = trace(@src());
3016 defer tracy.end();2357 defer tracy.end();
30172358
3018 const pt = o.dg.pt;2359 const pt = dg.pt;
3019 const zcu = pt.zcu;2360 const zcu = pt.zcu;
3020 const ip = &zcu.intern_pool;2361 const ip = &zcu.intern_pool;
3021 const nav = ip.getNav(o.dg.pass.nav);2362 const nav = ip.getNav(dg.owner_nav.unwrap().?);
3022 const nav_ty: Type = .fromInterned(nav.typeOf(ip));2363 const nav_ty: Type = .fromInterned(nav.typeOf(ip));
30232364
3024 if (!nav_ty.hasRuntimeBits(zcu)) return;2365 const is_const: bool, const is_threadlocal: bool, const init_val: Value = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
3025 switch (ip.indexToKey(nav.status.fully_resolved.val)) {2366 else => .{ true, false, .fromInterned(nav.status.fully_resolved.val) },
3026 .@"extern" => |@"extern"| {2367 .variable => |v| .{ false, v.is_threadlocal, .fromInterned(v.init) },
3027 if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{2368 .@"extern" => return,
3028 .is_const = @"extern".is_const,2369 };
3029 .is_threadlocal = @"extern".is_threadlocal,
3030 .linkage = @"extern".linkage,
3031 .visibility = @"extern".visibility,
3032 });
30332370
3034 const fwd = &o.dg.fwd_decl.writer;2371 if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s| {
3035 try fwd.writeAll("zig_extern ");2372 try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});
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 ),
3079 }2373 }
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 });
3080}2387}
2388pub fn genDeclFwd(dg: *DeclGen, w: *Writer) Error!void {
2389 const tracy = trace(@src());
2390 defer tracy.end();
30812391
3082pub fn genDeclValue(2392 const pt = dg.pt;
3083 o: *Object,2393 const zcu = pt.zcu;
3084 val: Value,2394 const ip = &zcu.intern_pool;
3085 decl_c_value: CValue,2395 const nav = ip.getNav(dg.owner_nav.unwrap().?);
3086 alignment: Alignment,2396 const nav_ty: Type = .fromInterned(nav.typeOf(ip));
3087 @"linksection": InternPool.OptionalNullTerminatedString,
3088) Error!void {
3089 const zcu = o.dg.pt.zcu;
3090 const ty = val.typeOf(zcu);
30912397
3092 const fwd = &o.dg.fwd_decl.writer;2398 const is_const: bool, const is_threadlocal: bool, const init_val: Value = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
3093 try fwd.writeAll("static ");2399 else => .{ true, false, .fromInterned(nav.status.fully_resolved.val) },
3094 try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete);2400 .variable => |v| .{ false, v.is_threadlocal, .fromInterned(v.init) },
3095 try fwd.writeAll(";\n");
30962401
3097 const w = &o.code.writer;2402 .@"extern" => |@"extern"| switch (nav_ty.zigTypeTag(zcu)) {
3098 if (@"linksection".toSlice(&zcu.intern_pool)) |s|2403 .@"fn" => {
3099 try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});2404 try w.writeAll("zig_extern ");
3100 try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete);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);
3101 try w.writeAll(" = ");2466 try w.writeAll(" = ");
3102 try o.dg.renderValue(w, val, .StaticInitializer);2467 try dg.renderValue(w, options.init_val, .static_initializer);
3103 try w.writeByte(';');2468 try w.writeAll(";\n");
3104 try o.newline();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");
3105}2484}
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 {
3108 const zcu = dg.pt.zcu;2487 const zcu = dg.pt.zcu;
3109 const ip = &zcu.intern_pool;2488 const ip = &zcu.intern_pool;
3110 const fwd = &dg.fwd_decl.writer;
31112489
3112 const main_name = export_indices[0].ptr(zcu).opts.name;2490 const main_name = export_indices[0].ptr(zcu).opts.name;
3113 try fwd.writeAll("#define ");2491 try w.writeAll("#define ");
3114 switch (exported) {2492 switch (exported) {
3115 .nav => |nav| try dg.renderNavName(fwd, nav),2493 .nav => |nav| try renderNavName(w, nav, ip),
3116 .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)),2494 .uav => |uav| try renderUavName(w, Value.fromInterned(uav)),
3117 }2495 }
3118 try fwd.writeByte(' ');2496 try w.writeByte(' ');
3119 try fwd.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))});2497 try w.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))});
3120 try fwd.writeByte('\n');2498 try w.writeByte('\n');
31212499
3122 const exported_val = exported.getValue(zcu);2500 const exported_val = exported.getValue(zcu);
3123 if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| {2501 if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| {
3124 const @"export" = export_index.ptr(zcu);2502 const @"export" = export_index.ptr(zcu);
3125 try fwd.writeAll("zig_extern ");2503 try w.writeAll("zig_extern ");
3126 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn ");2504 if (@"export".opts.linkage == .weak) try w.writeAll("zig_weak_linkage_fn ");
3127 try dg.renderFunctionSignature(2505 try dg.renderFunctionSignature(
3128 fwd,2506 w,
3129 exported.getValue(zcu),2507 exported.getValue(zcu),
3130 exported.getAlign(zcu),2508 exported.getAlign(zcu),
3131 .forward,2509 .forward_decl,
3132 .{ .@"export" = .{2510 .{ .@"export" = .{
3133 .main_name = main_name,2511 .main_name = main_name,
3134 .extern_name = @"export".opts.name,2512 .extern_name = @"export".opts.name,
3135 } },2513 } },
3136 );2514 );
3137 try fwd.writeAll(";\n");2515 try w.writeAll(";\n");
3138 };2516 };
3139 const is_const = switch (ip.indexToKey(exported_val.toIntern())) {2517 const is_const = switch (ip.indexToKey(exported_val.toIntern())) {
3140 .func => unreachable,2518 .func => unreachable,
...@@ -3144,39 +2522,38 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const...@@ -3144,39 +2522,38 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
3144 };2522 };
3145 for (export_indices) |export_index| {2523 for (export_indices) |export_index| {
3146 const @"export" = export_index.ptr(zcu);2524 const @"export" = export_index.ptr(zcu);
3147 try fwd.writeAll("zig_extern ");2525 try w.writeAll("zig_extern ");
3148 if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage ");2526 if (@"export".opts.linkage == .weak) try w.writeAll("zig_weak_linkage ");
3149 if (@"export".opts.section.toSlice(ip)) |s| try fwd.print("zig_linksection({f}) ", .{2527 if (@"export".opts.section.toSlice(ip)) |s| try w.print("zig_linksection({f}) ", .{
3150 fmtStringLiteral(s, null),2528 fmtStringLiteral(s, null),
3151 });2529 });
3152 const extern_name = @"export".opts.name.toSlice(ip);2530 const extern_name = @"export".opts.name.toSlice(ip);
3153 const is_mangled = isMangledIdent(extern_name, true);2531 const is_mangled = isMangledIdent(extern_name, true);
3154 const is_export = @"export".opts.name != main_name;2532 const is_export = @"export".opts.name != main_name;
3155 try dg.renderTypeAndName(2533 try dg.renderTypeAndName(
3156 fwd,2534 w,
3157 exported.getValue(zcu).typeOf(zcu),2535 exported.getValue(zcu).typeOf(zcu),
3158 .{ .identifier = extern_name },2536 .{ .identifier = extern_name },
3159 CQualifiers.init(.{ .@"const" = is_const }),2537 .{ .@"const" = is_const },
3160 exported.getAlign(zcu),2538 exported.getAlign(zcu),
3161 .complete,
3162 );2539 );
3163 if (is_mangled and is_export) {2540 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})", .{
3165 fmtIdentSolo(extern_name),2542 fmtIdentSolo(extern_name),
3166 fmtStringLiteral(extern_name, null),2543 fmtStringLiteral(extern_name, null),
3167 fmtStringLiteral(main_name.toSlice(ip), null),2544 fmtStringLiteral(main_name.toSlice(ip), null),
3168 });2545 });
3169 } else if (is_mangled) {2546 } else if (is_mangled) {
3170 try fwd.print(" zig_mangled({f}, {f})", .{2547 try w.print(" zig_mangled({f}, {f})", .{
3171 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),2548 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),
3172 });2549 });
3173 } else if (is_export) {2550 } else if (is_export) {
3174 try fwd.print(" zig_export({f}, {f})", .{2551 try w.print(" zig_export({f}, {f})", .{
3175 fmtStringLiteral(main_name.toSlice(ip), null),2552 fmtStringLiteral(main_name.toSlice(ip), null),
3176 fmtStringLiteral(extern_name, null),2553 fmtStringLiteral(extern_name, null),
3177 });2554 });
3178 }2555 }
3179 try fwd.writeAll(";\n");2556 try w.writeAll(";\n");
3180 }2557 }
3181}2558}
31822559
...@@ -3185,15 +2562,15 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const...@@ -3185,15 +2562,15 @@ pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const
3185/// have been added to `free_locals_map`. For a version of this function that restores this state,2562/// have been added to `free_locals_map`. For a version of this function that restores this state,
3186/// see `genBodyResolveState`.2563/// see `genBodyResolveState`.
3187fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {2564fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
3188 const w = &f.object.code.writer;2565 const w = &f.code.writer;
3189 if (body.len == 0) {2566 if (body.len == 0) {
3190 try w.writeAll("{}");2567 try w.writeAll("{}");
3191 } else {2568 } else {
3192 try w.writeByte('{');2569 try w.writeByte('{');
3193 f.object.indent();2570 f.indent();
3194 try f.object.newline();2571 try f.newline();
3195 try genBodyInner(f, body);2572 try genBodyInner(f, body);
3196 try f.object.outdent();2573 try f.outdent();
3197 try w.writeByte('}');2574 try w.writeByte('}');
3198 }2575 }
3199}2576}
...@@ -3207,13 +2584,13 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -3207,13 +2584,13 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) Error!void {
3207fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void {2584fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) Error!void {
3208 if (body.len == 0) {2585 if (body.len == 0) {
3209 // Don't go to the expense of cloning everything!2586 // 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("{}");
3211 return;2588 return;
3212 }2589 }
32132590
3214 // TODO: we can probably avoid the copies in some other common cases too.2591 // 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
3218 // Save the original value_map and free_locals_map so that we can restore them after the body.2595 // Save the original value_map and free_locals_map so that we can restore them after the body.
3219 var old_value_map = try f.value_map.clone();2596 var old_value_map = try f.value_map.clone();
...@@ -3254,13 +2631,13 @@ fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []con...@@ -3254,13 +2631,13 @@ fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []con
3254}2631}
32552632
3256fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {2633fn 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;
3258 const ip = &zcu.intern_pool;2635 const ip = &zcu.intern_pool;
3259 const air_tags = f.air.instructions.items(.tag);2636 const air_tags = f.air.instructions.items(.tag);
3260 const air_datas = f.air.instructions.items(.data);2637 const air_datas = f.air.instructions.items(.data);
32612638
3262 for (body) |inst| {2639 for (body) |inst| {
3263 if (f.object.dg.expected_block) |_|2640 if (f.dg.expected_block) |_|
3264 return f.fail("runtime code not allowed in naked function", .{});2641 return f.fail("runtime code not allowed in naked function", .{});
3265 if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip))2642 if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip))
3266 continue;2643 continue;
...@@ -3529,8 +2906,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {...@@ -3529,8 +2906,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) Error!void {
3529 .ret => return airRet(f, inst, false),2906 .ret => return airRet(f, inst, false),
3530 .ret_safe => return airRet(f, inst, false), // TODO2907 .ret_safe => return airRet(f, inst, false), // TODO
3531 .ret_load => return airRet(f, inst, true),2908 .ret_load => return airRet(f, inst, true),
3532 .trap => return airTrap(f, &f.object.code.writer),2909 .trap => return airTrap(f),
3533 .unreach => return airUnreach(&f.object),2910 .unreach => return airUnreach(f),
35342911
3535 // Instructions which may be `noreturn`.2912 // Instructions which may be `noreturn`.
3536 .block => res: {2913 .block => res: {
...@@ -3573,21 +2950,21 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [...@@ -3573,21 +2950,21 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [
3573 const operand = try f.resolveInst(ty_op.operand);2950 const operand = try f.resolveInst(ty_op.operand);
3574 try reap(f, inst, &.{ty_op.operand});2951 try reap(f, inst, &.{ty_op.operand});
35752952
3576 const w = &f.object.code.writer;2953 const w = &f.code.writer;
3577 const local = try f.allocLocal(inst, inst_ty);2954 const local = try f.allocLocal(inst, inst_ty);
3578 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));2955 try f.writeCValue(w, local, .other);
3579 try f.writeCValue(w, local, .Other);2956 try w.writeAll(" = ");
3580 try a.assign(f, w);
3581 if (is_ptr) {2957 if (is_ptr) {
3582 try w.writeByte('&');2958 try w.writeByte('&');
3583 try f.writeCValueDerefMember(w, operand, .{ .identifier = field_name });2959 try f.writeCValueDerefMember(w, operand, .{ .identifier = field_name });
3584 } else try f.writeCValueMember(w, operand, .{ .identifier = field_name });2960 } else try f.writeCValueMember(w, operand, .{ .identifier = field_name });
3585 try a.end(f, w);2961 try w.writeByte(';');
2962 try f.newline();
3586 return local;2963 return local;
3587}2964}
35882965
3589fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {2966fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3590 const zcu = f.object.dg.pt.zcu;2967 const zcu = f.dg.pt.zcu;
3591 const inst_ty = f.typeOfIndex(inst);2968 const inst_ty = f.typeOfIndex(inst);
3592 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;2969 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3593 assert(inst_ty.hasRuntimeBits(zcu));2970 assert(inst_ty.hasRuntimeBits(zcu));
...@@ -3596,21 +2973,24 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3596,21 +2973,24 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3596 const index = try f.resolveInst(bin_op.rhs);2973 const index = try f.resolveInst(bin_op.rhs);
3597 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });2974 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
35982975
3599 const w = &f.object.code.writer;2976 const w = &f.code.writer;
3600 const local = try f.allocLocal(inst, inst_ty);2977 const local = try f.allocLocal(inst, inst_ty);
3601 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));2978 try f.writeCValue(w, local, .other);
3602 try f.writeCValue(w, local, .Other);2979 try w.writeAll(" = ");
3603 try a.assign(f, w);2980 switch (f.typeOf(bin_op.lhs).ptrSize(zcu)) {
3604 try f.writeCValue(w, ptr, .Other);2981 .one => try f.writeCValueDerefMember(w, ptr, .{ .identifier = "array" }),
2982 .many, .c => try f.writeCValue(w, ptr, .other),
2983 .slice => unreachable,
2984 }
3605 try w.writeByte('[');2985 try w.writeByte('[');
3606 try f.writeCValue(w, index, .Other);2986 try f.writeCValue(w, index, .other);
3607 try w.writeByte(']');2987 try w.writeAll("];");
3608 try a.end(f, w);2988 try f.newline();
3609 return local;2989 return local;
3610}2990}
36112991
3612fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {2992fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3613 const pt = f.object.dg.pt;2993 const pt = f.dg.pt;
3614 const zcu = pt.zcu;2994 const zcu = pt.zcu;
3615 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;2995 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3616 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;2996 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 {...@@ -3623,28 +3003,26 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3623 const index = try f.resolveInst(bin_op.rhs);3003 const index = try f.resolveInst(bin_op.rhs);
3624 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3004 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36253005
3626 const w = &f.object.code.writer;3006 const w = &f.code.writer;
3627 const local = try f.allocLocal(inst, inst_ty);3007 const local = try f.allocLocal(inst, inst_ty);
3628 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3008 try f.writeCValue(w, local, .other);
3629 try f.writeCValue(w, local, .Other);3009 try w.writeAll(" = ");
3630 try a.assign(f, w);
3631 try w.writeByte('(');
3632 try f.renderType(w, inst_ty);
3633 try w.writeByte(')');
3634 try w.writeByte('&');3010 try w.writeByte('&');
3635 if (ptr_ty.ptrSize(zcu) == .one) {3011 if (ptr_ty.ptrSize(zcu) == .one) {
3636 // It's a pointer to an array, so we need to de-reference.3012 // `*[n]T` was turned into a pointer to `struct { T array[n]; }`
3637 try f.writeCValueDeref(w, ptr);3013 try f.writeCValueDerefMember(w, ptr, .{ .identifier = "array" });
3638 } else try f.writeCValue(w, ptr, .Other);3014 } else {
3015 try f.writeCValue(w, ptr, .other);
3016 }
3639 try w.writeByte('[');3017 try w.writeByte('[');
3640 try f.writeCValue(w, index, .Other);3018 try f.writeCValue(w, index, .other);
3641 try w.writeByte(']');3019 try w.writeAll("];");
3642 try a.end(f, w);3020 try f.newline();
3643 return local;3021 return local;
3644}3022}
36453023
3646fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {3024fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3647 const zcu = f.object.dg.pt.zcu;3025 const zcu = f.dg.pt.zcu;
3648 const inst_ty = f.typeOfIndex(inst);3026 const inst_ty = f.typeOfIndex(inst);
3649 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3027 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3650 assert(inst_ty.hasRuntimeBits(zcu));3028 assert(inst_ty.hasRuntimeBits(zcu));
...@@ -3653,21 +3031,20 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3653,21 +3031,20 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3653 const index = try f.resolveInst(bin_op.rhs);3031 const index = try f.resolveInst(bin_op.rhs);
3654 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3032 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36553033
3656 const w = &f.object.code.writer;3034 const w = &f.code.writer;
3657 const local = try f.allocLocal(inst, inst_ty);3035 const local = try f.allocLocal(inst, inst_ty);
3658 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3036 try f.writeCValue(w, local, .other);
3659 try f.writeCValue(w, local, .Other);3037 try w.writeAll(" = ");
3660 try a.assign(f, w);
3661 try f.writeCValueMember(w, slice, .{ .identifier = "ptr" });3038 try f.writeCValueMember(w, slice, .{ .identifier = "ptr" });
3662 try w.writeByte('[');3039 try w.writeByte('[');
3663 try f.writeCValue(w, index, .Other);3040 try f.writeCValue(w, index, .other);
3664 try w.writeByte(']');3041 try w.writeAll("];");
3665 try a.end(f, w);3042 try f.newline();
3666 return local;3043 return local;
3667}3044}
36683045
3669fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {3046fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3670 const pt = f.object.dg.pt;3047 const pt = f.dg.pt;
3671 const zcu = pt.zcu;3048 const zcu = pt.zcu;
3672 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3049 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3673 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;3050 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 {...@@ -3681,22 +3058,21 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3681 const index = try f.resolveInst(bin_op.rhs);3058 const index = try f.resolveInst(bin_op.rhs);
3682 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3059 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
36833060
3684 const w = &f.object.code.writer;3061 const w = &f.code.writer;
3685 const local = try f.allocLocal(inst, inst_ty);3062 const local = try f.allocLocal(inst, inst_ty);
3686 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3063 try f.writeCValue(w, local, .other);
3687 try f.writeCValue(w, local, .Other);3064 try w.writeAll(" = ");
3688 try a.assign(f, w);
3689 try w.writeByte('&');3065 try w.writeByte('&');
3690 try f.writeCValueMember(w, slice, .{ .identifier = "ptr" });3066 try f.writeCValueMember(w, slice, .{ .identifier = "ptr" });
3691 try w.writeByte('[');3067 try w.writeByte('[');
3692 try f.writeCValue(w, index, .Other);3068 try f.writeCValue(w, index, .other);
3693 try w.writeByte(']');3069 try w.writeAll("];");
3694 try a.end(f, w);3070 try f.newline();
3695 return local;3071 return local;
3696}3072}
36973073
3698fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {3074fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3699 const zcu = f.object.dg.pt.zcu;3075 const zcu = f.dg.pt.zcu;
3700 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3076 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3701 const inst_ty = f.typeOfIndex(inst);3077 const inst_ty = f.typeOfIndex(inst);
3702 assert(inst_ty.hasRuntimeBits(zcu));3078 assert(inst_ty.hasRuntimeBits(zcu));
...@@ -3705,32 +3081,28 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3705,32 +3081,28 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
3705 const index = try f.resolveInst(bin_op.rhs);3081 const index = try f.resolveInst(bin_op.rhs);
3706 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3082 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
37073083
3708 const w = &f.object.code.writer;3084 const w = &f.code.writer;
3709 const local = try f.allocLocal(inst, inst_ty);3085 const local = try f.allocLocal(inst, inst_ty);
3710 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));3086 try f.writeCValue(w, local, .other);
3711 try f.writeCValue(w, local, .Other);3087 try w.writeAll(" = ");
3712 try a.assign(f, w);3088 try f.writeCValueMember(w, array, .{ .identifier = "array" });
3713 try f.writeCValue(w, array, .Other);
3714 try w.writeByte('[');3089 try w.writeByte('[');
3715 try f.writeCValue(w, index, .Other);3090 try f.writeCValue(w, index, .other);
3716 try w.writeByte(']');3091 try w.writeAll("];");
3717 try a.end(f, w);3092 try f.newline();
3718 return local;3093 return local;
3719}3094}
37203095
3721fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {3096fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
3722 const pt = f.object.dg.pt;3097 const pt = f.dg.pt;
3723 const zcu = pt.zcu;3098 const zcu = pt.zcu;
3724 const inst_ty = f.typeOfIndex(inst);3099 const inst_ty = f.typeOfIndex(inst);
3725 const elem_ty = inst_ty.childType(zcu);3100 const elem_ty = inst_ty.childType(zcu);
3726 if (!elem_ty.hasRuntimeBits(zcu)) return .{ .undef = inst_ty };3101 if (!elem_ty.hasRuntimeBits(zcu)) return .{ .undef = inst_ty };
37273102
3728 const local = try f.allocLocalValue(.{3103 const local = try f.allocLocalValue(.{
3729 .ctype = try f.ctypeFromType(elem_ty, .complete),3104 .type = elem_ty,
3730 .alignas = CType.AlignAs.fromAlignment(.{3105 .alignment = inst_ty.ptrInfo(zcu).flags.alignment,
3731 .@"align" = inst_ty.ptrInfo(zcu).flags.alignment,
3732 .abi = elem_ty.abiAlignment(zcu),
3733 }),
3734 });3106 });
3735 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });3107 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
3736 try f.allocs.put(zcu.gpa, local.new_local, true);3108 try f.allocs.put(zcu.gpa, local.new_local, true);
...@@ -3741,11 +3113,11 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3741,11 +3113,11 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
3741 // For packed aggregates, we zero-initialize to try and work around a design flaw3113 // For packed aggregates, we zero-initialize to try and work around a design flaw
3742 // related to how `packed`, `undefined`, and RLS interact. See comment in `airStore`3114 // related to how `packed`, `undefined`, and RLS interact. See comment in `airStore`
3743 // for details.3115 // for details.
3744 const w = &f.object.code.writer;3116 const w = &f.code.writer;
3745 try w.print("memset(&t{d}, 0x00, sizeof(", .{local.new_local});3117 try w.print("memset(&t{d}, 0x00, sizeof(", .{local.new_local});
3746 try f.renderType(w, elem_ty);3118 try f.renderType(w, elem_ty);
3747 try w.writeAll("));");3119 try w.writeAll("));");
3748 try f.object.newline();3120 try f.newline();
3749 },3121 },
3750 .auto, .@"extern" => {},3122 .auto, .@"extern" => {},
3751 },3123 },
...@@ -3756,18 +3128,15 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3756,18 +3128,15 @@ fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
3756}3128}
37573129
3758fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {3130fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3759 const pt = f.object.dg.pt;3131 const pt = f.dg.pt;
3760 const zcu = pt.zcu;3132 const zcu = pt.zcu;
3761 const inst_ty = f.typeOfIndex(inst);3133 const inst_ty = f.typeOfIndex(inst);
3762 const elem_ty = inst_ty.childType(zcu);3134 const elem_ty = inst_ty.childType(zcu);
3763 if (!elem_ty.hasRuntimeBits(zcu)) return .{ .undef = inst_ty };3135 if (!elem_ty.hasRuntimeBits(zcu)) return .{ .undef = inst_ty };
37643136
3765 const local = try f.allocLocalValue(.{3137 const local = try f.allocLocalValue(.{
3766 .ctype = try f.ctypeFromType(elem_ty, .complete),3138 .type = elem_ty,
3767 .alignas = CType.AlignAs.fromAlignment(.{3139 .alignment = inst_ty.ptrInfo(zcu).flags.alignment,
3768 .@"align" = inst_ty.ptrInfo(zcu).flags.alignment,
3769 .abi = elem_ty.abiAlignment(zcu),
3770 }),
3771 });3140 });
3772 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });3141 log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
3773 try f.allocs.put(zcu.gpa, local.new_local, true);3142 try f.allocs.put(zcu.gpa, local.new_local, true);
...@@ -3778,11 +3147,11 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3778,11 +3147,11 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3778 // For packed aggregates, we zero-initialize to try and work around a design flaw3147 // For packed aggregates, we zero-initialize to try and work around a design flaw
3779 // related to how `packed`, `undefined`, and RLS interact. See comment in `airStore`3148 // related to how `packed`, `undefined`, and RLS interact. See comment in `airStore`
3780 // for details.3149 // for details.
3781 const w = &f.object.code.writer;3150 const w = &f.code.writer;
3782 try w.print("memset(&t{d}, 0x00, sizeof(", .{local.new_local});3151 try w.print("memset(&t{d}, 0x00, sizeof(", .{local.new_local});
3783 try f.renderType(w, elem_ty);3152 try f.renderType(w, elem_ty);
3784 try w.writeAll("));");3153 try w.writeAll("));");
3785 try f.object.newline();3154 try f.newline();
3786 },3155 },
3787 .auto, .@"extern" => {},3156 .auto, .@"extern" => {},
3788 },3157 },
...@@ -3793,24 +3162,18 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3793,24 +3162,18 @@ fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
3793}3162}
37943163
3795fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {3164fn 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
3799 const i = f.next_arg_index;3165 const i = f.next_arg_index;
3800 f.next_arg_index += 1;3166 f.next_arg_index += 1;
3801 const result: CValue = if (inst_ctype.eql(try f.ctypeFromType(inst_ty, .complete)))3167 const result: CValue = .{ .arg = i };
3802 .{ .arg = i }
3803 else
3804 .{ .arg_array = i };
38053168
3806 if (f.liveness.isUnused(inst)) {3169 if (f.liveness.isUnused(inst)) {
3807 const w = &f.object.code.writer;3170 const w = &f.code.writer;
3808 try w.writeByte('(');3171 try w.writeByte('(');
3809 try f.renderType(w, .void);3172 try f.renderType(w, .void);
3810 try w.writeByte(')');3173 try w.writeByte(')');
3811 try f.writeCValue(w, result, .Other);3174 try f.writeCValue(w, result, .other);
3812 try w.writeByte(';');3175 try w.writeByte(';');
3813 try f.object.newline();3176 try f.newline();
3814 return .none;3177 return .none;
3815 }3178 }
38163179
...@@ -3818,7 +3181,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3818,7 +3181,7 @@ fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
3818}3181}
38193182
3820fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {3183fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3821 const pt = f.object.dg.pt;3184 const pt = f.dg.pt;
3822 const zcu = pt.zcu;3185 const zcu = pt.zcu;
3823 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3186 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 {...@@ -3841,94 +3204,69 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
3841 ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)3204 ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)
3842 else3205 else
3843 true;3206 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;
3848 const local = try f.allocLocal(inst, src_ty);3209 const local = try f.allocLocal(inst, src_ty);
3849 const v = try Vectorize.start(f, inst, w, ptr_ty);3210 const v = try Vectorize.start(f, inst, w, ptr_ty);
38503211
3851 if (need_memcpy) {3212 if (!is_aligned) {
3852 try w.writeAll("memcpy(");3213 try w.writeAll("memcpy(&");
3853 if (!is_array) try w.writeByte('&');3214 try f.writeCValue(w, local, .other);
3854 try f.writeCValue(w, local, .Other);
3855 try v.elem(f, w);3215 try v.elem(f, w);
3856 try w.writeAll(", (const char *)");3216 try w.writeAll(", (const char *)");
3857 try f.writeCValue(w, operand, .Other);3217 try f.writeCValue(w, operand, .other);
3858 try v.elem(f, w);3218 try v.elem(f, w);
3859 try w.writeAll(", sizeof(");3219 try w.writeAll(", sizeof(");
3860 try f.renderType(w, src_ty);3220 try f.renderType(w, src_ty);
3861 try w.writeAll("))");3221 try w.writeAll("))");
3862 } else {3222 } else {
3863 try f.writeCValue(w, local, .Other);3223 try f.writeCValue(w, local, .other);
3864 try v.elem(f, w);3224 try v.elem(f, w);
3865 try w.writeAll(" = ");3225 try w.writeAll(" = ");
3866 try f.writeCValueDeref(w, operand);3226 try f.writeCValueDeref(w, operand);
3867 try v.elem(f, w);3227 try v.elem(f, w);
3868 }3228 }
3869 try w.writeByte(';');3229 try w.writeByte(';');
3870 try f.object.newline();3230 try f.newline();
3871 try v.end(f, inst, w);3231 try v.end(f, inst, w);
38723232
3873 return local;3233 return local;
3874}3234}
38753235
3876fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {3236fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
3877 const pt = f.object.dg.pt;3237 const pt = f.dg.pt;
3878 const zcu = pt.zcu;3238 const zcu = pt.zcu;
3879 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;3239 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;
3881 const op_inst = un_op.toIndex();3241 const op_inst = un_op.toIndex();
3882 const op_ty = f.typeOf(un_op);3242 const op_ty = f.typeOf(un_op);
3883 const ret_ty = if (is_ptr) op_ty.childType(zcu) else op_ty;3243 const ret_ty = if (is_ptr) op_ty.childType(zcu) else op_ty;
3884 const ret_ctype = try f.ctypeFromType(ret_ty, .parameter);
38853244
3886 if (op_inst != null and f.air.instructions.items(.tag)[@intFromEnum(op_inst.?)] == .call_always_tail) {3245 if (op_inst != null and f.air.instructions.items(.tag)[@intFromEnum(op_inst.?)] == .call_always_tail) {
3887 try reap(f, inst, &.{un_op});3246 try reap(f, inst, &.{un_op});
3888 _ = try airCall(f, op_inst.?, .always_tail);3247 _ = try airCall(f, op_inst.?, .always_tail);
3889 } else if (ret_ctype.index != .void) {3248 } else if (ret_ty.hasRuntimeBits(zcu)) {
3890 const operand = try f.resolveInst(un_op);3249 const operand = try f.resolveInst(un_op);
3891 try reap(f, inst, &.{un_op});3250 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
3914 try w.writeAll("return ");3252 try w.writeAll("return ");
3915 if (deref)3253 if (is_ptr) {
3916 try f.writeCValueDeref(w, ret_val)3254 try f.writeCValueDeref(w, operand);
3917 else3255 } else switch (operand) {
3918 try f.writeCValue(w, ret_val, .Other);3256 // Instead of 'return &local', emit 'return undefined'.
3919 try w.writeAll(";\n");3257 .local_ref => try f.dg.renderUndefValue(w, ret_ty, .other),
3920 if (is_array) {3258 else => try f.writeCValue(w, operand, .other),
3921 try freeLocal(f, inst, ret_val.new_local, null);
3922 }3259 }
3260 try w.writeAll(";\n");
3923 } else {3261 } else {
3924 try reap(f, inst, &.{un_op});3262 try reap(f, inst, &.{un_op});
3925 // Not even allowed to return void in a naked function.3263 // 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");
3927 }3265 }
3928}3266}
39293267
3930fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {3268fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
3931 const pt = f.object.dg.pt;3269 const pt = f.dg.pt;
3932 const zcu = pt.zcu;3270 const zcu = pt.zcu;
3933 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3271 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 {...@@ -3940,23 +3278,23 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
3940 const operand_ty = f.typeOf(ty_op.operand);3278 const operand_ty = f.typeOf(ty_op.operand);
3941 const scalar_ty = operand_ty.scalarType(zcu);3279 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;
3946 const local = try f.allocLocal(inst, inst_ty);3284 const local = try f.allocLocal(inst, inst_ty);
3947 const v = try Vectorize.start(f, inst, w, operand_ty);3285 const v = try Vectorize.start(f, inst, w, operand_ty);
3948 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));3286 try f.writeCValue(w, local, .other);
3949 try f.writeCValue(w, local, .Other);
3950 try v.elem(f, w);3287 try v.elem(f, w);
3951 try a.assign(f, w);3288 try w.writeAll(" = ");
3952 try f.renderIntCast(w, inst_scalar_ty, operand, v, scalar_ty, .Other);3289 try f.renderIntCast(w, inst_scalar_ty, operand, v, scalar_ty, .other);
3953 try a.end(f, w);3290 try w.writeByte(';');
3291 try f.newline();
3954 try v.end(f, inst, w);3292 try v.end(f, inst, w);
3955 return local;3293 return local;
3956}3294}
39573295
3958fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {3296fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3959 const pt = f.object.dg.pt;3297 const pt = f.dg.pt;
3960 const zcu = pt.zcu;3298 const zcu = pt.zcu;
3961 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3299 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 {...@@ -3978,13 +3316,12 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3978 const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits);3316 const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits);
3979 if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand);3317 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;
3982 const local = try f.allocLocal(inst, inst_ty);3320 const local = try f.allocLocal(inst, inst_ty);
3983 const v = try Vectorize.start(f, inst, w, operand_ty);3321 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));3322 try f.writeCValue(w, local, .other);
3985 try f.writeCValue(w, local, .Other);
3986 try v.elem(f, w);3323 try v.elem(f, w);
3987 try a.assign(f, w);3324 try w.writeAll(" = ");
3988 if (need_cast) {3325 if (need_cast) {
3989 try w.writeByte('(');3326 try w.writeByte('(');
3990 try f.renderType(w, inst_scalar_ty);3327 try f.renderType(w, inst_scalar_ty);
...@@ -3992,18 +3329,18 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3992,18 +3329,18 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
3992 }3329 }
3993 if (need_lo) {3330 if (need_lo) {
3994 try w.writeAll("zig_lo_");3331 try w.writeAll("zig_lo_");
3995 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);3332 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
3996 try w.writeByte('(');3333 try w.writeByte('(');
3997 }3334 }
3998 if (!need_mask) {3335 if (!need_mask) {
3999 try f.writeCValue(w, operand, .Other);3336 try f.writeCValue(w, operand, .other);
4000 try v.elem(f, w);3337 try v.elem(f, w);
4001 } else switch (dest_int_info.signedness) {3338 } else switch (dest_int_info.signedness) {
4002 .unsigned => {3339 .unsigned => {
4003 try w.writeAll("zig_and_");3340 try w.writeAll("zig_and_");
4004 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);3341 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
4005 try w.writeByte('(');3342 try w.writeByte('(');
4006 try f.writeCValue(w, operand, .FunctionArgument);3343 try f.writeCValue(w, operand, .other);
4007 try v.elem(f, w);3344 try v.elem(f, w);
4008 try w.print(", {f})", .{3345 try w.print(", {f})", .{
4009 try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)),3346 try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)),
...@@ -4015,7 +3352,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4015,7 +3352,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
4015 const shift_val = try pt.intValue(.u8, c_bits - dest_bits);3352 const shift_val = try pt.intValue(.u8, c_bits - dest_bits);
40163353
4017 try w.writeAll("zig_shr_");3354 try w.writeAll("zig_shr_");
4018 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);3355 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
4019 if (c_bits == 128) {3356 if (c_bits == 128) {
4020 try w.print("(zig_bitCast_i{d}(", .{c_bits});3357 try w.print("(zig_bitCast_i{d}(", .{c_bits});
4021 } else {3358 } else {
...@@ -4027,7 +3364,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4027,7 +3364,7 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
4027 } else {3364 } else {
4028 try w.print("(uint{d}_t)", .{c_bits});3365 try w.print("(uint{d}_t)", .{c_bits});
4029 }3366 }
4030 try f.writeCValue(w, operand, .FunctionArgument);3367 try f.writeCValue(w, operand, .other);
4031 try v.elem(f, w);3368 try v.elem(f, w);
4032 if (c_bits == 128) try w.writeByte(')');3369 if (c_bits == 128) try w.writeByte(')');
4033 try w.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)});3370 try w.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)});
...@@ -4036,13 +3373,14 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4036,13 +3373,14 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
4036 },3373 },
4037 }3374 }
4038 if (need_lo) try w.writeByte(')');3375 if (need_lo) try w.writeByte(')');
4039 try a.end(f, w);3376 try w.writeByte(';');
3377 try f.newline();
4040 try v.end(f, inst, w);3378 try v.end(f, inst, w);
4041 return local;3379 return local;
4042}3380}
40433381
4044fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {3382fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
4045 const pt = f.object.dg.pt;3383 const pt = f.dg.pt;
4046 const zcu = pt.zcu;3384 const zcu = pt.zcu;
4047 // *a = b;3385 // *a = b;
4048 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3386 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 {...@@ -4060,7 +3398,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
40603398
4061 const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndef(zcu) else false;3399 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;
4064 if (val_is_undef) {3402 if (val_is_undef) {
4065 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3403 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
4066 if (safety and ptr_info.packed_offset.host_size == 0) {3404 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 {...@@ -4080,11 +3418,11 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
4080 },3418 },
4081 };3419 };
4082 try w.writeAll("memset(");3420 try w.writeAll("memset(");
4083 try f.writeCValue(w, ptr_val, .FunctionArgument);3421 try f.writeCValue(w, ptr_val, .other);
4084 try w.print(", {s}, sizeof(", .{byte_str});3422 try w.print(", {s}, sizeof(", .{byte_str});
4085 try f.renderType(w, .fromInterned(ptr_info.child));3423 try f.renderType(w, .fromInterned(ptr_info.child));
4086 try w.writeAll("));");3424 try w.writeAll("));");
4087 try f.object.newline();3425 try f.newline();
4088 }3426 }
4089 return .none;3427 return .none;
4090 }3428 }
...@@ -4093,46 +3431,29 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {...@@ -4093,46 +3431,29 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
4093 ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)3431 ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)
4094 else3432 else
4095 true;3433 true;
4096 const is_array = lowersToArray(.fromInterned(ptr_info.child), zcu);
4097 const need_memcpy = !is_aligned or is_array;
40983434
4099 const src_val = try f.resolveInst(bin_op.rhs);3435 const src_val = try f.resolveInst(bin_op.rhs);
4100 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3436 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
41013437
4102 const src_scalar_ctype = try f.ctypeFromType(src_ty.scalarType(zcu), .complete);3438 if (!is_aligned) {
4103 if (need_memcpy) {
4104 // For this memcpy to safely work we need the rhs to have the same3439 // For this memcpy to safely work we need the rhs to have the same
4105 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).3440 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
4106 assert(src_ty.eql(.fromInterned(ptr_info.child), zcu));3441 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
4122 const v = try Vectorize.start(f, inst, w, ptr_ty);3443 const v = try Vectorize.start(f, inst, w, ptr_ty);
4123 try w.writeAll("memcpy((char *)");3444 try w.writeAll("memcpy((char *)");
4124 try f.writeCValue(w, ptr_val, .FunctionArgument);3445 try f.writeCValue(w, ptr_val, .other);
4125 try v.elem(f, w);3446 try v.elem(f, w);
4126 try w.writeAll(", ");3447 try w.writeAll(", &");
4127 if (!is_array) try w.writeByte('&');3448 switch (src_val) {
4128 try f.writeCValue(w, array_src, .FunctionArgument);3449 .constant => |val| try f.dg.renderValueAsLvalue(w, val),
3450 else => try f.writeCValue(w, src_val, .other),
3451 }
4129 try v.elem(f, w);3452 try v.elem(f, w);
4130 try w.writeAll(", sizeof(");3453 try w.writeAll(", sizeof(");
4131 try f.renderType(w, src_ty);3454 try f.renderType(w, src_ty);
4132 try w.writeAll("))");3455 try w.writeAll("));");
4133 try f.freeCValue(inst, array_src);3456 try f.newline();
4134 try w.writeByte(';');
4135 try f.object.newline();
4136 try v.end(f, inst, w);3457 try v.end(f, inst, w);
4137 } else {3458 } else {
4138 switch (ptr_val) {3459 switch (ptr_val) {
...@@ -4144,20 +3465,20 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {...@@ -4144,20 +3465,20 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
4144 else => {},3465 else => {},
4145 }3466 }
4146 const v = try Vectorize.start(f, inst, w, ptr_ty);3467 const v = try Vectorize.start(f, inst, w, ptr_ty);
4147 const a = try Assignment.start(f, w, src_scalar_ctype);
4148 try f.writeCValueDeref(w, ptr_val);3468 try f.writeCValueDeref(w, ptr_val);
4149 try v.elem(f, w);3469 try v.elem(f, w);
4150 try a.assign(f, w);3470 try w.writeAll(" = ");
4151 try f.writeCValue(w, src_val, .Other);3471 try f.writeCValue(w, src_val, .other);
4152 try v.elem(f, w);3472 try v.elem(f, w);
4153 try a.end(f, w);3473 try w.writeByte(';');
3474 try f.newline();
4154 try v.end(f, inst, w);3475 try v.end(f, inst, w);
4155 }3476 }
4156 return .none;3477 return .none;
4157}3478}
41583479
4159fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue {3480fn 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;
4161 const zcu = pt.zcu;3482 const zcu = pt.zcu;
4162 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3483 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4163 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;3484 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:...@@ -4170,7 +3491,7 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
4170 const operand_ty = f.typeOf(bin_op.lhs);3491 const operand_ty = f.typeOf(bin_op.lhs);
4171 const scalar_ty = operand_ty.scalarType(zcu);3492 const scalar_ty = operand_ty.scalarType(zcu);
41723493
4173 const w = &f.object.code.writer;3494 const w = &f.code.writer;
4174 const local = try f.allocLocal(inst, inst_ty);3495 const local = try f.allocLocal(inst, inst_ty);
4175 const v = try Vectorize.start(f, inst, w, operand_ty);3496 const v = try Vectorize.start(f, inst, w, operand_ty);
4176 try f.writeCValueMember(w, local, .{ .field = 1 });3497 try f.writeCValueMember(w, local, .{ .field = 1 });
...@@ -4178,26 +3499,26 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:...@@ -4178,26 +3499,26 @@ fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info:
4178 try w.writeAll(" = zig_");3499 try w.writeAll(" = zig_");
4179 try w.writeAll(operation);3500 try w.writeAll(operation);
4180 try w.writeAll("o_");3501 try w.writeAll("o_");
4181 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);3502 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
4182 try w.writeAll("(&");3503 try w.writeAll("(&");
4183 try f.writeCValueMember(w, local, .{ .field = 0 });3504 try f.writeCValueMember(w, local, .{ .field = 0 });
4184 try v.elem(f, w);3505 try v.elem(f, w);
4185 try w.writeAll(", ");3506 try w.writeAll(", ");
4186 try f.writeCValue(w, lhs, .FunctionArgument);3507 try f.writeCValue(w, lhs, .other);
4187 try v.elem(f, w);3508 try v.elem(f, w);
4188 try w.writeAll(", ");3509 try w.writeAll(", ");
4189 try f.writeCValue(w, rhs, .FunctionArgument);3510 try f.writeCValue(w, rhs, .other);
4190 if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, w);3511 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);
4192 try w.writeAll(");");3513 try w.writeAll(");");
4193 try f.object.newline();3514 try f.newline();
4194 try v.end(f, inst, w);3515 try v.end(f, inst, w);
41953516
4196 return local;3517 return local;
4197}3518}
41983519
4199fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {3520fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
4200 const pt = f.object.dg.pt;3521 const pt = f.dg.pt;
4201 const zcu = pt.zcu;3522 const zcu = pt.zcu;
4202 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3523 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4203 const operand_ty = f.typeOf(ty_op.operand);3524 const operand_ty = f.typeOf(ty_op.operand);
...@@ -4209,17 +3530,17 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4209,17 +3530,17 @@ fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
42093530
4210 const inst_ty = f.typeOfIndex(inst);3531 const inst_ty = f.typeOfIndex(inst);
42113532
4212 const w = &f.object.code.writer;3533 const w = &f.code.writer;
4213 const local = try f.allocLocal(inst, inst_ty);3534 const local = try f.allocLocal(inst, inst_ty);
4214 const v = try Vectorize.start(f, inst, w, operand_ty);3535 const v = try Vectorize.start(f, inst, w, operand_ty);
4215 try f.writeCValue(w, local, .Other);3536 try f.writeCValue(w, local, .other);
4216 try v.elem(f, w);3537 try v.elem(f, w);
4217 try w.writeAll(" = ");3538 try w.writeAll(" = ");
4218 try w.writeByte('!');3539 try w.writeByte('!');
4219 try f.writeCValue(w, op, .Other);3540 try f.writeCValue(w, op, .other);
4220 try v.elem(f, w);3541 try v.elem(f, w);
4221 try w.writeByte(';');3542 try w.writeByte(';');
4222 try f.object.newline();3543 try f.newline();
4223 try v.end(f, inst, w);3544 try v.end(f, inst, w);
42243545
4225 return local;3546 return local;
...@@ -4232,7 +3553,7 @@ fn airBinOp(...@@ -4232,7 +3553,7 @@ fn airBinOp(
4232 operation: []const u8,3553 operation: []const u8,
4233 info: BuiltinInfo,3554 info: BuiltinInfo,
4234) !CValue {3555) !CValue {
4235 const pt = f.object.dg.pt;3556 const pt = f.dg.pt;
4236 const zcu = pt.zcu;3557 const zcu = pt.zcu;
4237 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3558 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
4238 const operand_ty = f.typeOf(bin_op.lhs);3559 const operand_ty = f.typeOf(bin_op.lhs);
...@@ -4246,21 +3567,21 @@ fn airBinOp(...@@ -4246,21 +3567,21 @@ fn airBinOp(
42463567
4247 const inst_ty = f.typeOfIndex(inst);3568 const inst_ty = f.typeOfIndex(inst);
42483569
4249 const w = &f.object.code.writer;3570 const w = &f.code.writer;
4250 const local = try f.allocLocal(inst, inst_ty);3571 const local = try f.allocLocal(inst, inst_ty);
4251 const v = try Vectorize.start(f, inst, w, operand_ty);3572 const v = try Vectorize.start(f, inst, w, operand_ty);
4252 try f.writeCValue(w, local, .Other);3573 try f.writeCValue(w, local, .other);
4253 try v.elem(f, w);3574 try v.elem(f, w);
4254 try w.writeAll(" = ");3575 try w.writeAll(" = ");
4255 try f.writeCValue(w, lhs, .Other);3576 try f.writeCValue(w, lhs, .other);
4256 try v.elem(f, w);3577 try v.elem(f, w);
4257 try w.writeByte(' ');3578 try w.writeByte(' ');
4258 try w.writeAll(operator);3579 try w.writeAll(operator);
4259 try w.writeByte(' ');3580 try w.writeByte(' ');
4260 try f.writeCValue(w, rhs, .Other);3581 try f.writeCValue(w, rhs, .other);
4261 try v.elem(f, w);3582 try v.elem(f, w);
4262 try w.writeByte(';');3583 try w.writeByte(';');
4263 try f.object.newline();3584 try f.newline();
4264 try v.end(f, inst, w);3585 try v.end(f, inst, w);
42653586
4266 return local;3587 return local;
...@@ -4272,7 +3593,7 @@ fn airCmpOp(...@@ -4272,7 +3593,7 @@ fn airCmpOp(
4272 data: anytype,3593 data: anytype,
4273 operator: std.math.CompareOperator,3594 operator: std.math.CompareOperator,
4274) !CValue {3595) !CValue {
4275 const pt = f.object.dg.pt;3596 const pt = f.dg.pt;
4276 const zcu = pt.zcu;3597 const zcu = pt.zcu;
4277 const lhs_ty = f.typeOf(data.lhs);3598 const lhs_ty = f.typeOf(data.lhs);
4278 const scalar_ty = lhs_ty.scalarType(zcu);3599 const scalar_ty = lhs_ty.scalarType(zcu);
...@@ -4297,26 +3618,26 @@ fn airCmpOp(...@@ -4297,26 +3618,26 @@ fn airCmpOp(
42973618
4298 const rhs_ty = f.typeOf(data.rhs);3619 const rhs_ty = f.typeOf(data.rhs);
4299 const need_cast = lhs_ty.isSinglePointer(zcu) or rhs_ty.isSinglePointer(zcu);3620 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;
4301 const local = try f.allocLocal(inst, inst_ty);3622 const local = try f.allocLocal(inst, inst_ty);
4302 const v = try Vectorize.start(f, inst, w, lhs_ty);3623 const v = try Vectorize.start(f, inst, w, lhs_ty);
4303 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));3624 try f.writeCValue(w, local, .other);
4304 try f.writeCValue(w, local, .Other);
4305 try v.elem(f, w);3625 try v.elem(f, w);
4306 try a.assign(f, w);3626 try w.writeAll(" = ");
4307 if (lhs != .undef and lhs.eql(rhs)) try w.writeAll(switch (operator) {3627 if (lhs != .undef and lhs.eql(rhs)) try w.writeAll(switch (operator) {
4308 .lt, .neq, .gt => "false",3628 .lt, .neq, .gt => "false",
4309 .lte, .eq, .gte => "true",3629 .lte, .eq, .gte => "true",
4310 }) else {3630 }) else {
4311 if (need_cast) try w.writeAll("(void*)");3631 if (need_cast) try w.writeAll("(void*)");
4312 try f.writeCValue(w, lhs, .Other);3632 try f.writeCValue(w, lhs, .other);
4313 try v.elem(f, w);3633 try v.elem(f, w);
4314 try w.writeAll(compareOperatorC(operator));3634 try w.writeAll(compareOperatorC(operator));
4315 if (need_cast) try w.writeAll("(void*)");3635 if (need_cast) try w.writeAll("(void*)");
4316 try f.writeCValue(w, rhs, .Other);3636 try f.writeCValue(w, rhs, .other);
4317 try v.elem(f, w);3637 try v.elem(f, w);
4318 }3638 }
4319 try a.end(f, w);3639 try w.writeByte(';');
3640 try f.newline();
4320 try v.end(f, inst, w);3641 try v.end(f, inst, w);
43213642
4322 return local;3643 return local;
...@@ -4327,9 +3648,8 @@ fn airEquality(...@@ -4327,9 +3648,8 @@ fn airEquality(
4327 inst: Air.Inst.Index,3648 inst: Air.Inst.Index,
4328 operator: std.math.CompareOperator,3649 operator: std.math.CompareOperator,
4329) !CValue {3650) !CValue {
4330 const pt = f.object.dg.pt;3651 const pt = f.dg.pt;
4331 const zcu = pt.zcu;3652 const zcu = pt.zcu;
4332 const ctype_pool = &f.object.dg.ctype_pool;
4333 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3653 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
43343654
4335 const operand_ty = f.typeOf(bin_op.lhs);3655 const operand_ty = f.typeOf(bin_op.lhs);
...@@ -4350,54 +3670,64 @@ fn airEquality(...@@ -4350,54 +3670,64 @@ fn airEquality(
4350 const rhs = try f.resolveInst(bin_op.rhs);3670 const rhs = try f.resolveInst(bin_op.rhs);
4351 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3671 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;
4354 const local = try f.allocLocal(inst, .bool);3682 const local = try f.allocLocal(inst, .bool);
4355 const a = try Assignment.start(f, w, .bool);3683 try f.writeCValue(w, local, .other);
4356 try f.writeCValue(w, local, .Other);3684 try w.writeAll(" = ");
4357 try a.assign(f, w);
43583685
4359 const operand_ctype = try f.ctypeFromType(operand_ty, .complete);3686 switch (operand_ty.zigTypeTag(zcu)) {
4360 if (lhs != .undef and lhs.eql(rhs)) try w.writeAll(switch (operator) {3687 .optional => switch (CType.classifyOptional(operand_ty, zcu)) {
4361 .lt, .lte, .gte, .gt => unreachable,3688 .npv_payload => unreachable, // opv optional
4362 .neq => "false",3689
4363 .eq => "true",3690 .error_set, .ptr_like => {},
4364 }) else switch (operand_ctype.info(ctype_pool)) {3691
4365 .basic, .pointer => {3692 .slice_like => unreachable, // equality is not defined on slices
4366 try f.writeCValue(w, lhs, .Other);3693
4367 try w.writeAll(compareOperatorC(operator));3694 .opv_payload => {
4368 try f.writeCValue(w, rhs, .Other);3695 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });
4369 },3696 try w.writeAll(compareOperatorC(operator));
4370 .aligned, .array, .vector, .fwd_decl, .function => unreachable,3697 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });
4371 .aggregate => |aggregate| if (aggregate.fields.len == 2 and3698 try w.writeByte(';');
4372 (aggregate.fields.at(0, ctype_pool).name.index == .is_null or3699 try f.newline();
4373 aggregate.fields.at(1, ctype_pool).name.index == .is_null))3700 return local;
4374 {3701 },
4375 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });3702
4376 try w.writeAll(" || ");3703 .@"struct" => {
4377 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });3704 // `lhs.is_null || rhs.is_null ? lhs.is_null == rhs.is_null : lhs.payload == rhs.payload`
4378 try w.writeAll(" ? ");3705 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });
4379 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });3706 try w.writeAll(" || ");
4380 try w.writeAll(compareOperatorC(operator));3707 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });
4381 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });3708 try w.writeAll(" ? ");
4382 try w.writeAll(" : ");3709 try f.writeCValueMember(w, lhs, .{ .identifier = "is_null" });
4383 try f.writeCValueMember(w, lhs, .{ .identifier = "payload" });3710 try w.writeAll(compareOperatorC(operator));
4384 try w.writeAll(compareOperatorC(operator));3711 try f.writeCValueMember(w, rhs, .{ .identifier = "is_null" });
4385 try f.writeCValueMember(w, rhs, .{ .identifier = "payload" });3712 try w.writeAll(" : ");
4386 } else for (0..aggregate.fields.len) |field_index| {3713 try f.writeCValueMember(w, lhs, .{ .identifier = "payload" });
4387 if (field_index > 0) try w.writeAll(switch (operator) {3714 try w.writeAll(compareOperatorC(operator));
4388 .lt, .lte, .gte, .gt => unreachable,3715 try f.writeCValueMember(w, rhs, .{ .identifier = "payload" });
4389 .eq => " && ",3716 try w.writeByte(';');
4390 .neq => " || ",3717 try f.newline();
4391 });3718 return local;
4392 const field_name: CValue = .{3719 },
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);
4398 },3720 },
3721 .bool, .int, .pointer, .@"enum", .error_set => {},
3722 .@"struct", .@"union" => assert(operand_ty.containerLayout(zcu) == .@"packed"),
3723 else => unreachable,
4399 }3724 }
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
4402 return local;3732 return local;
4403}3733}
...@@ -4408,18 +3738,18 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4408,18 +3738,18 @@ fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
4408 const operand = try f.resolveInst(un_op);3738 const operand = try f.resolveInst(un_op);
4409 try reap(f, inst, &.{un_op});3739 try reap(f, inst, &.{un_op});
44103740
4411 const w = &f.object.code.writer;3741 const w = &f.code.writer;
4412 const local = try f.allocLocal(inst, .bool);3742 const local = try f.allocLocal(inst, .bool);
4413 try f.writeCValue(w, local, .Other);3743 try f.writeCValue(w, local, .other);
4414 try w.writeAll(" = ");3744 try w.writeAll(" = ");
4415 try f.writeCValue(w, operand, .Other);3745 try f.writeCValue(w, operand, .other);
4416 try w.print(" < sizeof({f}) / sizeof(*{0f});", .{fmtIdentSolo("zig_errorName")});3746 try w.print(" < sizeof({f}) / sizeof(*{0f});", .{fmtIdentSolo("zig_errorName")});
4417 try f.object.newline();3747 try f.newline();
4418 return local;3748 return local;
4419}3749}
44203750
4421fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {3751fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
4422 const pt = f.object.dg.pt;3752 const pt = f.dg.pt;
4423 const zcu = pt.zcu;3753 const zcu = pt.zcu;
4424 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3754 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4425 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;3755 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 {...@@ -4432,38 +3762,34 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
4432 const inst_scalar_ty = inst_ty.scalarType(zcu);3762 const inst_scalar_ty = inst_ty.scalarType(zcu);
4433 const elem_ty = inst_scalar_ty.indexableElem(zcu);3763 const elem_ty = inst_scalar_ty.indexableElem(zcu);
4434 assert(elem_ty.hasRuntimeBits(zcu));3764 assert(elem_ty.hasRuntimeBits(zcu));
4435 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
44363765
4437 const local = try f.allocLocal(inst, inst_ty);3766 const local = try f.allocLocal(inst, inst_ty);
4438 const w = &f.object.code.writer;3767 const w = &f.code.writer;
4439 const v = try Vectorize.start(f, inst, w, inst_ty);3768 const v = try Vectorize.start(f, inst, w, inst_ty);
4440 const a = try Assignment.start(f, w, inst_scalar_ctype);3769 try f.writeCValue(w, local, .other);
4441 try f.writeCValue(w, local, .Other);
4442 try v.elem(f, w);3770 try v.elem(f, w);
4443 try a.assign(f, w);3771 try w.writeAll(" = ");
4444 // We must convert to and from integer types to prevent UB if the operation3772 // We must convert to and from integer types to prevent UB if the operation
4445 // results in a NULL pointer, or if LHS is NULL. The operation is only UB3773 // results in a NULL pointer, or if LHS is NULL. The operation is only UB
4446 // if the result is NULL and then dereferenced.3774 // if the result is NULL and then dereferenced.
4447 try w.writeByte('(');3775 try w.writeByte('(');
4448 try f.renderCType(w, inst_scalar_ctype);3776 try f.renderType(w, inst_scalar_ty);
4449 try w.writeAll(")(((uintptr_t)");3777 try w.writeAll(")(((uintptr_t)");
4450 try f.writeCValue(w, lhs, .Other);3778 try f.writeCValue(w, lhs, .other);
4451 try v.elem(f, w);3779 try v.elem(f, w);
4452 try w.writeAll(") ");3780 try w.print(") {c} (", .{operator});
4453 try w.writeByte(operator);3781 try f.writeCValue(w, rhs, .other);
4454 try w.writeAll(" (");
4455 try f.writeCValue(w, rhs, .Other);
4456 try v.elem(f, w);3782 try v.elem(f, w);
4457 try w.writeAll("*sizeof(");3783 try w.writeAll("*sizeof(");
4458 try f.renderType(w, elem_ty);3784 try f.renderType(w, elem_ty);
4459 try w.writeAll(")))");3785 try w.writeAll(")));");
4460 try a.end(f, w);3786 try f.newline();
4461 try v.end(f, inst, w);3787 try v.end(f, inst, w);
4462 return local;3788 return local;
4463}3789}
44643790
4465fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue {3791fn 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;
4467 const zcu = pt.zcu;3793 const zcu = pt.zcu;
4468 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3794 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...@@ -4477,36 +3803,34 @@ fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []cons
4477 const rhs = try f.resolveInst(bin_op.rhs);3803 const rhs = try f.resolveInst(bin_op.rhs);
4478 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3804 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
44793805
4480 const w = &f.object.code.writer;3806 const w = &f.code.writer;
4481 const local = try f.allocLocal(inst, inst_ty);3807 const local = try f.allocLocal(inst, inst_ty);
4482 const v = try Vectorize.start(f, inst, w, inst_ty);3808 const v = try Vectorize.start(f, inst, w, inst_ty);
4483 try f.writeCValue(w, local, .Other);3809 try f.writeCValue(w, local, .other);
4484 try v.elem(f, w);3810 try v.elem(f, w);
4485 // (lhs <> rhs) ? lhs : rhs3811 // (lhs <> rhs) ? lhs : rhs
4486 try w.writeAll(" = (");3812 try w.writeAll(" = (");
4487 try f.writeCValue(w, lhs, .Other);3813 try f.writeCValue(w, lhs, .other);
4488 try v.elem(f, w);3814 try v.elem(f, w);
4489 try w.writeByte(' ');3815 try w.writeByte(' ');
4490 try w.writeByte(operator);3816 try w.writeByte(operator);
4491 try w.writeByte(' ');3817 try w.writeByte(' ');
4492 try f.writeCValue(w, rhs, .Other);3818 try f.writeCValue(w, rhs, .other);
4493 try v.elem(f, w);3819 try v.elem(f, w);
4494 try w.writeAll(") ? ");3820 try w.writeAll(") ? ");
4495 try f.writeCValue(w, lhs, .Other);3821 try f.writeCValue(w, lhs, .other);
4496 try v.elem(f, w);3822 try v.elem(f, w);
4497 try w.writeAll(" : ");3823 try w.writeAll(" : ");
4498 try f.writeCValue(w, rhs, .Other);3824 try f.writeCValue(w, rhs, .other);
4499 try v.elem(f, w);3825 try v.elem(f, w);
4500 try w.writeByte(';');3826 try w.writeByte(';');
4501 try f.object.newline();3827 try f.newline();
4502 try v.end(f, inst, w);3828 try v.end(f, inst, w);
45033829
4504 return local;3830 return local;
4505}3831}
45063832
4507fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {3833fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
4508 const pt = f.object.dg.pt;
4509 const zcu = pt.zcu;
4510 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3834 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
4511 const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;3835 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 {...@@ -4515,24 +3839,22 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
4515 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });3839 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
45163840
4517 const inst_ty = f.typeOfIndex(inst);3841 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;
4521 const local = try f.allocLocal(inst, inst_ty);3844 const local = try f.allocLocal(inst, inst_ty);
4522 {3845
4523 const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete));3846 try f.writeCValueMember(w, local, .{ .identifier = "ptr" });
4524 try f.writeCValueMember(w, local, .{ .identifier = "ptr" });3847 try w.writeAll(" = ");
4525 try a.assign(f, w);3848 try f.writeCValue(w, ptr, .other);
4526 try f.writeCValue(w, ptr, .Other);3849 try w.writeByte(';');
4527 try a.end(f, w);3850 try f.newline();
4528 }3851
4529 {3852 try f.writeCValueMember(w, local, .{ .identifier = "len" });
4530 const a = try Assignment.start(f, w, .usize);3853 try w.writeAll(" = ");
4531 try f.writeCValueMember(w, local, .{ .identifier = "len" });3854 try f.writeCValue(w, len, .other);
4532 try a.assign(f, w);3855 try w.writeByte(';');
4533 try f.writeCValue(w, len, .Other);3856 try f.newline();
4534 try a.end(f, w);3857
4535 }
4536 return local;3858 return local;
4537}3859}
45383860
...@@ -4541,14 +3863,14 @@ fn airCall(...@@ -4541,14 +3863,14 @@ fn airCall(
4541 inst: Air.Inst.Index,3863 inst: Air.Inst.Index,
4542 modifier: std.builtin.CallModifier,3864 modifier: std.builtin.CallModifier,
4543) !CValue {3865) !CValue {
4544 const pt = f.object.dg.pt;3866 const pt = f.dg.pt;
4545 const zcu = pt.zcu;3867 const zcu = pt.zcu;
4546 const ip = &zcu.intern_pool;3868 const ip = &zcu.intern_pool;
4547 // Not even allowed to call panic in a naked function.3869 // 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;3872 const gpa = f.dg.gpa;
4551 const w = &f.object.code.writer;3873 const w = &f.code.writer;
45523874
4553 const call = f.air.unwrapCall(inst);3875 const call = f.air.unwrapCall(inst);
4554 const args = call.args;3876 const args = call.args;
...@@ -4557,27 +3879,11 @@ fn airCall(...@@ -4557,27 +3879,11 @@ fn airCall(
4557 defer gpa.free(resolved_args);3879 defer gpa.free(resolved_args);
4558 for (resolved_args, args) |*resolved_arg, arg| {3880 for (resolved_args, args) |*resolved_arg, arg| {
4559 const arg_ty = f.typeOf(arg);3881 const arg_ty = f.typeOf(arg);
4560 const arg_ctype = try f.ctypeFromType(arg_ty, .parameter);3882 if (!arg_ty.hasRuntimeBits(zcu)) {
4561 if (arg_ctype.index == .void) {
4562 resolved_arg.* = .none;3883 resolved_arg.* = .none;
4563 continue;3884 continue;
4564 }3885 }
4565 resolved_arg.* = try f.resolveInst(arg);3886 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 }
4581 }3887 }
45823888
4583 const callee = try f.resolveInst(call.callee);3889 const callee = try f.resolveInst(call.callee);
...@@ -4596,28 +3902,22 @@ fn airCall(...@@ -4596,28 +3902,22 @@ fn airCall(
4596 };3902 };
4597 const fn_info = zcu.typeToFunc(if (callee_is_ptr) callee_ty.childType(zcu) else callee_ty).?;3903 const fn_info = zcu.typeToFunc(if (callee_is_ptr) callee_ty.childType(zcu) else callee_ty).?;
4598 const ret_ty: Type = .fromInterned(fn_info.return_type);3904 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
4604 const result_local = result: {3906 const result_local = result: {
4605 if (modifier == .always_tail) {3907 if (modifier == .always_tail) {
4606 try w.writeAll("zig_always_tail return ");3908 try w.writeAll("zig_always_tail return ");
4607 break :result .none;3909 break :result .none;
4608 } else if (ret_ctype.index == .void) {3910 } else if (!ret_ty.hasRuntimeBits(zcu)) {
4609 break :result .none;3911 break :result .none;
4610 } else if (f.liveness.isUnused(inst)) {3912 } else if (f.liveness.isUnused(inst)) {
4611 try w.writeByte('(');3913 try w.writeAll("(void)");
4612 try f.renderCType(w, .void);
4613 try w.writeByte(')');
4614 break :result .none;3914 break :result .none;
4615 } else {3915 } else {
4616 const local = try f.allocAlignedLocal(inst, .{3916 const local = try f.allocAlignedLocal(inst, .{
4617 .ctype = ret_ctype,3917 .type = ret_ty,
4618 .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(zcu)),3918 .alignment = .none,
4619 });3919 });
4620 try f.writeCValue(w, local, .Other);3920 try f.writeCValue(w, local, .other);
4621 try w.writeAll(" = ");3921 try w.writeAll(" = ");
4622 break :result local;3922 break :result local;
4623 }3923 }
...@@ -4644,8 +3944,19 @@ fn airCall(...@@ -4644,8 +3944,19 @@ fn airCall(
4644 if (!callee_is_ptr) try w.writeByte('&');3944 if (!callee_is_ptr) try w.writeByte('&');
4645 }3945 }
4646 switch (modifier) {3946 switch (modifier) {
4647 .auto, .always_tail => try f.object.dg.renderNavName(w, fn_nav),3947 .auto, .always_tail => try renderNavName(w, fn_nav, ip),
4648 inline .never_tail, .never_inline => |m| try w.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))),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 },
4649 else => unreachable,3960 else => unreachable,
4650 }3961 }
4651 if (need_cast) try w.writeByte(')');3962 if (need_cast) try w.writeByte(')');
...@@ -4658,7 +3969,7 @@ fn airCall(...@@ -4658,7 +3969,7 @@ fn airCall(
4658 else => unreachable,3969 else => unreachable,
4659 }3970 }
4660 // Fall back to function pointer call.3971 // Fall back to function pointer call.
4661 try f.writeCValue(w, callee, .Other);3972 try f.writeCValue(w, callee, .other);
4662 }3973 }
46633974
4664 try w.writeByte('(');3975 try w.writeByte('(');
...@@ -4667,38 +3978,20 @@ fn airCall(...@@ -4667,38 +3978,20 @@ fn airCall(
4667 if (resolved_arg == .none) continue;3978 if (resolved_arg == .none) continue;
4668 if (need_comma) try w.writeAll(", ");3979 if (need_comma) try w.writeAll(", ");
4669 need_comma = true;3980 need_comma = true;
4670 try f.writeCValue(w, resolved_arg, .FunctionArgument);3981 try f.writeCValue(w, resolved_arg, .other);
4671 try f.freeCValue(inst, resolved_arg);
4672 }3982 }
4673 try w.writeAll(");");3983 try w.writeAll(");");
4674 switch (modifier) {3984 switch (modifier) {
4675 .always_tail => try w.writeByte('\n'),3985 .always_tail => try w.writeByte('\n'),
4676 else => try f.object.newline(),3986 else => try f.newline(),
4677 }3987 }
46783988
4679 const result = result: {3989 return result_local;
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;
4697}3990}
46983991
4699fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {3992fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
4700 const dbg_stmt = f.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;3993 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;
4702 // TODO re-evaluate whether to emit these or not. If we naively emit3995 // TODO re-evaluate whether to emit these or not. If we naively emit
4703 // these directives, the output file will report bogus line numbers because3996 // these directives, the output file will report bogus line numbers because
4704 // every newline after the #line directive adds one to the line.3997 // every newline after the #line directive adds one to the line.
...@@ -4707,32 +4000,32 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4707,32 +4000,32 @@ fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
4707 // newlines until the next dbg_stmt occurs.4000 // newlines until the next dbg_stmt occurs.
4708 // Perhaps an additional compilation option is in order?4001 // Perhaps an additional compilation option is in order?
4709 //try w.print("#line {d}", .{dbg_stmt.line + 1});4002 //try w.print("#line {d}", .{dbg_stmt.line + 1});
4710 //try f.object.newline();4003 //try f.newline();
4711 try w.print("/* file:{d}:{d} */", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });4004 try w.print("/* file:{d}:{d} */", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
4712 try f.object.newline();4005 try f.newline();
4713 return .none;4006 return .none;
4714}4007}
47154008
4716fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue {4009fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue {
4717 try f.object.code.writer.writeAll("(void)0;");4010 try f.code.writer.writeAll("(void)0;");
4718 try f.object.newline();4011 try f.newline();
4719 return .none;4012 return .none;
4720}4013}
47214014
4722fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {4015fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
4723 const pt = f.object.dg.pt;4016 const pt = f.dg.pt;
4724 const zcu = pt.zcu;4017 const zcu = pt.zcu;
4725 const ip = &zcu.intern_pool;4018 const ip = &zcu.intern_pool;
4726 const block = f.air.unwrapDbgBlock(inst);4019 const block = f.air.unwrapDbgBlock(inst);
4727 const owner_nav = ip.getNav(zcu.funcInfo(block.func).owner_nav);4020 const owner_nav = ip.getNav(zcu.funcInfo(block.func).owner_nav);
4728 const w = &f.object.code.writer;4021 const w = &f.code.writer;
4729 try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)});4022 try w.print("/* inline:{f} */", .{owner_nav.fqn.fmt(&zcu.intern_pool)});
4730 try f.object.newline();4023 try f.newline();
4731 return lowerBlock(f, inst, block.body);4024 return lowerBlock(f, inst, block.body);
4732}4025}
47334026
4734fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {4027fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
4735 const pt = f.object.dg.pt;4028 const pt = f.dg.pt;
4736 const zcu = pt.zcu;4029 const zcu = pt.zcu;
4737 const tag = f.air.instructions.items(.tag)[@intFromEnum(inst)];4030 const tag = f.air.instructions.items(.tag)[@intFromEnum(inst)];
4738 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;4031 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 {...@@ -4741,9 +4034,9 @@ fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
4741 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);4034 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);
47424035
4743 try reap(f, inst, &.{pl_op.operand});4036 try reap(f, inst, &.{pl_op.operand});
4744 const w = &f.object.code.writer;4037 const w = &f.code.writer;
4745 try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) });4038 try w.print("/* {s}:{s} */", .{ @tagName(tag), name.toSlice(f.air) });
4746 try f.object.newline();4039 try f.newline();
4747 return .none;4040 return .none;
4748}4041}
47494042
...@@ -4753,13 +4046,13 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4753,13 +4046,13 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
4753}4046}
47544047
4755fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) !CValue {4048fn 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;
4757 const zcu = pt.zcu;4050 const zcu = pt.zcu;
4758 const liveness_block = f.liveness.getBlock(inst);4051 const liveness_block = f.liveness.getBlock(inst);
47594052
4760 const block_id = f.next_block_index;4053 const block_id = f.next_block_index;
4761 f.next_block_index += 1;4054 f.next_block_index += 1;
4762 const w = &f.object.code.writer;4055 const w = &f.code.writer;
47634056
4764 const inst_ty = f.typeOfIndex(inst);4057 const inst_ty = f.typeOfIndex(inst);
4765 const result = if (inst_ty.hasRuntimeBits(zcu) and !f.liveness.isUnused(inst))4058 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)...@@ -4767,7 +4060,7 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
4767 else4060 else
4768 .none;4061 .none;
47694062
4770 try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{4063 try f.blocks.putNoClobber(f.dg.gpa, inst, .{
4771 .block_id = block_id,4064 .block_id = block_id,
4772 .result = result,4065 .result = result,
4773 });4066 });
...@@ -4782,23 +4075,23 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)...@@ -4782,23 +4075,23 @@ fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index)
4782 }4075 }
47834076
4784 // noreturn blocks have no `br` instructions reaching them, so we don't want a label4077 // noreturn blocks have no `br` instructions reaching them, so we don't want a label
4785 if (f.object.dg.is_naked_fn) {4078 if (f.dg.is_naked_fn) {
4786 if (f.object.dg.expected_block) |expected_block| {4079 if (f.dg.expected_block) |expected_block| {
4787 if (block_id != expected_block)4080 if (block_id != expected_block)
4788 return f.fail("runtime code not allowed in naked function", .{});4081 return f.fail("runtime code not allowed in naked function", .{});
4789 f.object.dg.expected_block = null;4082 f.dg.expected_block = null;
4790 }4083 }
4791 } else if (!f.typeOfIndex(inst).isNoReturn(zcu)) {4084 } else if (!f.typeOfIndex(inst).isNoReturn(zcu)) {
4792 // label must be followed by an expression, include an empty one.4085 // label must be followed by an expression, include an empty one.
4793 try w.print("\nzig_block_{d}:;", .{block_id});4086 try w.print("\nzig_block_{d}:;", .{block_id});
4794 try f.object.newline();4087 try f.newline();
4795 }4088 }
47964089
4797 return result;4090 return result;
4798}4091}
47994092
4800fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {4093fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {
4801 const pt = f.object.dg.pt;4094 const pt = f.dg.pt;
4802 const unwrapped_try = f.air.unwrapTry(inst);4095 const unwrapped_try = f.air.unwrapTry(inst);
4803 const body = unwrapped_try.else_body;4096 const body = unwrapped_try.else_body;
4804 const err_union_ty = f.air.typeOf(unwrapped_try.error_union, &pt.zcu.intern_pool);4097 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 {...@@ -4806,7 +4099,7 @@ fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {
4806}4099}
48074100
4808fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue {4101fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue {
4809 const pt = f.object.dg.pt;4102 const pt = f.dg.pt;
4810 const unwrapped_try = f.air.unwrapTryPtr(inst);4103 const unwrapped_try = f.air.unwrapTryPtr(inst);
4811 const body = unwrapped_try.else_body;4104 const body = unwrapped_try.else_body;
4812 const err_union_ty = f.air.typeOf(unwrapped_try.error_union_ptr, &pt.zcu.intern_pool).childType(pt.zcu);4105 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(...@@ -4821,46 +4114,38 @@ fn lowerTry(
4821 err_union_ty: Type,4114 err_union_ty: Type,
4822 is_ptr: bool,4115 is_ptr: bool,
4823) !CValue {4116) !CValue {
4824 const pt = f.object.dg.pt;4117 const pt = f.dg.pt;
4825 const zcu = pt.zcu;4118 const zcu = pt.zcu;
4826 const err_union = try f.resolveInst(operand);4119 const err_union = try f.resolveInst(operand);
4827 const inst_ty = f.typeOfIndex(inst);4120 const inst_ty = f.typeOfIndex(inst);
4828 const liveness_condbr = f.liveness.getCondBr(inst);4121 const liveness_condbr = f.liveness.getCondBr(inst);
4829 const w = &f.object.code.writer;4122 const w = &f.code.writer;
4830 const payload_ty = err_union_ty.errorUnionPayload(zcu);4123 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)) {4125 try w.writeAll("if (");
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(") ");
48514126
4852 try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);4127 // Reap the operand so that it can be reused inside genBody.
4853 try f.object.newline();4128 // Remember we must avoid calling reap() twice for the same operand
4854 if (f.object.dg.expected_block) |_|4129 // in this function.
4855 return f.fail("runtime code not allowed in naked function", .{});4130 try reap(f, inst, &.{operand});
4856 }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
4858 // Now we have the "then branch" (in terms of the liveness data); process any deaths.4143 // Now we have the "then branch" (in terms of the liveness data); process any deaths.
4859 for (liveness_condbr.then_deaths) |death| {4144 for (liveness_condbr.then_deaths) |death| {
4860 try die(f, inst, death.toRef());4145 try die(f, inst, death.toRef());
4861 }4146 }
48624147
4863 if (!payload_has_bits) {4148 if (!payload_ty.hasRuntimeBits(zcu)) {
4864 if (!is_ptr) {4149 if (!is_ptr) {
4865 return .none;4150 return .none;
4866 } else {4151 } else {
...@@ -4873,14 +4158,14 @@ fn lowerTry(...@@ -4873,14 +4158,14 @@ fn lowerTry(
4873 if (f.liveness.isUnused(inst)) return .none;4158 if (f.liveness.isUnused(inst)) return .none;
48744159
4875 const local = try f.allocLocal(inst, inst_ty);4160 const local = try f.allocLocal(inst, inst_ty);
4876 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));4161 try f.writeCValue(w, local, .other);
4877 try f.writeCValue(w, local, .Other);4162 try w.writeAll(" = ");
4878 try a.assign(f, w);
4879 if (is_ptr) {4163 if (is_ptr) {
4880 try w.writeByte('&');4164 try w.writeByte('&');
4881 try f.writeCValueDerefMember(w, err_union, .{ .identifier = "payload" });4165 try f.writeCValueDerefMember(w, err_union, .{ .identifier = "payload" });
4882 } else try f.writeCValueMember(w, err_union, .{ .identifier = "payload" });4166 } else try f.writeCValueMember(w, err_union, .{ .identifier = "payload" });
4883 try a.end(f, w);4167 try w.writeByte(';');
4168 try f.newline();
4884 return local;4169 return local;
4885}4170}
48864171
...@@ -4888,25 +4173,24 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {...@@ -4888,25 +4173,24 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
4888 const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br;4173 const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
4889 const block = f.blocks.get(branch.block_inst).?;4174 const block = f.blocks.get(branch.block_inst).?;
4890 const result = block.result;4175 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) {
4894 if (result != .none) return f.fail("runtime code not allowed in naked function", .{});4179 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;
4896 return;4181 return;
4897 }4182 }
48984183
4899 // If result is .none then the value of the block is unused.4184 // If result is .none then the value of the block is unused.
4900 if (result != .none) {4185 if (result != .none) {
4901 const operand_ty = f.typeOf(branch.operand);
4902 const operand = try f.resolveInst(branch.operand);4186 const operand = try f.resolveInst(branch.operand);
4903 try reap(f, inst, &.{branch.operand});4187 try reap(f, inst, &.{branch.operand});
49044188
4905 const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete));4189 try f.writeCValue(w, result, .other);
4906 try f.writeCValue(w, result, .Other);4190 try w.writeAll(" = ");
4907 try a.assign(f, w);4191 try f.writeCValue(w, operand, .other);
4908 try f.writeCValue(w, operand, .Other);4192 try w.writeByte(';');
4909 try a.end(f, w);4193 try f.newline();
4910 }4194 }
49114195
4912 try w.print("goto zig_block_{d};\n", .{block.block_id});4196 try w.print("goto zig_block_{d};\n", .{block.block_id});
...@@ -4914,14 +4198,14 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {...@@ -4914,14 +4198,14 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !void {
49144198
4915fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {4199fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {
4916 const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat;4200 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)});
4918}4202}
49194203
4920fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {4204fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
4921 const pt = f.object.dg.pt;4205 const pt = f.dg.pt;
4922 const zcu = pt.zcu;4206 const zcu = pt.zcu;
4923 const br = f.air.instructions.items(.data)[@intFromEnum(inst)].br;4207 const br = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
4924 const w = &f.object.code.writer;4208 const w = &f.code.writer;
49254209
4926 if (try f.air.value(br.operand, pt)) |cond_val| {4210 if (try f.air.value(br.operand, pt)) |cond_val| {
4927 // Comptime-known dispatch. Iterate the cases to find the correct4211 // Comptime-known dispatch. Iterate the cases to find the correct
...@@ -4950,11 +4234,11 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {...@@ -4950,11 +4234,11 @@ fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
4950 // Runtime-known dispatch. Set the switch condition, and branch back.4234 // Runtime-known dispatch. Set the switch condition, and branch back.
4951 const cond = try f.resolveInst(br.operand);4235 const cond = try f.resolveInst(br.operand);
4952 const cond_local = f.loop_switch_conds.get(br.block_inst).?;4236 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);
4954 try w.writeAll(" = ");4238 try w.writeAll(" = ");
4955 try f.writeCValue(w, cond, .Other);4239 try f.writeCValue(w, cond, .other);
4956 try w.writeByte(';');4240 try w.writeByte(';');
4957 try f.object.newline();4241 try f.newline();
4958 try w.print("goto zig_switch_{d}_loop;\n", .{@intFromEnum(br.block_inst)});4242 try w.print("goto zig_switch_{d}_loop;\n", .{@intFromEnum(br.block_inst)});
4959}4243}
49604244
...@@ -4971,11 +4255,10 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4971,11 +4255,10 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
4971}4255}
49724256
4973fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CValue {4257fn 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;
4975 const zcu = pt.zcu;4259 const zcu = pt.zcu;
4976 const target = &f.object.dg.mod.resolved_target.result;4260 const target = &f.dg.mod.resolved_target.result;
4977 const ctype_pool = &f.object.dg.ctype_pool;4261 const w = &f.code.writer;
4978 const w = &f.object.code.writer;
49794262
4980 if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) {4263 if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) {
4981 const src_info = dest_ty.intInfo(zcu);4264 const src_info = dest_ty.intInfo(zcu);
...@@ -4986,26 +4269,16 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal...@@ -4986,26 +4269,16 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
49864269
4987 if (dest_ty.isPtrAtRuntime(zcu) or operand_ty.isPtrAtRuntime(zcu)) {4270 if (dest_ty.isPtrAtRuntime(zcu) or operand_ty.isPtrAtRuntime(zcu)) {
4988 const local = try f.allocLocal(null, dest_ty);4271 const local = try f.allocLocal(null, dest_ty);
4989 try f.writeCValue(w, local, .Other);4272 try f.writeCValue(w, local, .other);
4990 try w.writeAll(" = (");4273 try w.writeAll(" = (");
4991 try f.renderType(w, dest_ty);4274 try f.renderType(w, dest_ty);
4992 try w.writeByte(')');4275 try w.writeByte(')');
4993 try f.writeCValue(w, operand, .Other);4276 try f.writeCValue(w, operand, .other);
4994 try w.writeByte(';');4277 try w.writeByte(';');
4995 try f.object.newline();4278 try f.newline();
4996 return local;4279 return local;
4997 }4280 }
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
5009 const local = try f.allocLocal(null, dest_ty);4282 const local = try f.allocLocal(null, dest_ty);
5010 // On big-endian targets, copying ABI integers with padding bits is awkward, because the padding bits are at the low bytes of the value.4283 // On big-endian targets, copying ABI integers with padding bits is awkward, because the padding bits are at the low bytes of the value.
5011 // We need to offset the source or destination pointer appropriately and copy the right number of bytes.4284 // 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...@@ -5013,141 +4286,134 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CVal
5013 // e.g. [10]u8 -> u80. We need to offset the destination so that we copy to the least significant bits of the integer.4286 // e.g. [10]u8 -> u80. We need to offset the destination so that we copy to the least significant bits of the integer.
5014 const offset = dest_ty.abiSize(zcu) - operand_ty.abiSize(zcu);4287 const offset = dest_ty.abiSize(zcu) - operand_ty.abiSize(zcu);
5015 try w.writeAll("memcpy((char *)&");4288 try w.writeAll("memcpy((char *)&");
5016 try f.writeCValue(w, local, .Other);4289 try f.writeCValue(w, local, .other);
5017 try w.print(" + {d}, &", .{offset});4290 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 }
5019 try w.print(", {d});", .{operand_ty.abiSize(zcu)});4295 try w.print(", {d});", .{operand_ty.abiSize(zcu)});
5020 } else if (target.cpu.arch.endian() == .big and operand_ty.isAbiInt(zcu) and !dest_ty.isAbiInt(zcu)) {4296 } else if (target.cpu.arch.endian() == .big and operand_ty.isAbiInt(zcu) and !dest_ty.isAbiInt(zcu)) {
5021 // e.g. u80 -> [10]u8. We need to offset the source so that we copy from the least significant bits of the integer.4297 // e.g. u80 -> [10]u8. We need to offset the source so that we copy from the least significant bits of the integer.
5022 const offset = operand_ty.abiSize(zcu) - dest_ty.abiSize(zcu);4298 const offset = operand_ty.abiSize(zcu) - dest_ty.abiSize(zcu);
5023 try w.writeAll("memcpy(&");4299 try w.writeAll("memcpy(&");
5024 try f.writeCValue(w, local, .Other);4300 try f.writeCValue(w, local, .other);
5025 try w.writeAll(", (const char *)&");4301 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 }
5027 try w.print(" + {d}, {d});", .{ offset, dest_ty.abiSize(zcu) });4306 try w.print(" + {d}, {d});", .{ offset, dest_ty.abiSize(zcu) });
5028 } else {4307 } else {
5029 try w.writeAll("memcpy(&");4308 try w.writeAll("memcpy(&");
5030 try f.writeCValue(w, local, .Other);4309 try f.writeCValue(w, local, .other);
5031 try w.writeAll(", &");4310 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 }
5033 try w.print(", {d});", .{@min(dest_ty.abiSize(zcu), operand_ty.abiSize(zcu))});4315 try w.print(", {d});", .{@min(dest_ty.abiSize(zcu), operand_ty.abiSize(zcu))});
5034 }4316 }
50354317
5036 try f.object.newline();4318 try f.newline();
50374319
5038 // Ensure padding bits have the expected value.4320 // Ensure padding bits have the expected value.
5039 if (dest_ty.isAbiInt(zcu)) {4321 if (dest_ty.isAbiInt(zcu)) {
5040 const dest_ctype = try f.ctypeFromType(dest_ty, .complete);4322 switch (CType.classifyInt(dest_ty, zcu)) {
5041 const dest_info = dest_ty.intInfo(zcu);4323 .void => unreachable, // opv
5042 var bits: u16 = dest_info.bits;4324 .small => {
5043 var wrap_ctype: ?CType = null;4325 try f.writeCValue(w, local, .other);
5044 var need_bitcasts = false;4326 try w.writeAll(" = zig_wrap_");
50454327 try f.dg.renderTypeForBuiltinFnName(w, dest_ty);
5046 try f.writeCValue(w, local, .Other);4328 try w.writeByte('(');
5047 switch (dest_ctype.info(ctype_pool)) {4329 try f.writeCValue(w, local, .other);
5048 else => {},4330 try f.dg.renderBuiltinInfo(w, dest_ty, .bits);
5049 .array => |array_info| {4331 try w.writeAll(");");
5050 try w.print("[{d}]", .{switch (target.cpu.arch.endian()) {4332 try f.newline();
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;
5059 },4333 },
5060 }4334 .big => |big| {
5061 try w.writeAll(" = ");4335 const dest_info = dest_ty.intInfo(zcu);
5062 if (need_bitcasts) {4336 const padding_index: u16 = switch (target.cpu.arch.endian()) {
5063 try w.writeAll("zig_bitCast_");4337 .little => big.limbs_len - 1,
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,
5085 .big => 0,4338 .big => 0,
5086 },4339 };
5087 }),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 },
5088 }4360 }
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();
5094 }4361 }
50954362
5096 try f.freeCValue(null, operand_lval);
5097 return local;4363 return local;
5098}4364}
50994365
5100fn airTrap(f: *Function, w: *Writer) !void {4366fn airTrap(f: *Function) !void {
5101 // Not even allowed to call trap in a naked function.4367 // Not even allowed to call trap in a naked function.
5102 if (f.object.dg.is_naked_fn) return;4368 if (f.dg.is_naked_fn) return;
5103 try w.writeAll("zig_trap();\n");4369 try f.code.writer.writeAll("zig_trap();\n");
5104}4370}
51054371
5106fn airBreakpoint(f: *Function) !CValue {4372fn airBreakpoint(f: *Function) !CValue {
5107 const w = &f.object.code.writer;4373 const w = &f.code.writer;
5108 try w.writeAll("zig_breakpoint();");4374 try w.writeAll("zig_breakpoint();");
5109 try f.object.newline();4375 try f.newline();
5110 return .none;4376 return .none;
5111}4377}
51124378
5113fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {4379fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
5114 const w = &f.object.code.writer;4380 const w = &f.code.writer;
5115 const local = try f.allocLocal(inst, .usize);4381 const local = try f.allocLocal(inst, .usize);
5116 try f.writeCValue(w, local, .Other);4382 try f.writeCValue(w, local, .other);
5117 try w.writeAll(" = (");4383 try w.writeAll(" = (");
5118 try f.renderType(w, .usize);4384 try f.renderType(w, .usize);
5119 try w.writeAll(")zig_return_address();");4385 try w.writeAll(")zig_return_address();");
5120 try f.object.newline();4386 try f.newline();
5121 return local;4387 return local;
5122}4388}
51234389
5124fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {4390fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
5125 const w = &f.object.code.writer;4391 const w = &f.code.writer;
5126 const local = try f.allocLocal(inst, .usize);4392 const local = try f.allocLocal(inst, .usize);
5127 try f.writeCValue(w, local, .Other);4393 try f.writeCValue(w, local, .other);
5128 try w.writeAll(" = (");4394 try w.writeAll(" = (");
5129 try f.renderType(w, .usize);4395 try f.renderType(w, .usize);
5130 try w.writeAll(")zig_frame_address();");4396 try w.writeAll(")zig_frame_address();");
5131 try f.object.newline();4397 try f.newline();
5132 return local;4398 return local;
5133}4399}
51344400
5135fn airUnreach(o: *Object) !void {4401fn airUnreach(f: *Function) !void {
5136 // Not even allowed to call unreachable in a naked function.4402 // Not even allowed to call unreachable in a naked function.
5137 if (o.dg.is_naked_fn) return;4403 if (f.dg.is_naked_fn) return;
5138 try o.code.writer.writeAll("zig_unreachable();\n");4404 try f.code.writer.writeAll("zig_unreachable();\n");
5139}4405}
51404406
5141fn airLoop(f: *Function, inst: Air.Inst.Index) !void {4407fn airLoop(f: *Function, inst: Air.Inst.Index) !void {
5142 const block = f.air.unwrapBlock(inst);4408 const block = f.air.unwrapBlock(inst);
5143 const w = &f.object.code.writer;4409 const w = &f.code.writer;
51444410
5145 // `repeat` instructions matching this loop will branch to4411 // `repeat` instructions matching this loop will branch to
5146 // this label. Since we need a label for arbitrary `repeat`4412 // this label. Since we need a label for arbitrary `repeat`
5147 // anyway, there's actually no need to use a "real" looping4413 // anyway, there's actually no need to use a "real" looping
5148 // construct at all!4414 // construct at all!
5149 try w.print("zig_loop_{d}:", .{@intFromEnum(inst)});4415 try w.print("zig_loop_{d}:", .{@intFromEnum(inst)});
5150 try f.object.newline();4416 try f.newline();
5151 try genBodyInner(f, block.body); // no need to restore state, we're noreturn4417 try genBodyInner(f, block.body); // no need to restore state, we're noreturn
5152}4418}
51534419
...@@ -5158,15 +4424,15 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {...@@ -5158,15 +4424,15 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {
5158 const then_body = cond_br.then_body;4424 const then_body = cond_br.then_body;
5159 const else_body = cond_br.else_body;4425 const else_body = cond_br.else_body;
5160 const liveness_condbr = f.liveness.getCondBr(inst);4426 const liveness_condbr = f.liveness.getCondBr(inst);
5161 const w = &f.object.code.writer;4427 const w = &f.code.writer;
51624428
5163 try w.writeAll("if (");4429 try w.writeAll("if (");
5164 try f.writeCValue(w, cond, .Other);4430 try f.writeCValue(w, cond, .other);
5165 try w.writeAll(") ");4431 try w.writeAll(") ");
51664432
5167 try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);4433 try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);
5168 try f.object.newline();4434 try f.newline();
5169 if (else_body.len > 0) if (f.object.dg.expected_block) |_|4435 if (else_body.len > 0) if (f.dg.expected_block) |_|
5170 return f.fail("runtime code not allowed in naked function", .{});4436 return f.fail("runtime code not allowed in naked function", .{});
51714437
5172 // We don't need to use `genBodyResolveState` for the else block, because this instruction is4438 // 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 {...@@ -5184,23 +4450,23 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {
5184}4450}
51854451
5186fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void {4452fn 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;
5188 const zcu = pt.zcu;4454 const zcu = pt.zcu;
5189 const gpa = f.object.dg.gpa;4455 const gpa = f.dg.gpa;
5190 const switch_br = f.air.unwrapSwitch(inst);4456 const switch_br = f.air.unwrapSwitch(inst);
5191 const init_condition = try f.resolveInst(switch_br.operand);4457 const init_condition = try f.resolveInst(switch_br.operand);
5192 try reap(f, inst, &.{switch_br.operand});4458 try reap(f, inst, &.{switch_br.operand});
5193 const condition_ty = f.typeOf(switch_br.operand);4459 const condition_ty = f.typeOf(switch_br.operand);
5194 const w = &f.object.code.writer;4460 const w = &f.code.writer;
51954461
5196 // For dispatches, we will create a local alloc to contain the condition value.4462 // For dispatches, we will create a local alloc to contain the condition value.
5197 // This may not result in optimal codegen for switch loops, but it minimizes the4463 // This may not result in optimal codegen for switch loops, but it minimizes the
5198 // amount of C code we generate, which is probably more desirable here (and is simpler).4464 // amount of C code we generate, which is probably more desirable here (and is simpler).
5199 const condition = if (is_dispatch_loop) cond: {4465 const condition = if (is_dispatch_loop) cond: {
5200 const new_local = try f.allocLocal(inst, condition_ty);4466 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);
5202 try w.print("zig_switch_{d}_loop:", .{@intFromEnum(inst)});4468 try w.print("zig_switch_{d}_loop:", .{@intFromEnum(inst)});
5203 try f.object.newline();4469 try f.newline();
5204 try f.loop_switch_conds.put(gpa, inst, new_local.new_local);4470 try f.loop_switch_conds.put(gpa, inst, new_local.new_local);
5205 break :cond new_local;4471 break :cond new_local;
5206 } else init_condition;4472 } else init_condition;
...@@ -5222,9 +4488,9 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void...@@ -5222,9 +4488,9 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5222 try f.renderType(w, lowered_condition_ty);4488 try f.renderType(w, lowered_condition_ty);
5223 try w.writeByte(')');4489 try w.writeByte(')');
5224 }4490 }
5225 try f.writeCValue(w, condition, .Other);4491 try f.writeCValue(w, condition, .other);
5226 try w.writeAll(") {");4492 try w.writeAll(") {");
5227 f.object.indent();4493 f.indent();
52284494
5229 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1);4495 const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1);
5230 defer gpa.free(liveness.deaths);4496 defer gpa.free(liveness.deaths);
...@@ -5237,7 +4503,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void...@@ -5237,7 +4503,7 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5237 continue;4503 continue;
5238 }4504 }
5239 for (case.items) |item| {4505 for (case.items) |item| {
5240 try f.object.newline();4506 try f.newline();
5241 try w.writeAll("case ");4507 try w.writeAll("case ");
5242 const item_value = try f.air.value(item, pt);4508 const item_value = try f.air.value(item, pt);
5243 // If `item_value` is a pointer with a known integer address, print the address4509 // 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...@@ -5254,28 +4520,28 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5254 try f.renderType(w, .usize);4520 try f.renderType(w, .usize);
5255 try w.writeByte(')');4521 try w.writeByte(')');
5256 }4522 }
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);
5258 }4524 }
5259 try w.writeByte(':');4525 try w.writeByte(':');
5260 }4526 }
5261 try w.writeAll(" {");4527 try w.writeAll(" {");
5262 f.object.indent();4528 f.indent();
5263 try f.object.newline();4529 try f.newline();
5264 if (is_dispatch_loop) {4530 if (is_dispatch_loop) {
5265 try w.print("zig_switch_{d}_dispatch_{d}:;", .{ @intFromEnum(inst), case.idx });4531 try w.print("zig_switch_{d}_dispatch_{d}:;", .{ @intFromEnum(inst), case.idx });
5266 try f.object.newline();4532 try f.newline();
5267 }4533 }
5268 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);4534 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
5269 try f.object.outdent();4535 try f.outdent();
5270 try w.writeByte('}');4536 try w.writeByte('}');
5271 if (f.object.dg.expected_block) |_|4537 if (f.dg.expected_block) |_|
5272 return f.fail("runtime code not allowed in naked function", .{});4538 return f.fail("runtime code not allowed in naked function", .{});
52734539
5274 // The case body must be noreturn so we don't need to insert a break.4540 // The case body must be noreturn so we don't need to insert a break.
5275 }4541 }
52764542
5277 const else_body = it.elseBody();4543 const else_body = it.elseBody();
5278 try f.object.newline();4544 try f.newline();
52794545
5280 try w.writeAll("default: ");4546 try w.writeAll("default: ");
5281 if (any_range_cases) {4547 if (any_range_cases) {
...@@ -5288,33 +4554,33 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void...@@ -5288,33 +4554,33 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5288 try w.writeAll("if (");4554 try w.writeAll("if (");
5289 for (case.items, 0..) |item, item_i| {4555 for (case.items, 0..) |item, item_i| {
5290 if (item_i != 0) try w.writeAll(" || ");4556 if (item_i != 0) try w.writeAll(" || ");
5291 try f.writeCValue(w, condition, .Other);4557 try f.writeCValue(w, condition, .other);
5292 try w.writeAll(" == ");4558 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);
5294 }4560 }
5295 for (case.ranges, 0..) |range, range_i| {4561 for (case.ranges, 0..) |range, range_i| {
5296 if (case.items.len != 0 or range_i != 0) try w.writeAll(" || ");4562 if (case.items.len != 0 or range_i != 0) try w.writeAll(" || ");
5297 // "(x >= lower && x <= upper)"4563 // "(x >= lower && x <= upper)"
5298 try w.writeByte('(');4564 try w.writeByte('(');
5299 try f.writeCValue(w, condition, .Other);4565 try f.writeCValue(w, condition, .other);
5300 try w.writeAll(" >= ");4566 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);
5302 try w.writeAll(" && ");4568 try w.writeAll(" && ");
5303 try f.writeCValue(w, condition, .Other);4569 try f.writeCValue(w, condition, .other);
5304 try w.writeAll(" <= ");4570 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);
5306 try w.writeByte(')');4572 try w.writeByte(')');
5307 }4573 }
5308 try w.writeAll(") {");4574 try w.writeAll(") {");
5309 f.object.indent();4575 f.indent();
5310 try f.object.newline();4576 try f.newline();
5311 if (is_dispatch_loop) {4577 if (is_dispatch_loop) {
5312 try w.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });4578 try w.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });
5313 }4579 }
5314 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);4580 try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
5315 try f.object.outdent();4581 try f.outdent();
5316 try w.writeByte('}');4582 try w.writeByte('}');
5317 if (f.object.dg.expected_block) |_|4583 if (f.dg.expected_block) |_|
5318 return f.fail("runtime code not allowed in naked function", .{});4584 return f.fail("runtime code not allowed in naked function", .{});
5319 }4585 }
5320 }4586 }
...@@ -5328,16 +4594,16 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void...@@ -5328,16 +4594,16 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void
5328 try die(f, inst, death.toRef());4594 try die(f, inst, death.toRef());
5329 }4595 }
5330 try genBody(f, else_body);4596 try genBody(f, else_body);
5331 if (f.object.dg.expected_block) |_|4597 if (f.dg.expected_block) |_|
5332 return f.fail("runtime code not allowed in naked function", .{});4598 return f.fail("runtime code not allowed in naked function", .{});
5333 } else try airUnreach(&f.object);4599 } else try airUnreach(f);
5334 try f.object.newline();4600 try f.newline();
5335 try f.object.outdent();4601 try f.outdent();
5336 try w.writeAll("}\n");4602 try w.writeAll("}\n");
5337}4603}
53384604
5339fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool {4605fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool {
5340 const dg = f.object.dg;4606 const dg = f.dg;
5341 const target = &dg.mod.resolved_target.result;4607 const target = &dg.mod.resolved_target.result;
5342 return switch (constraint[0]) {4608 return switch (constraint[0]) {
5343 '{' => true,4609 '{' => true,
...@@ -5357,28 +4623,28 @@ fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool...@@ -5357,28 +4623,28 @@ fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool
5357}4623}
53584624
5359fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {4625fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5360 const pt = f.object.dg.pt;4626 const pt = f.dg.pt;
5361 const zcu = pt.zcu;4627 const zcu = pt.zcu;
5362 const unwrapped_asm = f.air.unwrapAsm(inst);4628 const unwrapped_asm = f.air.unwrapAsm(inst);
5363 const is_volatile = unwrapped_asm.is_volatile;4629 const is_volatile = unwrapped_asm.is_volatile;
5364 const gpa = f.object.dg.gpa;4630 const gpa = f.dg.gpa;
5365 const outputs = unwrapped_asm.outputs;4631 const outputs = unwrapped_asm.outputs;
5366 const inputs = unwrapped_asm.inputs;4632 const inputs = unwrapped_asm.inputs;
53674633
5368 const result = result: {4634 const result = result: {
5369 const w = &f.object.code.writer;4635 const w = &f.code.writer;
5370 const inst_ty = f.typeOfIndex(inst);4636 const inst_ty = f.typeOfIndex(inst);
5371 const inst_local = if (inst_ty.hasRuntimeBits(zcu)) local: {4637 const inst_local = if (inst_ty.hasRuntimeBits(zcu)) local: {
5372 const inst_local = try f.allocLocalValue(.{4638 const inst_local = try f.allocLocalValue(.{
5373 .ctype = try f.ctypeFromType(inst_ty, .complete),4639 .type = inst_ty,
5374 .alignas = CType.AlignAs.fromAbiAlignment(inst_ty.abiAlignment(zcu)),4640 .alignment = .none,
5375 });4641 });
5376 if (f.wantSafety()) {4642 if (f.wantSafety()) {
5377 try f.writeCValue(w, inst_local, .Other);4643 try f.writeCValue(w, inst_local, .other);
5378 try w.writeAll(" = ");4644 try w.writeAll(" = ");
5379 try f.writeCValue(w, .{ .undef = inst_ty }, .Other);4645 try f.writeCValue(w, .{ .undef = inst_ty }, .other);
5380 try w.writeByte(';');4646 try w.writeByte(';');
5381 try f.object.newline();4647 try f.newline();
5382 }4648 }
5383 break :local inst_local;4649 break :local inst_local;
5384 } else .none;4650 } else .none;
...@@ -5399,20 +4665,20 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5399,20 +4665,20 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5399 const output_ty = if (output.operand == .none) inst_ty else f.typeOf(output.operand).childType(zcu);4665 const output_ty = if (output.operand == .none) inst_ty else f.typeOf(output.operand).childType(zcu);
5400 try w.writeAll("register ");4666 try w.writeAll("register ");
5401 const output_local = try f.allocLocalValue(.{4667 const output_local = try f.allocLocalValue(.{
5402 .ctype = try f.ctypeFromType(output_ty, .complete),4668 .type = output_ty,
5403 .alignas = CType.AlignAs.fromAbiAlignment(output_ty.abiAlignment(zcu)),4669 .alignment = .none,
5404 });4670 });
5405 try f.allocs.put(gpa, output_local.new_local, false);4671 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);
5407 try w.writeAll(" __asm(\"");4673 try w.writeAll(" __asm(\"");
5408 try w.writeAll(constraint["={".len .. constraint.len - "}".len]);4674 try w.writeAll(constraint["={".len .. constraint.len - "}".len]);
5409 try w.writeAll("\")");4675 try w.writeAll("\")");
5410 if (f.wantSafety()) {4676 if (f.wantSafety()) {
5411 try w.writeAll(" = ");4677 try w.writeAll(" = ");
5412 try f.writeCValue(w, .{ .undef = output_ty }, .Other);4678 try f.writeCValue(w, .{ .undef = output_ty }, .other);
5413 }4679 }
5414 try w.writeByte(';');4680 try w.writeByte(';');
5415 try f.object.newline();4681 try f.newline();
5416 }4682 }
5417 }4683 }
54184684
...@@ -5432,29 +4698,29 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5432,29 +4698,29 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5432 const input_ty = f.typeOf(input.operand);4698 const input_ty = f.typeOf(input.operand);
5433 if (is_reg) try w.writeAll("register ");4699 if (is_reg) try w.writeAll("register ");
5434 const input_local = try f.allocLocalValue(.{4700 const input_local = try f.allocLocalValue(.{
5435 .ctype = try f.ctypeFromType(input_ty, .complete),4701 .type = input_ty,
5436 .alignas = CType.AlignAs.fromAbiAlignment(input_ty.abiAlignment(zcu)),4702 .alignment = .none,
5437 });4703 });
5438 try f.allocs.put(gpa, input_local.new_local, false);4704 try f.allocs.put(gpa, input_local.new_local, false);
5439 // Do not render the declaration as `const` qualified if we're generating an4705 // Do not render the declaration as `const` qualified if we're generating an
5440 // explicit `register` local, as GCC will ignore the constraint completely.4706 // 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);
5442 if (is_reg) {4708 if (is_reg) {
5443 try w.writeAll(" __asm(\"");4709 try w.writeAll(" __asm(\"");
5444 try w.writeAll(constraint["{".len .. constraint.len - "}".len]);4710 try w.writeAll(constraint["{".len .. constraint.len - "}".len]);
5445 try w.writeAll("\")");4711 try w.writeAll("\")");
5446 }4712 }
5447 try w.writeAll(" = ");4713 try w.writeAll(" = ");
5448 try f.writeCValue(w, input_val, .Other);4714 try f.writeCValue(w, input_val, .other);
5449 try w.writeByte(';');4715 try w.writeByte(';');
5450 try f.object.newline();4716 try f.newline();
5451 }4717 }
5452 }4718 }
54534719
5454 {4720 {
5455 const asm_source = unwrapped_asm.source;4721 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);
5458 const allocator = stack.get();4724 const allocator = stack.get();
5459 const fixed_asm_source = try allocator.alloc(u8, asm_source.len);4725 const fixed_asm_source = try allocator.alloc(u8, asm_source.len);
5460 defer allocator.free(fixed_asm_source);4726 defer allocator.free(fixed_asm_source);
...@@ -5520,10 +4786,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5520,10 +4786,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5520 const is_reg = constraint[1] == '{';4786 const is_reg = constraint[1] == '{';
5521 try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)});4787 try w.print("{f}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)});
5522 if (is_reg) {4788 if (is_reg) {
5523 try f.writeCValue(w, .{ .local = locals_index }, .Other);4789 try f.writeCValue(w, .{ .local = locals_index }, .other);
5524 locals_index += 1;4790 locals_index += 1;
5525 } else if (output.operand == .none) {4791 } else if (output.operand == .none) {
5526 try f.writeCValue(w, inst_local, .FunctionArgument);4792 try f.writeCValue(w, inst_local, .other);
5527 } else {4793 } else {
5528 try f.writeCValueDeref(w, try f.resolveInst(output.operand));4794 try f.writeCValueDeref(w, try f.resolveInst(output.operand));
5529 }4795 }
...@@ -5547,7 +4813,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5547,7 +4813,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5547 const input_local_idx = locals_index;4813 const input_local_idx = locals_index;
5548 locals_index += 1;4814 locals_index += 1;
5549 break :local .{ .local = input_local_idx };4815 break :local .{ .local = input_local_idx };
5550 } else input_val, .Other);4816 } else input_val, .other);
5551 try w.writeByte(')');4817 try w.writeByte(')');
5552 }4818 }
5553 try w.writeByte(':');4819 try w.writeByte(':');
...@@ -5567,7 +4833,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5567,7 +4833,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5567 const field_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;4833 const field_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
5568 assert(field_name.len != 0);4834 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;
5571 var c_name_buf: [16]u8 = undefined;4837 var c_name_buf: [16]u8 = undefined;
5572 const name =4838 const name =
5573 if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: {4839 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 {...@@ -5594,7 +4860,7 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5594 }4860 }
5595 w.undo(1); // erase the last comma4861 w.undo(1); // erase the last comma
5596 try w.writeAll(");");4862 try w.writeAll(");");
5597 try f.object.newline();4863 try f.newline();
55984864
5599 locals_index = locals_begin;4865 locals_index = locals_begin;
5600 it = unwrapped_asm.iterateOutputs();4866 it = unwrapped_asm.iterateOutputs();
...@@ -5608,10 +4874,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5608,10 +4874,10 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5608 else4874 else
5609 try f.resolveInst(output.operand));4875 try f.resolveInst(output.operand));
5610 try w.writeAll(" = ");4876 try w.writeAll(" = ");
5611 try f.writeCValue(w, .{ .local = locals_index }, .Other);4877 try f.writeCValue(w, .{ .local = locals_index }, .other);
5612 locals_index += 1;4878 locals_index += 1;
5613 try w.writeByte(';');4879 try w.writeByte(';');
5614 try f.object.newline();4880 try f.newline();
5615 }4881 }
5616 }4882 }
56174883
...@@ -5633,147 +4899,145 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5633,147 +4899,145 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5633fn airIsNull(4899fn airIsNull(
5634 f: *Function,4900 f: *Function,
5635 inst: Air.Inst.Index,4901 inst: Air.Inst.Index,
5636 operator: std.math.CompareOperator,4902 operator: enum { eq, neq },
5637 is_ptr: bool,4903 is_ptr: bool,
5638) !CValue {4904) !CValue {
5639 const pt = f.object.dg.pt;4905 const pt = f.dg.pt;
5640 const zcu = pt.zcu;4906 const zcu = pt.zcu;
5641 const ctype_pool = &f.object.dg.ctype_pool;
5642 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4907 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;
5645 const operand = try f.resolveInst(un_op);4910 const operand = try f.resolveInst(un_op);
5646 try reap(f, inst, &.{un_op});4911 try reap(f, inst, &.{un_op});
56474912
5648 const local = try f.allocLocal(inst, .bool);4913 const local = try f.allocLocal(inst, .bool);
5649 const a = try Assignment.start(f, w, .bool);4914 try f.writeCValue(w, local, .other);
5650 try f.writeCValue(w, local, .Other);4915 try w.writeAll(" = ");
5651 try a.assign(f, w);
56524916
5653 const operand_ty = f.typeOf(un_op);4917 const operand_ty = f.typeOf(un_op);
5654 const optional_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;4918 const optional_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
5655 const opt_ctype = try f.ctypeFromType(optional_ty, .complete);4919
5656 const rhs = switch (opt_ctype.info(ctype_pool)) {4920 const pre: []const u8, const maybe_field: ?[]const u8, const post: []const u8 = switch (operator) {
5657 .basic, .pointer => rhs: {4921 // zig fmt: off
5658 if (is_ptr)4922 .eq => switch (CType.classifyOptional(optional_ty, zcu)) {
5659 try f.writeCValueDeref(w, operand)4923 .npv_payload => unreachable, // opv optional
5660 else4924 .error_set => .{ "", null, " == 0" },
5661 try f.writeCValue(w, operand, .Other);4925 .ptr_like => .{ "", null, " == NULL" },
5662 break :rhs if (opt_ctype.isBool())4926 .slice_like => .{ "", "ptr", " == NULL" },
5663 "true"4927 .opv_payload => .{ "", "is_null", "" },
5664 else if (opt_ctype.isInteger())4928 .@"struct" => .{ "", "is_null", "" },
5665 "0"
5666 else
5667 "NULL";
5668 },4929 },
5669 .aligned, .array, .vector, .fwd_decl, .function => unreachable,4930 .neq => switch (CType.classifyOptional(optional_ty, zcu)) {
5670 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {4931 .npv_payload => unreachable, // opv optional
5671 .is_null, .payload => rhs: {4932 .error_set => .{ "", null, " != 0" },
5672 if (is_ptr)4933 .ptr_like => .{ "", null, " != NULL" },
5673 try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" })4934 .slice_like => .{ "", "ptr", " != NULL" },
5674 else4935 .opv_payload => .{ "!", "is_null", "" },
5675 try f.writeCValueMember(w, operand, .{ .identifier = "is_null" });4936 .@"struct" => .{ "!", "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,
5686 },4937 },
4938 // zig fmt: on
5687 };4939 };
5688 try w.writeAll(compareOperatorC(operator));4940
5689 try w.writeAll(rhs);4941 try w.writeAll(pre);
5690 try a.end(f, w);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();
5691 return local;4959 return local;
5692}4960}
56934961
5694fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {4962fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
5695 const pt = f.object.dg.pt;4963 const pt = f.dg.pt;
5696 const zcu = pt.zcu;4964 const zcu = pt.zcu;
5697 const ctype_pool = &f.object.dg.ctype_pool;
5698 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4965 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
56994966
5700 const inst_ty = f.typeOfIndex(inst);4967 const inst_ty = f.typeOfIndex(inst);
5701 const operand_ty = f.typeOf(ty_op.operand);4968 const operand_ty = f.typeOf(ty_op.operand);
5702 const opt_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;4969 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
5706 const operand = try f.resolveInst(ty_op.operand);4971 const operand = try f.resolveInst(ty_op.operand);
5707 switch (opt_ctype.info(ctype_pool)) {4972
5708 .basic, .pointer => return f.moveCValue(inst, inst_ty, operand),4973 switch (CType.classifyOptional(opt_ty, zcu)) {
5709 .aligned, .array, .vector, .fwd_decl, .function => unreachable,4974 .npv_payload => unreachable, // opv optional
5710 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {4975
5711 .is_null, .payload => {4976 .opv_payload => return if (is_ptr) .{ .undef = inst_ty } else .none,
5712 const w = &f.object.code.writer;4977
5713 const local = try f.allocLocal(inst, inst_ty);4978 .error_set,
5714 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));4979 .ptr_like,
5715 try f.writeCValue(w, local, .Other);4980 .slice_like,
5716 try a.assign(f, w);4981 => return f.moveCValue(inst, inst_ty, operand),
5717 if (is_ptr) {4982
5718 try w.writeByte('&');4983 .@"struct" => {
5719 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });4984 const w = &f.code.writer;
5720 } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" });4985 const local = try f.allocLocal(inst, inst_ty);
5721 try a.end(f, w);4986 try f.writeCValue(w, local, .other);
5722 return local;4987 try w.writeAll(" = ");
5723 },4988 if (is_ptr) {
5724 .ptr, .len => return f.moveCValue(inst, inst_ty, operand),4989 try w.writeByte('&');
5725 else => unreachable,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;
5726 },4995 },
5727 }4996 }
5728}4997}
57294998
5730fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {4999fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
5731 const pt = f.object.dg.pt;5000 const pt = f.dg.pt;
5732 const zcu = pt.zcu;5001 const zcu = pt.zcu;
5733 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5002 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;
5735 const operand = try f.resolveInst(ty_op.operand);5004 const operand = try f.resolveInst(ty_op.operand);
5736 try reap(f, inst, &.{ty_op.operand});5005 try reap(f, inst, &.{ty_op.operand});
5737 const operand_ty = f.typeOf(ty_op.operand);5006 const operand_ty = f.typeOf(ty_op.operand);
5007 const opt_ty = operand_ty.childType(zcu);
57385008
5739 const inst_ty = f.typeOfIndex(inst);5009 const inst_ty = f.typeOfIndex(inst);
5740 const opt_ctype = try f.ctypeFromType(operand_ty.childType(zcu), .complete);5010
5741 switch (opt_ctype.info(&f.object.dg.ctype_pool)) {5011 switch (CType.classifyOptional(opt_ty, zcu)) {
5742 .basic => {5012 .npv_payload => unreachable, // opv optional
5743 const a = try Assignment.start(f, w, opt_ctype);5013
5744 try f.writeCValueDeref(w, operand);5014 .opv_payload => {
5745 try a.assign(f, w);5015 try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" });
5746 try f.object.dg.renderValue(w, Value.false, .Other);5016 try w.writeAll(" = ");
5747 try a.end(f, w);5017 try f.dg.renderValue(w, .false, .other);
5748 return .none;5018 try w.writeByte(';');
5749 },5019 try f.newline();
5750 .pointer => {5020 return .{ .undef = inst_ty };
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;
5759 },5021 },
5760 .aligned, .array, .vector, .fwd_decl, .function => unreachable,5022
5761 .aggregate => {5023 .error_set,
5762 {5024 .ptr_like,
5763 const a = try Assignment.start(f, w, opt_ctype);5025 .slice_like,
5764 try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" });5026 => return f.moveCValue(inst, inst_ty, operand),
5765 try a.assign(f, w);5027
5766 try f.object.dg.renderValue(w, Value.false, .Other);5028 .@"struct" => {
5767 try a.end(f, w);5029 try f.writeCValueDerefMember(w, operand, .{ .identifier = "is_null" });
5768 }5030 try w.writeAll(" = ");
5031 try f.dg.renderValue(w, .false, .other);
5032 try w.writeByte(';');
5033 try f.newline();
5769 if (f.liveness.isUnused(inst)) return .none;5034 if (f.liveness.isUnused(inst)) return .none;
5770 const local = try f.allocLocal(inst, inst_ty);5035 const local = try f.allocLocal(inst, inst_ty);
5771 const a = try Assignment.start(f, w, opt_ctype);5036 try f.writeCValue(w, local, .other);
5772 try f.writeCValue(w, local, .Other);5037 try w.writeAll(" = &");
5773 try a.assign(f, w);
5774 try w.writeByte('&');
5775 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });5038 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });
5776 try a.end(f, w);5039 try w.writeByte(';');
5040 try f.newline();
5777 return local;5041 return local;
5778 },5042 },
5779 }5043 }
...@@ -5817,18 +5081,20 @@ fn fieldLocation(...@@ -5817,18 +5081,20 @@ fn fieldLocation(
5817 .union_type => {5081 .union_type => {
5818 const loaded_union = ip.loadUnionType(container_ty.toIntern());5082 const loaded_union = ip.loadUnionType(container_ty.toIntern());
5819 switch (loaded_union.layout) {5083 switch (loaded_union.layout) {
5820 .auto, .@"extern" => {5084 .auto => {
5821 const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);5085 const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
5822 if (!field_ty.hasRuntimeBits(zcu))5086 if (!field_ty.hasRuntimeBits(zcu)) {
5823 return if (loaded_union.has_runtime_tag and !container_ty.unionHasAllZeroBitFieldTypes(zcu))5087 if (container_ty.unionHasAllZeroBitFieldTypes(zcu)) return .begin;
5824 .{ .field = .{ .identifier = "payload" } }5088 return .{ .field = .{ .identifier = "payload" } };
5825 else5089 }
5826 .begin;
5827 const field_name = ip.loadEnumType(loaded_union.enum_tag_type).field_names.get(ip)[field_index];5090 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)5091 return .{ .field = .{ .payload_identifier = field_name.toSlice(ip) } };
5829 .{ .payload_identifier = field_name.toSlice(ip) }5092 },
5830 else5093 .@"extern" => {
5831 .{ .identifier = field_name.toSlice(ip) } };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) } };
5832 },5098 },
5833 .@"packed" => return .begin,5099 .@"packed" => return .begin,
5834 }5100 }
...@@ -5865,7 +5131,7 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue...@@ -5865,7 +5131,7 @@ fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue
5865}5131}
58665132
5867fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {5133fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5868 const pt = f.object.dg.pt;5134 const pt = f.dg.pt;
5869 const zcu = pt.zcu;5135 const zcu = pt.zcu;
5870 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5136 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
5871 const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;5137 const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
...@@ -5877,26 +5143,26 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5877,26 +5143,26 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5877 const field_ptr_val = try f.resolveInst(extra.field_ptr);5143 const field_ptr_val = try f.resolveInst(extra.field_ptr);
5878 try reap(f, inst, &.{extra.field_ptr});5144 try reap(f, inst, &.{extra.field_ptr});
58795145
5880 const w = &f.object.code.writer;5146 const w = &f.code.writer;
5881 const local = try f.allocLocal(inst, container_ptr_ty);5147 const local = try f.allocLocal(inst, container_ptr_ty);
5882 try f.writeCValue(w, local, .Other);5148 try f.writeCValue(w, local, .other);
5883 try w.writeAll(" = (");5149 try w.writeAll(" = (");
5884 try f.renderType(w, container_ptr_ty);5150 try f.renderType(w, container_ptr_ty);
5885 try w.writeByte(')');5151 try w.writeByte(')');
58865152
5887 switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, zcu)) {5153 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),
5889 .field => |field| {5155 .field => |field| {
5890 const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);5156 const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);
58915157
5892 try w.writeAll("((");5158 try w.writeAll("((");
5893 try f.renderType(w, u8_ptr_ty);5159 try f.renderType(w, u8_ptr_ty);
5894 try w.writeByte(')');5160 try w.writeByte(')');
5895 try f.writeCValue(w, field_ptr_val, .Other);5161 try f.writeCValue(w, field_ptr_val, .other);
5896 try w.writeAll(" - offsetof(");5162 try w.writeAll(" - offsetof(");
5897 try f.renderType(w, container_ty);5163 try f.renderType(w, container_ty);
5898 try w.writeAll(", ");5164 try w.writeAll(", ");
5899 try f.writeCValue(w, field, .Other);5165 try f.writeCValue(w, field, .other);
5900 try w.writeAll("))");5166 try w.writeAll("))");
5901 },5167 },
5902 .byte_offset => |byte_offset| {5168 .byte_offset => |byte_offset| {
...@@ -5905,7 +5171,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5905,7 +5171,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5905 try w.writeAll("((");5171 try w.writeAll("((");
5906 try f.renderType(w, u8_ptr_ty);5172 try f.renderType(w, u8_ptr_ty);
5907 try w.writeByte(')');5173 try w.writeByte(')');
5908 try f.writeCValue(w, field_ptr_val, .Other);5174 try f.writeCValue(w, field_ptr_val, .other);
5909 try w.print(" - {f})", .{5175 try w.print(" - {f})", .{
5910 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),5176 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
5911 });5177 });
...@@ -5913,7 +5179,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5913,7 +5179,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5913 }5179 }
59145180
5915 try w.writeByte(';');5181 try w.writeByte(';');
5916 try f.object.newline();5182 try f.newline();
5917 return local;5183 return local;
5918}5184}
59195185
...@@ -5924,23 +5190,19 @@ fn fieldPtr(...@@ -5924,23 +5190,19 @@ fn fieldPtr(
5924 container_ptr_val: CValue,5190 container_ptr_val: CValue,
5925 field_index: u32,5191 field_index: u32,
5926) !CValue {5192) !CValue {
5927 const pt = f.object.dg.pt;5193 const pt = f.dg.pt;
5928 const zcu = pt.zcu;5194 const zcu = pt.zcu;
5929 const container_ty = container_ptr_ty.childType(zcu);
5930 const field_ptr_ty = f.typeOfIndex(inst);5195 const field_ptr_ty = f.typeOfIndex(inst);
59315196
5932 // Ensure complete type definition is visible before accessing fields.5197 const w = &f.code.writer;
5933 _ = try f.ctypeFromType(container_ty, .complete);
5934
5935 const w = &f.object.code.writer;
5936 const local = try f.allocLocal(inst, field_ptr_ty);5198 const local = try f.allocLocal(inst, field_ptr_ty);
5937 try f.writeCValue(w, local, .Other);5199 try f.writeCValue(w, local, .other);
5938 try w.writeAll(" = (");5200 try w.writeAll(" = (");
5939 try f.renderType(w, field_ptr_ty);5201 try f.renderType(w, field_ptr_ty);
5940 try w.writeByte(')');5202 try w.writeByte(')');
59415203
5942 switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, zcu)) {5204 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),
5944 .field => |field| {5206 .field => |field| {
5945 try w.writeByte('&');5207 try w.writeByte('&');
5946 try f.writeCValueDerefMember(w, container_ptr_val, field);5208 try f.writeCValueDerefMember(w, container_ptr_val, field);
...@@ -5951,7 +5213,7 @@ fn fieldPtr(...@@ -5951,7 +5213,7 @@ fn fieldPtr(
5951 try w.writeAll("((");5213 try w.writeAll("((");
5952 try f.renderType(w, u8_ptr_ty);5214 try f.renderType(w, u8_ptr_ty);
5953 try w.writeByte(')');5215 try w.writeByte(')');
5954 try f.writeCValue(w, container_ptr_val, .Other);5216 try f.writeCValue(w, container_ptr_val, .other);
5955 try w.print(" + {f})", .{5217 try w.print(" + {f})", .{
5956 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),5218 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
5957 });5219 });
...@@ -5959,12 +5221,12 @@ fn fieldPtr(...@@ -5959,12 +5221,12 @@ fn fieldPtr(
5959 }5221 }
59605222
5961 try w.writeByte(';');5223 try w.writeByte(';');
5962 try f.object.newline();5224 try f.newline();
5963 return local;5225 return local;
5964}5226}
59655227
5966fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {5228fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
5967 const pt = f.object.dg.pt;5229 const pt = f.dg.pt;
5968 const zcu = pt.zcu;5230 const zcu = pt.zcu;
5969 const ip = &zcu.intern_pool;5231 const ip = &zcu.intern_pool;
5970 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5232 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 {...@@ -5976,10 +5238,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
5976 const struct_byval = try f.resolveInst(extra.struct_operand);5238 const struct_byval = try f.resolveInst(extra.struct_operand);
5977 try reap(f, inst, &.{extra.struct_operand});5239 try reap(f, inst, &.{extra.struct_operand});
5978 const struct_ty = f.typeOf(extra.struct_operand);5240 const struct_ty = f.typeOf(extra.struct_operand);
5979 const w = &f.object.code.writer;5241 const w = &f.code.writer;
5980
5981 // Ensure complete type definition is visible before accessing fields.
5982 _ = try f.ctypeFromType(struct_ty, .complete);
59835242
5984 assert(struct_ty.containerLayout(zcu) != .@"packed"); // `Air.Legalize.Feature.expand_packed_struct_field_val` handles this case5243 assert(struct_ty.containerLayout(zcu) != .@"packed"); // `Air.Legalize.Feature.expand_packed_struct_field_val` handles this case
5985 const field_name: CValue = switch (ip.indexToKey(struct_ty.toIntern())) {5244 const field_name: CValue = switch (ip.indexToKey(struct_ty.toIntern())) {
...@@ -5988,29 +5247,25 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5988,29 +5247,25 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
5988 const union_type = ip.loadUnionType(struct_ty.toIntern());5247 const union_type = ip.loadUnionType(struct_ty.toIntern());
5989 const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type);5248 const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type);
5990 const field_name_str = enum_tag_ty.enumFieldName(extra.field_index, zcu).toSlice(ip);5249 const field_name_str = enum_tag_ty.enumFieldName(extra.field_index, zcu).toSlice(ip);
5991 if (union_type.has_runtime_tag) {5250 break :name .{ .payload_identifier = field_name_str };
5992 break :name .{ .payload_identifier = field_name_str };
5993 } else {
5994 break :name .{ .identifier = field_name_str };
5995 }
5996 },5251 },
5997 .tuple_type => .{ .field = extra.field_index },5252 .tuple_type => .{ .field = extra.field_index },
5998 else => unreachable,5253 else => unreachable,
5999 };5254 };
60005255
6001 const local = try f.allocLocal(inst, inst_ty);5256 const local = try f.allocLocal(inst, inst_ty);
6002 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));5257 try f.writeCValue(w, local, .other);
6003 try f.writeCValue(w, local, .Other);5258 try w.writeAll(" = ");
6004 try a.assign(f, w);
6005 try f.writeCValueMember(w, struct_byval, field_name);5259 try f.writeCValueMember(w, struct_byval, field_name);
6006 try a.end(f, w);5260 try w.writeByte(';');
5261 try f.newline();
6007 return local;5262 return local;
6008}5263}
60095264
6010/// *(E!T) -> E5265/// *(E!T) -> E
6011/// Note that the result is never a pointer.5266/// Note that the result is never a pointer.
6012fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {5267fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6013 const pt = f.object.dg.pt;5268 const pt = f.dg.pt;
6014 const zcu = pt.zcu;5269 const zcu = pt.zcu;
6015 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5270 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 {...@@ -6020,37 +5275,23 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6020 try reap(f, inst, &.{ty_op.operand});5275 try reap(f, inst, &.{ty_op.operand});
60215276
6022 const operand_is_ptr = operand_ty.zigTypeTag(zcu) == .pointer;5277 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);
6026 const local = try f.allocLocal(inst, inst_ty);5278 const local = try f.allocLocal(inst, inst_ty);
60275279
6028 if (!payload_ty.hasRuntimeBits(zcu) and operand == .local and operand.local == local.new_local) {5280 const w = &f.code.writer;
6029 // The store will be 'x = x'; elide it.5281 try f.writeCValue(w, local, .other);
6030 return local;
6031 }
6032
6033 const w = &f.object.code.writer;
6034 try f.writeCValue(w, local, .Other);
6035 try w.writeAll(" = ");5282 try w.writeAll(" = ");
60365283
6037 if (!payload_ty.hasRuntimeBits(zcu))5284 if (operand_is_ptr)
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)
6044 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })5285 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })
6045 else5286 else
6046 try f.writeCValueMember(w, operand, .{ .identifier = "error" });5287 try f.writeCValueMember(w, operand, .{ .identifier = "error" });
6047 try w.writeByte(';');5288 try w.writeByte(';');
6048 try f.object.newline();5289 try f.newline();
6049 return local;5290 return local;
6050}5291}
60515292
6052fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {5293fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
6053 const pt = f.object.dg.pt;5294 const pt = f.dg.pt;
6054 const zcu = pt.zcu;5295 const zcu = pt.zcu;
6055 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5296 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...@@ -6060,154 +5301,124 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu
6060 const operand_ty = f.typeOf(ty_op.operand);5301 const operand_ty = f.typeOf(ty_op.operand);
6061 const error_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;5302 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;
6064 if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {5305 if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {
6065 if (!is_ptr) return .none;5306 assert(is_ptr); // opv bug in sema
6066
6067 const local = try f.allocLocal(inst, inst_ty);5307 const local = try f.allocLocal(inst, inst_ty);
6068 try f.writeCValue(w, local, .Other);5308 try f.writeCValue(w, local, .other);
6069 try w.writeAll(" = (");5309 try w.writeAll(" = (");
6070 try f.renderType(w, inst_ty);5310 try f.renderType(w, inst_ty);
6071 try w.writeByte(')');5311 try w.writeByte(')');
6072 try f.writeCValue(w, operand, .Other);5312 try f.writeCValue(w, operand, .other);
6073 try w.writeByte(';');5313 try w.writeByte(';');
6074 try f.object.newline();5314 try f.newline();
6075 return local;5315 return local;
6076 }5316 }
60775317
6078 const local = try f.allocLocal(inst, inst_ty);5318 const local = try f.allocLocal(inst, inst_ty);
6079 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));5319 try f.writeCValue(w, local, .other);
6080 try f.writeCValue(w, local, .Other);5320 try w.writeAll(" = ");
6081 try a.assign(f, w);
6082 if (is_ptr) {5321 if (is_ptr) {
6083 try w.writeByte('&');5322 try w.writeByte('&');
6084 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });5323 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });
6085 } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" });5324 } else try f.writeCValueMember(w, operand, .{ .identifier = "payload" });
6086 try a.end(f, w);5325 try w.writeByte(';');
5326 try f.newline();
6087 return local;5327 return local;
6088}5328}
60895329
6090fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {5330fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
6091 const ctype_pool = &f.object.dg.ctype_pool;5331 const zcu = f.dg.pt.zcu;
6092 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5332 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
60935333
6094 const inst_ty = f.typeOfIndex(inst);5334 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
6098 const operand = try f.resolveInst(ty_op.operand);5336 const operand = try f.resolveInst(ty_op.operand);
6099 switch (inst_ctype.info(ctype_pool)) {5337
6100 .basic, .pointer => return f.moveCValue(inst, inst_ty, operand),5338 switch (CType.classifyOptional(inst_ty, zcu)) {
6101 .aligned, .array, .vector, .fwd_decl, .function => unreachable,5339 .npv_payload => unreachable, // opv optional
6102 .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {5340
6103 .is_null, .payload => {5341 .opv_payload => unreachable, // opv bug in Sema
6104 const operand_ctype = try f.ctypeFromType(f.typeOf(ty_op.operand), .complete);5342
6105 const w = &f.object.code.writer;5343 .error_set,
6106 const local = try f.allocLocal(inst, inst_ty);5344 .ptr_like,
6107 {5345 .slice_like,
6108 const a = try Assignment.start(f, w, .bool);5346 => return f.moveCValue(inst, inst_ty, operand),
6109 try f.writeCValueMember(w, local, .{ .identifier = "is_null" });5347
6110 try a.assign(f, w);5348 .@"struct" => {
6111 try w.writeAll("false");5349 const w = &f.code.writer;
6112 try a.end(f, w);5350 const local = try f.allocLocal(inst, inst_ty);
6113 }5351
6114 {5352 try f.writeCValueMember(w, local, .{ .identifier = "is_null" });
6115 const a = try Assignment.start(f, w, operand_ctype);5353 try w.writeAll(" = false;");
6116 try f.writeCValueMember(w, local, .{ .identifier = "payload" });5354 try f.newline();
6117 try a.assign(f, w);5355
6118 try f.writeCValue(w, operand, .Other);5356 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6119 try a.end(f, w);5357 try w.writeAll(" = ");
6120 }5358 try f.writeCValue(w, operand, .other);
6121 return local;5359 try w.writeByte(';');
6122 },5360 try f.newline();
6123 .ptr, .len => return f.moveCValue(inst, inst_ty, operand),5361
6124 else => unreachable,5362 return local;
6125 },5363 },
6126 }5364 }
6127}5365}
61285366
6129fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {5367fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
6130 const pt = f.object.dg.pt;5368 const pt = f.dg.pt;
6131 const zcu = pt.zcu;5369 const zcu = pt.zcu;
6132 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5370 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
61335371
6134 const inst_ty = f.typeOfIndex(inst);5372 const inst_ty = f.typeOfIndex(inst);
6135 const payload_ty = inst_ty.errorUnionPayload(zcu);5373 const payload_ty = inst_ty.errorUnionPayload(zcu);
6136 const repr_is_err = !payload_ty.hasRuntimeBits(zcu);
6137 const err_ty = inst_ty.errorUnionSet(zcu);
6138 const err = try f.resolveInst(ty_op.operand);5374 const err = try f.resolveInst(ty_op.operand);
6139 try reap(f, inst, &.{ty_op.operand});5375 try reap(f, inst, &.{ty_op.operand});
61405376
6141 const w = &f.object.code.writer;5377 const w = &f.code.writer;
6142 const local = try f.allocLocal(inst, inst_ty);5378 const local = try f.allocLocal(inst, inst_ty);
61435379
6144 if (repr_is_err and err == .local and err.local == local.new_local) {5380 if (payload_ty.hasRuntimeBits(zcu)) {
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));
6151 try f.writeCValueMember(w, local, .{ .identifier = "payload" });5381 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6152 try a.assign(f, w);5382 try w.writeAll(" = ");
6153 try f.object.dg.renderUndefValue(w, payload_ty, .Other);5383 try f.dg.renderUndefValue(w, payload_ty, .other);
6154 try a.end(f, w);5384 try w.writeByte(';');
6155 }5385 try f.newline();
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);
6165 }5386 }
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
6166 return local;5394 return local;
6167}5395}
61685396
6169fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {5397fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
6170 const pt = f.object.dg.pt;5398 const pt = f.dg.pt;
6171 const zcu = pt.zcu;5399 const w = &f.code.writer;
6172 const w = &f.object.code.writer;
6173 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5400 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
6174 const inst_ty = f.typeOfIndex(inst);5401 const inst_ty = f.typeOfIndex(inst);
6175 const operand = try f.resolveInst(ty_op.operand);5402 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);
6180 const err_int_ty = try pt.errorIntType();5404 const err_int_ty = try pt.errorIntType();
6181 const no_err = try pt.intValue(err_int_ty, 0);5405 const no_err = try pt.intValue(err_int_ty, 0);
6182 try reap(f, inst, &.{ty_op.operand});5406 try reap(f, inst, &.{ty_op.operand});
61835407
6184 // First, set the non-error value.5408 // First, set the non-error value.
6185 if (!payload_ty.hasRuntimeBits(zcu)) {5409 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" });
6186 const a = try Assignment.start(f, w, try f.ctypeFromType(operand_ty, .complete));5410 try w.print(" = {f};", .{try f.fmtIntLiteralDec(no_err)});
6187 try f.writeCValueDeref(w, operand);5411 try f.newline();
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 }
62005412
6201 // Then return the payload pointer (only if it is used)5413 // Then return the payload pointer (only if it is used)
6202 if (f.liveness.isUnused(inst)) return .none;5414 if (f.liveness.isUnused(inst)) return .none;
62035415
6204 const local = try f.allocLocal(inst, inst_ty);5416 const local = try f.allocLocal(inst, inst_ty);
6205 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));5417 try f.writeCValue(w, local, .other);
6206 try f.writeCValue(w, local, .Other);5418 try w.writeAll(" = &");
6207 try a.assign(f, w);
6208 try w.writeByte('&');
6209 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });5419 try f.writeCValueDerefMember(w, operand, .{ .identifier = "payload" });
6210 try a.end(f, w);5420 try w.writeByte(';');
5421 try f.newline();
6211 return local;5422 return local;
6212}5423}
62135424
...@@ -6227,7 +5438,7 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6227,7 +5438,7 @@ fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {
6227}5438}
62285439
6229fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {5440fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
6230 const pt = f.object.dg.pt;5441 const pt = f.dg.pt;
6231 const zcu = pt.zcu;5442 const zcu = pt.zcu;
6232 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5443 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 {...@@ -6235,120 +5446,88 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
6235 const payload_ty = inst_ty.errorUnionPayload(zcu);5446 const payload_ty = inst_ty.errorUnionPayload(zcu);
6236 const payload = try f.resolveInst(ty_op.operand);5447 const payload = try f.resolveInst(ty_op.operand);
6237 assert(payload_ty.hasRuntimeBits(zcu));5448 assert(payload_ty.hasRuntimeBits(zcu));
6238 const err_ty = inst_ty.errorUnionSet(zcu);
6239 try reap(f, inst, &.{ty_op.operand});5449 try reap(f, inst, &.{ty_op.operand});
62405450
6241 const w = &f.object.code.writer;5451 const w = &f.code.writer;
6242 const local = try f.allocLocal(inst, inst_ty);5452 const local = try f.allocLocal(inst, inst_ty);
6243 {5453
6244 const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete));5454 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6245 try f.writeCValueMember(w, local, .{ .identifier = "payload" });5455 try w.writeAll(" = ");
6246 try a.assign(f, w);5456 try f.writeCValue(w, payload, .other);
6247 try f.writeCValue(w, payload, .Other);5457 try w.writeByte(';');
6248 try a.end(f, w);5458 try f.newline();
6249 }5459
6250 {5460 try f.writeCValueMember(w, local, .{ .identifier = "error" });
6251 const a = try Assignment.start(f, w, try f.ctypeFromType(err_ty, .complete));5461 try w.writeAll(" = ");
6252 try f.writeCValueMember(w, local, .{ .identifier = "error" });5462 try f.dg.renderValue(w, try pt.intValue(try pt.errorIntType(), 0), .other);
6253 try a.assign(f, w);5463 try w.writeByte(';');
6254 try f.object.dg.renderValue(w, try pt.intValue(try pt.errorIntType(), 0), .Other);5464 try f.newline();
6255 try a.end(f, w);5465
6256 }
6257 return local;5466 return local;
6258}5467}
62595468
6260fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue {5469fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue {
6261 const pt = f.object.dg.pt;5470 const pt = f.dg.pt;
6262 const zcu = pt.zcu;
6263 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;5471 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;
6266 const operand = try f.resolveInst(un_op);5474 const operand = try f.resolveInst(un_op);
6267 try reap(f, inst, &.{un_op});5475 try reap(f, inst, &.{un_op});
6268 const operand_ty = f.typeOf(un_op);
6269 const local = try f.allocLocal(inst, .bool);5476 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);5478 try f.writeCValue(w, local, .other);
6275 try f.writeCValue(w, local, .Other);5479 try w.writeAll(" = ");
6276 try a.assign(f, w);
6277 const err_int_ty = try pt.errorIntType();5480 const err_int_ty = try pt.errorIntType();
6278 if (!error_ty.errorSetIsEmpty(zcu))5481 if (is_ptr)
6279 if (payload_ty.hasRuntimeBits(zcu))5482 try f.writeCValueDerefMember(w, operand, .{ .identifier = "error" })
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)
6286 else5483 else
6287 try f.object.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .Other);5484 try f.writeCValueMember(w, operand, .{ .identifier = "error" });
6288 try w.writeByte(' ');5485 try w.print(" {s} ", .{operator});
6289 try w.writeAll(operator);5486 try f.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .other);
6290 try w.writeByte(' ');5487 try w.writeByte(';');
6291 try f.object.dg.renderValue(w, try pt.intValue(err_int_ty, 0), .Other);5488 try f.newline();
6292 try a.end(f, w);
6293 return local;5489 return local;
6294}5490}
62955491
6296fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {5492fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
6297 const pt = f.object.dg.pt;5493 const pt = f.dg.pt;
6298 const zcu = pt.zcu;5494 const zcu = pt.zcu;
6299 const ctype_pool = &f.object.dg.ctype_pool;
6300 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5495 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
63015496
6302 const operand = try f.resolveInst(ty_op.operand);5497 const operand = try f.resolveInst(ty_op.operand);
6303 try reap(f, inst, &.{ty_op.operand});5498 try reap(f, inst, &.{ty_op.operand});
6304 const inst_ty = f.typeOfIndex(inst);5499 const inst_ty = f.typeOfIndex(inst);
6305 const ptr_ty = inst_ty.slicePtrFieldType(zcu);5500 const w = &f.code.writer;
6306 const w = &f.object.code.writer;
6307 const local = try f.allocLocal(inst, inst_ty);5501 const local = try f.allocLocal(inst, inst_ty);
6308 const operand_ty = f.typeOf(ty_op.operand);5502 const operand_ty = f.typeOf(ty_op.operand);
6309 const array_ty = operand_ty.childType(zcu);5503 const array_ty = operand_ty.childType(zcu);
63105504
6311 {5505 // We have a `*[n]T`, which was turned into to a pointer to `struct { T array[n]; }`.
6312 const a = try Assignment.start(f, w, try f.ctypeFromType(ptr_ty, .complete));5506 // Ideally we would want to use 'operand->array' to convert to a `T *` (we get a `T []`
6313 try f.writeCValueMember(w, local, .{ .identifier = "ptr" });5507 // which decays to a pointer), but if the element type is zero-bit or the array length is
6314 try a.assign(f, w);5508 // zero, there will not be an `array` member (the array type lowers to `void`). We cannot
6315 if (operand == .undef) {5509 // check the type layout here because it may not be resolved, so in this instance, we must
6316 try f.writeCValue(w, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Other);5510 // use a pointer cast.
6317 } else {5511 try f.writeCValueMember(w, local, .{ .identifier = "ptr" });
6318 const ptr_ctype = try f.ctypeFromType(ptr_ty, .complete);5512 try w.writeAll(" = (");
6319 const ptr_child_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;5513 try f.dg.renderType(w, inst_ty.slicePtrFieldType(zcu));
6320 const elem_ty = array_ty.childType(zcu);5514 try w.writeByte(')');
6321 const elem_ctype = try f.ctypeFromType(elem_ty, .complete);5515 try f.writeCValue(w, operand, .other);
6322 if (!ptr_child_ctype.eql(elem_ctype)) {5516 try w.writeByte(';');
6323 try w.writeByte('(');5517 try f.newline();
6324 try f.renderCType(w, ptr_ctype);5518
6325 try w.writeByte(')');5519 try f.writeCValueMember(w, local, .{ .identifier = "len" });
6326 }5520 try w.print(" = {f}", .{
6327 const operand_ctype = try f.ctypeFromType(operand_ty, .complete);5521 try f.fmtIntLiteralDec(try pt.intValue(.usize, array_ty.arrayLen(zcu))),
6328 const operand_child_ctype = operand_ctype.info(ctype_pool).pointer.elem_ctype;5522 });
6329 if (operand_child_ctype.info(ctype_pool) == .array) {5523 try w.writeByte(';');
6330 try w.writeByte('&');5524 try f.newline();
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 }
63465525
6347 return local;5526 return local;
6348}5527}
63495528
6350fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {5529fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
6351 const pt = f.object.dg.pt;5530 const pt = f.dg.pt;
6352 const zcu = pt.zcu;5531 const zcu = pt.zcu;
6353 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;5532 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 {...@@ -6358,7 +5537,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
6358 try reap(f, inst, &.{ty_op.operand});5537 try reap(f, inst, &.{ty_op.operand});
6359 const operand_ty = f.typeOf(ty_op.operand);5538 const operand_ty = f.typeOf(ty_op.operand);
6360 const scalar_ty = operand_ty.scalarType(zcu);5539 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;
6362 const operation = if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isRuntimeFloat())5541 const operation = if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isRuntimeFloat())
6363 if (inst_scalar_ty.floatBits(target) < scalar_ty.floatBits(target)) "trunc" else "extend"5542 if (inst_scalar_ty.floatBits(target) < scalar_ty.floatBits(target)) "trunc" else "extend"
6364 else if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat())5543 else if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat())
...@@ -6368,16 +5547,15 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6368,16 +5547,15 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
6368 else5547 else
6369 unreachable;5548 unreachable;
63705549
6371 const w = &f.object.code.writer;5550 const w = &f.code.writer;
6372 const local = try f.allocLocal(inst, inst_ty);5551 const local = try f.allocLocal(inst, inst_ty);
6373 const v = try Vectorize.start(f, inst, w, operand_ty);5552 const v = try Vectorize.start(f, inst, w, operand_ty);
6374 const a = try Assignment.start(f, w, try f.ctypeFromType(scalar_ty, .complete));5553 try f.writeCValue(w, local, .other);
6375 try f.writeCValue(w, local, .Other);
6376 try v.elem(f, w);5554 try v.elem(f, w);
6377 try a.assign(f, w);5555 try w.writeAll(" = ");
6378 if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) {5556 if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) {
6379 try w.writeAll("zig_wrap_");5557 try w.writeAll("zig_wrap_");
6380 try f.object.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty);5558 try f.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty);
6381 try w.writeByte('(');5559 try w.writeByte('(');
6382 }5560 }
6383 try w.writeAll("zig_");5561 try w.writeAll("zig_");
...@@ -6385,14 +5563,15 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6385,14 +5563,15 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
6385 try w.writeAll(compilerRtAbbrev(scalar_ty, zcu, target));5563 try w.writeAll(compilerRtAbbrev(scalar_ty, zcu, target));
6386 try w.writeAll(compilerRtAbbrev(inst_scalar_ty, zcu, target));5564 try w.writeAll(compilerRtAbbrev(inst_scalar_ty, zcu, target));
6387 try w.writeByte('(');5565 try w.writeByte('(');
6388 try f.writeCValue(w, operand, .FunctionArgument);5566 try f.writeCValue(w, operand, .other);
6389 try v.elem(f, w);5567 try v.elem(f, w);
6390 try w.writeByte(')');5568 try w.writeByte(')');
6391 if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) {5569 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);
6393 try w.writeByte(')');5571 try w.writeByte(')');
6394 }5572 }
6395 try a.end(f, w);5573 try w.writeByte(';');
5574 try f.newline();
6396 try v.end(f, inst, w);5575 try v.end(f, inst, w);
63975576
6398 return local;5577 return local;
...@@ -6405,7 +5584,7 @@ fn airUnBuiltinCall(...@@ -6405,7 +5584,7 @@ fn airUnBuiltinCall(
6405 operation: []const u8,5584 operation: []const u8,
6406 info: BuiltinInfo,5585 info: BuiltinInfo,
6407) !CValue {5586) !CValue {
6408 const pt = f.object.dg.pt;5587 const pt = f.dg.pt;
6409 const zcu = pt.zcu;5588 const zcu = pt.zcu;
64105589
6411 const operand = try f.resolveInst(operand_ref);5590 const operand = try f.resolveInst(operand_ref);
...@@ -6415,30 +5594,32 @@ fn airUnBuiltinCall(...@@ -6415,30 +5594,32 @@ fn airUnBuiltinCall(
6415 const operand_ty = f.typeOf(operand_ref);5594 const operand_ty = f.typeOf(operand_ref);
6416 const scalar_ty = operand_ty.scalarType(zcu);5595 const scalar_ty = operand_ty.scalarType(zcu);
64175596
6418 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);5597 const ref_ret = lowersToBigInt(inst_scalar_ty, zcu);
6419 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;5598 const ref_arg = lowersToBigInt(scalar_ty, zcu);
64205599
6421 const w = &f.object.code.writer;5600 const w = &f.code.writer;
6422 const local = try f.allocLocal(inst, inst_ty);5601 const local = try f.allocLocal(inst, inst_ty);
6423 const v = try Vectorize.start(f, inst, w, operand_ty);5602 const v = try Vectorize.start(f, inst, w, operand_ty);
6424 if (!ref_ret) {5603 if (!ref_ret) {
6425 try f.writeCValue(w, local, .Other);5604 try f.writeCValue(w, local, .other);
6426 try v.elem(f, w);5605 try v.elem(f, w);
6427 try w.writeAll(" = ");5606 try w.writeAll(" = ");
6428 }5607 }
6429 try w.print("zig_{s}_", .{operation});5608 try w.print("zig_{s}_", .{operation});
6430 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);5609 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
6431 try w.writeByte('(');5610 try w.writeByte('(');
6432 if (ref_ret) {5611 if (ref_ret) {
6433 try f.writeCValue(w, local, .FunctionArgument);5612 try w.writeByte('&');
5613 try f.writeCValue(w, local, .other);
6434 try v.elem(f, w);5614 try v.elem(f, w);
6435 try w.writeAll(", ");5615 try w.writeAll(", ");
6436 }5616 }
6437 try f.writeCValue(w, operand, .FunctionArgument);5617 if (ref_arg) try w.writeByte('&');
5618 try f.writeCValue(w, operand, .other);
6438 try v.elem(f, w);5619 try v.elem(f, w);
6439 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);5620 try f.dg.renderBuiltinInfo(w, scalar_ty, info);
6440 try w.writeAll(");");5621 try w.writeAll(");");
6441 try f.object.newline();5622 try f.newline();
6442 try v.end(f, inst, w);5623 try v.end(f, inst, w);
64435624
6444 return local;5625 return local;
...@@ -6450,13 +5631,12 @@ fn airBinBuiltinCall(...@@ -6450,13 +5631,12 @@ fn airBinBuiltinCall(
6450 operation: []const u8,5631 operation: []const u8,
6451 info: BuiltinInfo,5632 info: BuiltinInfo,
6452) !CValue {5633) !CValue {
6453 const pt = f.object.dg.pt;5634 const pt = f.dg.pt;
6454 const zcu = pt.zcu;5635 const zcu = pt.zcu;
6455 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;5636 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
64565637
6457 const operand_ty = f.typeOf(bin_op.lhs);5638 const operand_ty = f.typeOf(bin_op.lhs);
6458 const operand_ctype = try f.ctypeFromType(operand_ty, .complete);5639 const is_big = lowersToBigInt(operand_ty, zcu);
6459 const is_big = operand_ctype.info(&f.object.dg.ctype_pool) == .array;
64605640
6461 const lhs = try f.resolveInst(bin_op.lhs);5641 const lhs = try f.resolveInst(bin_op.lhs);
6462 const rhs = try f.resolveInst(bin_op.rhs);5642 const rhs = try f.resolveInst(bin_op.rhs);
...@@ -6466,32 +5646,35 @@ fn airBinBuiltinCall(...@@ -6466,32 +5646,35 @@ fn airBinBuiltinCall(
6466 const inst_scalar_ty = inst_ty.scalarType(zcu);5646 const inst_scalar_ty = inst_ty.scalarType(zcu);
6467 const scalar_ty = operand_ty.scalarType(zcu);5647 const scalar_ty = operand_ty.scalarType(zcu);
64685648
6469 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);5649 const ref_ret = lowersToBigInt(inst_scalar_ty, zcu);
6470 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;5650 const ref_arg = lowersToBigInt(scalar_ty, zcu);
64715651
6472 const w = &f.object.code.writer;5652 const w = &f.code.writer;
6473 const local = try f.allocLocal(inst, inst_ty);5653 const local = try f.allocLocal(inst, inst_ty);
6474 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });5654 if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6475 const v = try Vectorize.start(f, inst, w, operand_ty);5655 const v = try Vectorize.start(f, inst, w, operand_ty);
6476 if (!ref_ret) {5656 if (!ref_ret) {
6477 try f.writeCValue(w, local, .Other);5657 try f.writeCValue(w, local, .other);
6478 try v.elem(f, w);5658 try v.elem(f, w);
6479 try w.writeAll(" = ");5659 try w.writeAll(" = ");
6480 }5660 }
6481 try w.print("zig_{s}_", .{operation});5661 try w.print("zig_{s}_", .{operation});
6482 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);5662 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
6483 try w.writeByte('(');5663 try w.writeByte('(');
6484 if (ref_ret) {5664 if (ref_ret) {
6485 try f.writeCValue(w, local, .FunctionArgument);5665 try w.writeByte('&');
5666 try f.writeCValue(w, local, .other);
6486 try v.elem(f, w);5667 try v.elem(f, w);
6487 try w.writeAll(", ");5668 try w.writeAll(", ");
6488 }5669 }
6489 try f.writeCValue(w, lhs, .FunctionArgument);5670 if (ref_arg) try w.writeByte('&');
5671 try f.writeCValue(w, lhs, .other);
6490 try v.elem(f, w);5672 try v.elem(f, w);
6491 try w.writeAll(", ");5673 try w.writeAll(", ");
6492 try f.writeCValue(w, rhs, .FunctionArgument);5674 if (ref_arg) try w.writeByte('&');
5675 try f.writeCValue(w, rhs, .other);
6493 if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, w);5676 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);
6495 try w.writeAll(");\n");5678 try w.writeAll(");\n");
6496 try v.end(f, inst, w);5679 try v.end(f, inst, w);
64975680
...@@ -6506,7 +5689,7 @@ fn airCmpBuiltinCall(...@@ -6506,7 +5689,7 @@ fn airCmpBuiltinCall(
6506 operation: enum { cmp, operator },5689 operation: enum { cmp, operator },
6507 info: BuiltinInfo,5690 info: BuiltinInfo,
6508) !CValue {5691) !CValue {
6509 const pt = f.object.dg.pt;5692 const pt = f.dg.pt;
6510 const zcu = pt.zcu;5693 const zcu = pt.zcu;
6511 const lhs = try f.resolveInst(data.lhs);5694 const lhs = try f.resolveInst(data.lhs);
6512 const rhs = try f.resolveInst(data.rhs);5695 const rhs = try f.resolveInst(data.rhs);
...@@ -6517,14 +5700,14 @@ fn airCmpBuiltinCall(...@@ -6517,14 +5700,14 @@ fn airCmpBuiltinCall(
6517 const operand_ty = f.typeOf(data.lhs);5700 const operand_ty = f.typeOf(data.lhs);
6518 const scalar_ty = operand_ty.scalarType(zcu);5701 const scalar_ty = operand_ty.scalarType(zcu);
65195702
6520 const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);5703 const ref_ret = lowersToBigInt(inst_scalar_ty, zcu);
6521 const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;5704 const ref_arg = lowersToBigInt(scalar_ty, zcu);
65225705
6523 const w = &f.object.code.writer;5706 const w = &f.code.writer;
6524 const local = try f.allocLocal(inst, inst_ty);5707 const local = try f.allocLocal(inst, inst_ty);
6525 const v = try Vectorize.start(f, inst, w, operand_ty);5708 const v = try Vectorize.start(f, inst, w, operand_ty);
6526 if (!ref_ret) {5709 if (!ref_ret) {
6527 try f.writeCValue(w, local, .Other);5710 try f.writeCValue(w, local, .other);
6528 try v.elem(f, w);5711 try v.elem(f, w);
6529 try w.writeAll(" = ");5712 try w.writeAll(" = ");
6530 }5713 }
...@@ -6532,33 +5715,36 @@ fn airCmpBuiltinCall(...@@ -6532,33 +5715,36 @@ fn airCmpBuiltinCall(
6532 else => @tagName(operation),5715 else => @tagName(operation),
6533 .operator => compareOperatorAbbrev(operator),5716 .operator => compareOperatorAbbrev(operator),
6534 }});5717 }});
6535 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);5718 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
6536 try w.writeByte('(');5719 try w.writeByte('(');
6537 if (ref_ret) {5720 if (ref_ret) {
6538 try f.writeCValue(w, local, .FunctionArgument);5721 try w.writeByte('&');
5722 try f.writeCValue(w, local, .other);
6539 try v.elem(f, w);5723 try v.elem(f, w);
6540 try w.writeAll(", ");5724 try w.writeAll(", ");
6541 }5725 }
6542 try f.writeCValue(w, lhs, .FunctionArgument);5726 if (ref_arg) try w.writeByte('&');
5727 try f.writeCValue(w, lhs, .other);
6543 try v.elem(f, w);5728 try v.elem(f, w);
6544 try w.writeAll(", ");5729 try w.writeAll(", ");
6545 try f.writeCValue(w, rhs, .FunctionArgument);5730 if (ref_arg) try w.writeByte('&');
5731 try f.writeCValue(w, rhs, .other);
6546 try v.elem(f, w);5732 try v.elem(f, w);
6547 try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);5733 try f.dg.renderBuiltinInfo(w, scalar_ty, info);
6548 try w.writeByte(')');5734 try w.writeByte(')');
6549 if (!ref_ret) try w.print("{s}{f}", .{5735 if (!ref_ret) try w.print("{s}{f}", .{
6550 compareOperatorC(operator),5736 compareOperatorC(operator),
6551 try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)),5737 try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)),
6552 });5738 });
6553 try w.writeByte(';');5739 try w.writeByte(';');
6554 try f.object.newline();5740 try f.newline();
6555 try v.end(f, inst, w);5741 try v.end(f, inst, w);
65565742
6557 return local;5743 return local;
6558}5744}
65595745
6560fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {5746fn 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;
6562 const zcu = pt.zcu;5748 const zcu = pt.zcu;
6563 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;5749 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
6564 const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;5750 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...@@ -6568,9 +5754,8 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
6568 const new_value = try f.resolveInst(extra.new_value);5754 const new_value = try f.resolveInst(extra.new_value);
6569 const ptr_ty = f.typeOf(extra.ptr);5755 const ptr_ty = f.typeOf(extra.ptr);
6570 const ty = ptr_ty.childType(zcu);5756 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;
6574 const new_value_mat = try Materialize.start(f, inst, ty, new_value);5759 const new_value_mat = try Materialize.start(f, inst, ty, new_value);
6575 try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value });5760 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...@@ -6581,13 +5766,11 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
65815766
6582 const local = try f.allocLocal(inst, inst_ty);5767 const local = try f.allocLocal(inst, inst_ty);
6583 if (inst_ty.isPtrLikeOptional(zcu)) {5768 if (inst_ty.isPtrLikeOptional(zcu)) {
6584 {5769 try f.writeCValue(w, local, .other);
6585 const a = try Assignment.start(f, w, ctype);5770 try w.writeAll(" = ");
6586 try f.writeCValue(w, local, .Other);5771 try f.writeCValue(w, expected_value, .other);
6587 try a.assign(f, w);5772 try w.writeByte(';');
6588 try f.writeCValue(w, expected_value, .Other);5773 try f.newline();
6589 try a.end(f, w);
6590 }
65915774
6592 try w.writeAll("if (");5775 try w.writeAll("if (");
6593 try w.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor});5776 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...@@ -6595,9 +5778,9 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
6595 try w.writeByte(')');5778 try w.writeByte(')');
6596 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");5779 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
6597 try w.writeAll(" *)");5780 try w.writeAll(" *)");
6598 try f.writeCValue(w, ptr, .Other);5781 try f.writeCValue(w, ptr, .other);
6599 try w.writeAll(", ");5782 try w.writeAll(", ");
6600 try f.writeCValue(w, local, .FunctionArgument);5783 try f.writeCValue(w, local, .other);
6601 try w.writeAll(", ");5784 try w.writeAll(", ");
6602 try new_value_mat.mat(f, w);5785 try new_value_mat.mat(f, w);
6603 try w.writeAll(", ");5786 try w.writeAll(", ");
...@@ -6605,56 +5788,49 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue...@@ -6605,56 +5788,49 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
6605 try w.writeAll(", ");5788 try w.writeAll(", ");
6606 try writeMemoryOrder(w, extra.failureOrder());5789 try writeMemoryOrder(w, extra.failureOrder());
6607 try w.writeAll(", ");5790 try w.writeAll(", ");
6608 try f.object.dg.renderTypeForBuiltinFnName(w, ty);5791 try f.dg.renderTypeForBuiltinFnName(w, ty);
6609 try w.writeAll(", ");5792 try w.writeAll(", ");
6610 try f.renderType(w, repr_ty);5793 try f.renderType(w, repr_ty);
6611 try w.writeByte(')');5794 try w.writeByte(')');
6612 try w.writeAll(") {");5795 try w.writeAll(") {");
6613 f.object.indent();5796 f.indent();
6614 try f.object.newline();5797 try f.newline();
6615 {5798
6616 const a = try Assignment.start(f, w, ctype);5799 try f.writeCValue(w, local, .other);
6617 try f.writeCValue(w, local, .Other);5800 try w.writeAll(" = NULL;");
6618 try a.assign(f, w);5801 try f.newline();
6619 try w.writeAll("NULL");5802
6620 try a.end(f, w);5803 try f.outdent();
6621 }
6622 try f.object.outdent();
6623 try w.writeByte('}');5804 try w.writeByte('}');
6624 try f.object.newline();5805 try f.newline();
6625 } else {5806 } else {
6626 {5807 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6627 const a = try Assignment.start(f, w, ctype);5808 try w.writeAll(" = ");
6628 try f.writeCValueMember(w, local, .{ .identifier = "payload" });5809 try f.writeCValue(w, expected_value, .other);
6629 try a.assign(f, w);5810 try w.writeByte(';');
6630 try f.writeCValue(w, expected_value, .Other);5811 try f.newline();
6631 try a.end(f, w);5812
6632 }5813 try f.writeCValueMember(w, local, .{ .identifier = "is_null" });
6633 {5814 try w.print(" = zig_cmpxchg_{s}((zig_atomic(", .{flavor});
6634 const a = try Assignment.start(f, w, .bool);5815 try f.renderType(w, ty);
6635 try f.writeCValueMember(w, local, .{ .identifier = "is_null" });5816 try w.writeByte(')');
6636 try a.assign(f, w);5817 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
6637 try w.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor});5818 try w.writeAll(" *)");
6638 try f.renderType(w, ty);5819 try f.writeCValue(w, ptr, .other);
6639 try w.writeByte(')');5820 try w.writeAll(", ");
6640 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");5821 try f.writeCValueMember(w, local, .{ .identifier = "payload" });
6641 try w.writeAll(" *)");5822 try w.writeAll(", ");
6642 try f.writeCValue(w, ptr, .Other);5823 try new_value_mat.mat(f, w);
6643 try w.writeAll(", ");5824 try w.writeAll(", ");
6644 try f.writeCValueMember(w, local, .{ .identifier = "payload" });5825 try writeMemoryOrder(w, extra.successOrder());
6645 try w.writeAll(", ");5826 try w.writeAll(", ");
6646 try new_value_mat.mat(f, w);5827 try writeMemoryOrder(w, extra.failureOrder());
6647 try w.writeAll(", ");5828 try w.writeAll(", ");
6648 try writeMemoryOrder(w, extra.successOrder());5829 try f.dg.renderTypeForBuiltinFnName(w, ty);
6649 try w.writeAll(", ");5830 try w.writeAll(", ");
6650 try writeMemoryOrder(w, extra.failureOrder());5831 try f.renderType(w, repr_ty);
6651 try w.writeAll(", ");5832 try w.writeAll(");");
6652 try f.object.dg.renderTypeForBuiltinFnName(w, ty);5833 try f.newline();
6653 try w.writeAll(", ");
6654 try f.renderType(w, repr_ty);
6655 try w.writeByte(')');
6656 try a.end(f, w);
6657 }
6658 }5834 }
6659 try new_value_mat.end(f, inst);5835 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...@@ -6667,7 +5843,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue
6667}5843}
66685844
6669fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {5845fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
6670 const pt = f.object.dg.pt;5846 const pt = f.dg.pt;
6671 const zcu = pt.zcu;5847 const zcu = pt.zcu;
6672 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;5848 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6673 const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data;5849 const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data;
...@@ -6677,7 +5853,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6677,7 +5853,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
6677 const ptr = try f.resolveInst(pl_op.operand);5853 const ptr = try f.resolveInst(pl_op.operand);
6678 const operand = try f.resolveInst(extra.operand);5854 const operand = try f.resolveInst(extra.operand);
66795855
6680 const w = &f.object.code.writer;5856 const w = &f.code.writer;
6681 const operand_mat = try Materialize.start(f, inst, ty, operand);5857 const operand_mat = try Materialize.start(f, inst, ty, operand);
6682 try reap(f, inst, &.{ pl_op.operand, extra.operand });5858 try reap(f, inst, &.{ pl_op.operand, extra.operand });
66835859
...@@ -6690,7 +5866,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6690,7 +5866,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
6690 try w.print("zig_atomicrmw_{s}", .{toAtomicRmwSuffix(extra.op())});5866 try w.print("zig_atomicrmw_{s}", .{toAtomicRmwSuffix(extra.op())});
6691 if (is_float) try w.writeAll("_float") else if (is_128) try w.writeAll("_int128");5867 if (is_float) try w.writeAll("_float") else if (is_128) try w.writeAll("_int128");
6692 try w.writeByte('(');5868 try w.writeByte('(');
6693 try f.writeCValue(w, local, .Other);5869 try f.writeCValue(w, local, .other);
6694 try w.writeAll(", (");5870 try w.writeAll(", (");
6695 const use_atomic = switch (extra.op()) {5871 const use_atomic = switch (extra.op()) {
6696 else => true,5872 else => true,
...@@ -6702,17 +5878,17 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6702,17 +5878,17 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
6702 if (use_atomic) try w.writeByte(')');5878 if (use_atomic) try w.writeByte(')');
6703 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");5879 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
6704 try w.writeAll(" *)");5880 try w.writeAll(" *)");
6705 try f.writeCValue(w, ptr, .Other);5881 try f.writeCValue(w, ptr, .other);
6706 try w.writeAll(", ");5882 try w.writeAll(", ");
6707 try operand_mat.mat(f, w);5883 try operand_mat.mat(f, w);
6708 try w.writeAll(", ");5884 try w.writeAll(", ");
6709 try writeMemoryOrder(w, extra.ordering());5885 try writeMemoryOrder(w, extra.ordering());
6710 try w.writeAll(", ");5886 try w.writeAll(", ");
6711 try f.object.dg.renderTypeForBuiltinFnName(w, ty);5887 try f.dg.renderTypeForBuiltinFnName(w, ty);
6712 try w.writeAll(", ");5888 try w.writeAll(", ");
6713 try f.renderType(w, repr_ty);5889 try f.renderType(w, repr_ty);
6714 try w.writeAll(");");5890 try w.writeAll(");");
6715 try f.object.newline();5891 try f.newline();
6716 try operand_mat.end(f, inst);5892 try operand_mat.end(f, inst);
67175893
6718 if (f.liveness.isUnused(inst)) {5894 if (f.liveness.isUnused(inst)) {
...@@ -6724,7 +5900,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6724,7 +5900,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
6724}5900}
67255901
6726fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {5902fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
6727 const pt = f.object.dg.pt;5903 const pt = f.dg.pt;
6728 const zcu = pt.zcu;5904 const zcu = pt.zcu;
6729 const atomic_load = f.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;5905 const atomic_load = f.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
6730 const ptr = try f.resolveInst(atomic_load.ptr);5906 const ptr = try f.resolveInst(atomic_load.ptr);
...@@ -6738,31 +5914,31 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6738,31 +5914,31 @@ fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
6738 ty;5914 ty;
67395915
6740 const inst_ty = f.typeOfIndex(inst);5916 const inst_ty = f.typeOfIndex(inst);
6741 const w = &f.object.code.writer;5917 const w = &f.code.writer;
6742 const local = try f.allocLocal(inst, inst_ty);5918 const local = try f.allocLocal(inst, inst_ty);
67435919
6744 try w.writeAll("zig_atomic_load(");5920 try w.writeAll("zig_atomic_load(");
6745 try f.writeCValue(w, local, .Other);5921 try f.writeCValue(w, local, .other);
6746 try w.writeAll(", (zig_atomic(");5922 try w.writeAll(", (zig_atomic(");
6747 try f.renderType(w, ty);5923 try f.renderType(w, ty);
6748 try w.writeByte(')');5924 try w.writeByte(')');
6749 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");5925 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
6750 try w.writeAll(" *)");5926 try w.writeAll(" *)");
6751 try f.writeCValue(w, ptr, .Other);5927 try f.writeCValue(w, ptr, .other);
6752 try w.writeAll(", ");5928 try w.writeAll(", ");
6753 try writeMemoryOrder(w, atomic_load.order);5929 try writeMemoryOrder(w, atomic_load.order);
6754 try w.writeAll(", ");5930 try w.writeAll(", ");
6755 try f.object.dg.renderTypeForBuiltinFnName(w, ty);5931 try f.dg.renderTypeForBuiltinFnName(w, ty);
6756 try w.writeAll(", ");5932 try w.writeAll(", ");
6757 try f.renderType(w, repr_ty);5933 try f.renderType(w, repr_ty);
6758 try w.writeAll(");");5934 try w.writeAll(");");
6759 try f.object.newline();5935 try f.newline();
67605936
6761 return local;5937 return local;
6762}5938}
67635939
6764fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CValue {5940fn 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;
6766 const zcu = pt.zcu;5942 const zcu = pt.zcu;
6767 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;5943 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6768 const ptr_ty = f.typeOf(bin_op.lhs);5944 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...@@ -6770,7 +5946,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
6770 const ptr = try f.resolveInst(bin_op.lhs);5946 const ptr = try f.resolveInst(bin_op.lhs);
6771 const element = try f.resolveInst(bin_op.rhs);5947 const element = try f.resolveInst(bin_op.rhs);
67725948
6773 const w = &f.object.code.writer;5949 const w = &f.code.writer;
6774 const element_mat = try Materialize.start(f, inst, ty, element);5950 const element_mat = try Materialize.start(f, inst, ty, element);
6775 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });5951 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...@@ -6784,32 +5960,22 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
6784 try w.writeByte(')');5960 try w.writeByte(')');
6785 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");5961 if (ptr_ty.isVolatilePtr(zcu)) try w.writeAll(" volatile");
6786 try w.writeAll(" *)");5962 try w.writeAll(" *)");
6787 try f.writeCValue(w, ptr, .Other);5963 try f.writeCValue(w, ptr, .other);
6788 try w.writeAll(", ");5964 try w.writeAll(", ");
6789 try element_mat.mat(f, w);5965 try element_mat.mat(f, w);
6790 try w.print(", {s}, ", .{order});5966 try w.print(", {s}, ", .{order});
6791 try f.object.dg.renderTypeForBuiltinFnName(w, ty);5967 try f.dg.renderTypeForBuiltinFnName(w, ty);
6792 try w.writeAll(", ");5968 try w.writeAll(", ");
6793 try f.renderType(w, repr_ty);5969 try f.renderType(w, repr_ty);
6794 try w.writeAll(");");5970 try w.writeAll(");");
6795 try f.object.newline();5971 try f.newline();
6796 try element_mat.end(f, inst);5972 try element_mat.end(f, inst);
67975973
6798 return .none;5974 return .none;
6799}5975}
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
6811fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {5977fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
6812 const pt = f.object.dg.pt;5978 const pt = f.dg.pt;
6813 const zcu = pt.zcu;5979 const zcu = pt.zcu;
6814 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;5980 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6815 const dest_ty = f.typeOf(bin_op.lhs);5981 const dest_ty = f.typeOf(bin_op.lhs);
...@@ -6818,7 +5984,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {...@@ -6818,7 +5984,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
6818 const elem_ty = f.typeOf(bin_op.rhs);5984 const elem_ty = f.typeOf(bin_op.rhs);
6819 const elem_abi_size = elem_ty.abiSize(zcu);5985 const elem_abi_size = elem_ty.abiSize(zcu);
6820 const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndef(zcu) else false;5986 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
6823 if (val_is_undef) {5989 if (val_is_undef) {
6824 if (!safety) {5990 if (!safety) {
...@@ -6832,153 +5998,128 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {...@@ -6832,153 +5998,128 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
6832 try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" });5998 try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" });
6833 try w.writeAll(", 0xaa, ");5999 try w.writeAll(", 0xaa, ");
6834 try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" });6000 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();
6840 },6001 },
6841 .one => {6002 .one => {
6842 const array_ty = dest_ty.childType(zcu);6003 try f.writeCValue(w, dest_slice, .other);
6843 const len = array_ty.arrayLen(zcu) * elem_abi_size;6004 try w.print(", 0xaa, {d}", .{dest_ty.childType(zcu).arrayLen(zcu)});
6844
6845 try f.writeCValue(w, dest_slice, .FunctionArgument);
6846 try w.print(", 0xaa, {d});", .{len});
6847 try f.object.newline();
6848 },6005 },
6849 .many, .c => unreachable,6006 .many, .c => unreachable,
6850 }6007 }
6008 if (elem_abi_size > 0) try w.print(" * {d}", .{elem_abi_size});
6009 try w.writeAll(");");
6010 try f.newline();
6851 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });6011 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6852 return .none;6012 return .none;
6853 }6013 }
68546014
6855 if (elem_abi_size > 1 or dest_ty.isVolatilePtr(zcu)) {6015 if (elem_abi_size == 1 and !dest_ty.isVolatilePtr(zcu)) {
6856 // For the assignment in this loop, the array pointer needs to get6016 const bitcasted = try bitcast(f, .u8, value, elem_ty);
6857 // casted to a regular pointer, otherwise an error like this occurs:6017 try w.writeAll("memset(");
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(" != ");
6875 switch (dest_ty.ptrSize(zcu)) {6018 switch (dest_ty.ptrSize(zcu)) {
6876 .slice => {6019 .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(", ");
6877 try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" });6024 try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" });
6878 },6025 },
6879 .one => {6026 .one => {
6880 const array_ty = dest_ty.childType(zcu);6027 try f.writeCValue(w, dest_slice, .other);
6881 try w.print("{d}", .{array_ty.arrayLen(zcu)});6028 try w.writeAll(", ");
6029 try f.writeCValue(w, bitcasted, .other);
6030 try w.print(", {d}", .{dest_ty.childType(zcu).arrayLen(zcu)});
6882 },6031 },
6883 .many, .c => unreachable,6032 .many, .c => unreachable,
6884 }6033 }
6885 try w.writeAll("; ++");6034 try w.writeAll(");");
6886 try f.writeCValue(w, index, .Other);6035 try f.newline();
6887 try w.writeAll(") ");6036 try f.freeCValue(inst, bitcasted);
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
6901 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });6037 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6902 try freeLocal(f, inst, index.new_local, null);
6903
6904 return .none;6038 return .none;
6905 }6039 }
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(" != ");
6910 switch (dest_ty.ptrSize(zcu)) {6052 switch (dest_ty.ptrSize(zcu)) {
6911 .slice => {6053 .slice => try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" }),
6912 try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" });6054 .one => try w.print("{d}", .{dest_ty.childType(zcu).arrayLen(zcu)}),
6913 try w.writeAll(", ");6055 .many, .c => unreachable,
6914 try f.writeCValue(w, bitcasted, .FunctionArgument);6056 }
6915 try w.writeAll(", ");6057 try w.writeAll("; ++");
6916 try f.writeCValueMember(w, dest_slice, .{ .identifier = "len" });6058 try f.writeCValue(w, index, .other);
6917 try w.writeAll(");");6059 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;
69236060
6924 try f.writeCValue(w, dest_slice, .FunctionArgument);6061 switch (dest_ty.ptrSize(zcu)) {
6925 try w.writeAll(", ");6062 .slice => try f.writeCValueMember(w, dest_slice, .{ .identifier = "ptr" }),
6926 try f.writeCValue(w, bitcasted, .FunctionArgument);6063 .one => try f.writeCValueDerefMember(w, dest_slice, .{ .identifier = "array" }),
6927 try w.print(", {d});", .{len});
6928 try f.object.newline();
6929 },
6930 .many, .c => unreachable,6064 .many, .c => unreachable,
6931 }6065 }
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
6933 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });6073 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6074 try freeLocal(f, inst, index.new_local, null);
6075
6934 return .none;6076 return .none;
6935}6077}
69366078
6937fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CValue {6079fn 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;
6939 const zcu = pt.zcu;6081 const zcu = pt.zcu;
6940 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6082 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6941 const dest_ptr = try f.resolveInst(bin_op.lhs);6083 const dest_ptr = try f.resolveInst(bin_op.lhs);
6942 const src_ptr = try f.resolveInst(bin_op.rhs);6084 const src_ptr = try f.resolveInst(bin_op.rhs);
6943 const dest_ty = f.typeOf(bin_op.lhs);6085 const dest_ty = f.typeOf(bin_op.lhs);
6944 const src_ty = f.typeOf(bin_op.rhs);6086 const src_ty = f.typeOf(bin_op.rhs);
6945 const w = &f.object.code.writer;6087 const w = &f.code.writer;
69466088
6947 if (dest_ty.ptrSize(zcu) != .one) {6089 if (dest_ty.ptrSize(zcu) != .one) {
6948 try w.writeAll("if (");6090 try w.writeAll("if (");
6949 try writeArrayLen(f, dest_ptr, dest_ty);6091 try f.writeCValueMember(w, dest_ptr, .{ .identifier = "len" });
6950 try w.writeAll(" != 0) ");6092 try w.writeAll(" != 0) ");
6951 }6093 }
6952 try w.writeAll(function_paren);6094 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 }
6954 try w.writeAll(", ");6100 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 }
6956 try w.writeAll(", ");6106 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 }
6958 try w.writeAll(" * sizeof(");6112 try w.writeAll(" * sizeof(");
6959 try f.renderType(w, dest_ty.indexableElem(zcu));6113 try f.renderType(w, dest_ty.indexableElem(zcu));
6960 try w.writeAll("));");6114 try w.writeAll("));");
6961 try f.object.newline();6115 try f.newline();
69626116
6963 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });6117 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
6964 return .none;6118 return .none;
6965}6119}
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
6980fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {6121fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
6981 const pt = f.object.dg.pt;6122 const pt = f.dg.pt;
6982 const zcu = pt.zcu;6123 const zcu = pt.zcu;
6983 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;6124 const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6984 const union_ptr = try f.resolveInst(bin_op.lhs);6125 const union_ptr = try f.resolveInst(bin_op.lhs);
...@@ -6988,19 +6129,18 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -6988,19 +6129,18 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
6988 const union_ty = f.typeOf(bin_op.lhs).childType(zcu);6129 const union_ty = f.typeOf(bin_op.lhs).childType(zcu);
6989 const layout = union_ty.unionGetLayout(zcu);6130 const layout = union_ty.unionGetLayout(zcu);
6990 if (layout.tag_size == 0) return .none;6131 if (layout.tag_size == 0) return .none;
6991 const tag_ty = union_ty.unionTagTypeRuntime(zcu).?;
69926132
6993 const w = &f.object.code.writer;6133 const w = &f.code.writer;
6994 const a = try Assignment.start(f, w, try f.ctypeFromType(tag_ty, .complete));
6995 try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" });6134 try f.writeCValueDerefMember(w, union_ptr, .{ .identifier = "tag" });
6996 try a.assign(f, w);6135 try w.writeAll(" = ");
6997 try f.writeCValue(w, new_tag, .Other);6136 try f.writeCValue(w, new_tag, .other);
6998 try a.end(f, w);6137 try w.writeByte(';');
6138 try f.newline();
6999 return .none;6139 return .none;
7000}6140}
70016141
7002fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {6142fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
7003 const pt = f.object.dg.pt;6143 const pt = f.dg.pt;
7004 const zcu = pt.zcu;6144 const zcu = pt.zcu;
7005 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;6145 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 {...@@ -7012,17 +6152,20 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
7012 if (layout.tag_size == 0) return .none;6152 if (layout.tag_size == 0) return .none;
70136153
7014 const inst_ty = f.typeOfIndex(inst);6154 const inst_ty = f.typeOfIndex(inst);
7015 const w = &f.object.code.writer;6155 const w = &f.code.writer;
7016 const local = try f.allocLocal(inst, inst_ty);6156 const local = try f.allocLocal(inst, inst_ty);
7017 const a = try Assignment.start(f, w, try f.ctypeFromType(inst_ty, .complete));6157 try f.writeCValue(w, local, .other);
7018 try f.writeCValue(w, local, .Other);6158 try w.writeAll(" = ");
7019 try a.assign(f, w);
7020 try f.writeCValueMember(w, operand, .{ .identifier = "tag" });6159 try f.writeCValueMember(w, operand, .{ .identifier = "tag" });
7021 try a.end(f, w);6160 try w.writeByte(';');
6161 try f.newline();
7022 return local;6162 return local;
7023}6163}
70246164
7025fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {6165fn 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;
7026 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;6169 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
70276170
7028 const inst_ty = f.typeOfIndex(inst);6171 const inst_ty = f.typeOfIndex(inst);
...@@ -7030,15 +6173,17 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7030,15 +6173,17 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
7030 const operand = try f.resolveInst(un_op);6173 const operand = try f.resolveInst(un_op);
7031 try reap(f, inst, &.{un_op});6174 try reap(f, inst, &.{un_op});
70326175
7033 const w = &f.object.code.writer;6176 const w = &f.code.writer;
7034 const local = try f.allocLocal(inst, inst_ty);6177 const local = try f.allocLocal(inst, inst_ty);
7035 try f.writeCValue(w, local, .Other);6178 try f.writeCValue(w, local, .other);
7036 try w.print(" = {s}(", .{6179 try f.need_tag_name_funcs.put(gpa, enum_ty.toIntern(), {});
7037 try f.getLazyFnName(.{ .tag_name = enum_ty.toIntern() }),6180 try w.print(" = zig_tagName_{f}__{d}(", .{
6181 fmtIdentUnsolo(enum_ty.containerTypeName(ip).toSlice(ip)),
6182 @intFromEnum(enum_ty.toIntern()),
7038 });6183 });
7039 try f.writeCValue(w, operand, .Other);6184 try f.writeCValue(w, operand, .other);
7040 try w.writeAll(");");6185 try w.writeAll(");");
7041 try f.object.newline();6186 try f.newline();
70426187
7043 return local;6188 return local;
7044}6189}
...@@ -7046,40 +6191,37 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7046,40 +6191,37 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
7046fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {6191fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
7047 const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;6192 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;
7050 const inst_ty = f.typeOfIndex(inst);6195 const inst_ty = f.typeOfIndex(inst);
7051 const operand = try f.resolveInst(un_op);6196 const operand = try f.resolveInst(un_op);
7052 try reap(f, inst, &.{un_op});6197 try reap(f, inst, &.{un_op});
7053 const local = try f.allocLocal(inst, inst_ty);6198 const local = try f.allocLocal(inst, inst_ty);
7054 try f.writeCValue(w, local, .Other);6199 try f.writeCValue(w, local, .other);
70556200
7056 try w.writeAll(" = zig_errorName[");6201 try w.writeAll(" = zig_errorName[");
7057 try f.writeCValue(w, operand, .Other);6202 try f.writeCValue(w, operand, .other);
7058 try w.writeAll(" - 1];");6203 try w.writeAll(" - 1];");
7059 try f.object.newline();6204 try f.newline();
7060 return local;6205 return local;
7061}6206}
70626207
7063fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {6208fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
7064 const pt = f.object.dg.pt;
7065 const zcu = pt.zcu;
7066 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;6209 const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
70676210
7068 const operand = try f.resolveInst(ty_op.operand);6211 const operand = try f.resolveInst(ty_op.operand);
7069 try reap(f, inst, &.{ty_op.operand});6212 try reap(f, inst, &.{ty_op.operand});
70706213
7071 const inst_ty = f.typeOfIndex(inst);6214 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;
7075 const local = try f.allocLocal(inst, inst_ty);6217 const local = try f.allocLocal(inst, inst_ty);
7076 const v = try Vectorize.start(f, inst, w, inst_ty);6218 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));6219 try f.writeCValue(w, local, .other);
7078 try f.writeCValue(w, local, .Other);
7079 try v.elem(f, w);6220 try v.elem(f, w);
7080 try a.assign(f, w);6221 try w.writeAll(" = ");
7081 try f.writeCValue(w, operand, .Other);6222 try f.writeCValue(w, operand, .other);
7082 try a.end(f, w);6223 try w.writeByte(';');
6224 try f.newline();
7083 try v.end(f, inst, w);6225 try v.end(f, inst, w);
70846226
7085 return local;6227 return local;
...@@ -7096,29 +6238,29 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7096,29 +6238,29 @@ fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
70966238
7097 const inst_ty = f.typeOfIndex(inst);6239 const inst_ty = f.typeOfIndex(inst);
70986240
7099 const w = &f.object.code.writer;6241 const w = &f.code.writer;
7100 const local = try f.allocLocal(inst, inst_ty);6242 const local = try f.allocLocal(inst, inst_ty);
7101 const v = try Vectorize.start(f, inst, w, inst_ty);6243 const v = try Vectorize.start(f, inst, w, inst_ty);
7102 try f.writeCValue(w, local, .Other);6244 try f.writeCValue(w, local, .other);
7103 try v.elem(f, w);6245 try v.elem(f, w);
7104 try w.writeAll(" = ");6246 try w.writeAll(" = ");
7105 try f.writeCValue(w, pred, .Other);6247 try f.writeCValue(w, pred, .other);
7106 try v.elem(f, w);6248 try v.elem(f, w);
7107 try w.writeAll(" ? ");6249 try w.writeAll(" ? ");
7108 try f.writeCValue(w, lhs, .Other);6250 try f.writeCValue(w, lhs, .other);
7109 try v.elem(f, w);6251 try v.elem(f, w);
7110 try w.writeAll(" : ");6252 try w.writeAll(" : ");
7111 try f.writeCValue(w, rhs, .Other);6253 try f.writeCValue(w, rhs, .other);
7112 try v.elem(f, w);6254 try v.elem(f, w);
7113 try w.writeByte(';');6255 try w.writeByte(';');
7114 try f.object.newline();6256 try f.newline();
7115 try v.end(f, inst, w);6257 try v.end(f, inst, w);
71166258
7117 return local;6259 return local;
7118}6260}
71196261
7120fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {6262fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
7121 const pt = f.object.dg.pt;6263 const pt = f.dg.pt;
7122 const zcu = pt.zcu;6264 const zcu = pt.zcu;
71236265
7124 const unwrapped = f.air.unwrapShuffleOne(zcu, inst);6266 const unwrapped = f.air.unwrapShuffleOne(zcu, inst);
...@@ -7126,22 +6268,22 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7126,22 +6268,22 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
7126 const operand = try f.resolveInst(unwrapped.operand);6268 const operand = try f.resolveInst(unwrapped.operand);
7127 const inst_ty = unwrapped.result_ty;6269 const inst_ty = unwrapped.result_ty;
71286270
7129 const w = &f.object.code.writer;6271 const w = &f.code.writer;
7130 const local = try f.allocLocal(inst, inst_ty);6272 const local = try f.allocLocal(inst, inst_ty);
7131 try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand6273 try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand
7132 for (mask, 0..) |mask_elem, out_idx| {6274 for (mask, 0..) |mask_elem, out_idx| {
7133 try f.writeCValue(w, local, .Other);6275 try f.writeCValueMember(w, local, .{ .identifier = "array" });
7134 try w.writeByte('[');6276 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);
7136 try w.writeAll("] = ");6278 try w.writeAll("] = ");
7137 switch (mask_elem.unwrap()) {6279 switch (mask_elem.unwrap()) {
7138 .elem => |src_idx| {6280 .elem => |src_idx| {
7139 try f.writeCValue(w, operand, .Other);6281 try f.writeCValueMember(w, operand, .{ .identifier = "array" });
7140 try w.writeByte('[');6282 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);
7142 try w.writeByte(']');6284 try w.writeByte(']');
7143 },6285 },
7144 .value => |val| try f.object.dg.renderValue(w, .fromInterned(val), .Other),6286 .value => |val| try f.dg.renderValue(w, .fromInterned(val), .other),
7145 }6287 }
7146 try w.writeAll(";\n");6288 try w.writeAll(";\n");
7147 }6289 }
...@@ -7150,7 +6292,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7150,7 +6292,7 @@ fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
7150}6292}
71516293
7152fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {6294fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {
7153 const pt = f.object.dg.pt;6295 const pt = f.dg.pt;
7154 const zcu = pt.zcu;6296 const zcu = pt.zcu;
71556297
7156 const unwrapped = f.air.unwrapShuffleTwo(zcu, inst);6298 const unwrapped = f.air.unwrapShuffleTwo(zcu, inst);
...@@ -7160,38 +6302,38 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7160,38 +6302,38 @@ fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {
7160 const inst_ty = unwrapped.result_ty;6302 const inst_ty = unwrapped.result_ty;
7161 const elem_ty = inst_ty.childType(zcu);6303 const elem_ty = inst_ty.childType(zcu);
71626304
7163 const w = &f.object.code.writer;6305 const w = &f.code.writer;
7164 const local = try f.allocLocal(inst, inst_ty);6306 const local = try f.allocLocal(inst, inst_ty);
7165 try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands6307 try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands
7166 for (mask, 0..) |mask_elem, out_idx| {6308 for (mask, 0..) |mask_elem, out_idx| {
7167 try f.writeCValue(w, local, .Other);6309 try f.writeCValueMember(w, local, .{ .identifier = "array" });
7168 try w.writeByte('[');6310 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);
7170 try w.writeAll("] = ");6312 try w.writeAll("] = ");
7171 switch (mask_elem.unwrap()) {6313 switch (mask_elem.unwrap()) {
7172 .a_elem => |src_idx| {6314 .a_elem => |src_idx| {
7173 try f.writeCValue(w, operand_a, .Other);6315 try f.writeCValueMember(w, operand_a, .{ .identifier = "array" });
7174 try w.writeByte('[');6316 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);
7176 try w.writeByte(']');6318 try w.writeByte(']');
7177 },6319 },
7178 .b_elem => |src_idx| {6320 .b_elem => |src_idx| {
7179 try f.writeCValue(w, operand_b, .Other);6321 try f.writeCValueMember(w, operand_b, .{ .identifier = "array" });
7180 try w.writeByte('[');6322 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);
7182 try w.writeByte(']');6324 try w.writeByte(']');
7183 },6325 },
7184 .undef => try f.object.dg.renderUndefValue(w, elem_ty, .Other),6326 .undef => try f.dg.renderUndefValue(w, elem_ty, .other),
7185 }6327 }
7186 try w.writeByte(';');6328 try w.writeByte(';');
7187 try f.object.newline();6329 try f.newline();
7188 }6330 }
71896331
7190 return local;6332 return local;
7191}6333}
71926334
7193fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {6335fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
7194 const pt = f.object.dg.pt;6336 const pt = f.dg.pt;
7195 const zcu = pt.zcu;6337 const zcu = pt.zcu;
7196 const reduce = f.air.instructions.items(.data)[@intFromEnum(inst)].reduce;6338 const reduce = f.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
71976339
...@@ -7199,7 +6341,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7199,7 +6341,7 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
7199 const operand = try f.resolveInst(reduce.operand);6341 const operand = try f.resolveInst(reduce.operand);
7200 try reap(f, inst, &.{reduce.operand});6342 try reap(f, inst, &.{reduce.operand});
7201 const operand_ty = f.typeOf(reduce.operand);6343 const operand_ty = f.typeOf(reduce.operand);
7202 const w = &f.object.code.writer;6344 const w = &f.code.writer;
72036345
7204 const use_operator = scalar_ty.bitSize(zcu) <= 64;6346 const use_operator = scalar_ty.bitSize(zcu) <= 64;
7205 const op: union(enum) {6347 const op: union(enum) {
...@@ -7246,10 +6388,10 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7246,10 +6388,10 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
7246 // }6388 // }
72476389
7248 const accum = try f.allocLocal(inst, scalar_ty);6390 const accum = try f.allocLocal(inst, scalar_ty);
7249 try f.writeCValue(w, accum, .Other);6391 try f.writeCValue(w, accum, .other);
7250 try w.writeAll(" = ");6392 try w.writeAll(" = ");
72516393
7252 try f.object.dg.renderValue(w, switch (reduce.operation) {6394 try f.dg.renderValue(w, switch (reduce.operation) {
7253 .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) {6395 .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) {
7254 .bool => Value.false,6396 .bool => Value.false,
7255 .int => try pt.intValue(scalar_ty, 0),6397 .int => try pt.intValue(scalar_ty, 0),
...@@ -7285,58 +6427,58 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7285,58 +6427,58 @@ fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
7285 .float => try pt.floatValue(scalar_ty, std.math.nan(f128)),6427 .float => try pt.floatValue(scalar_ty, std.math.nan(f128)),
7286 else => unreachable,6428 else => unreachable,
7287 },6429 },
7288 }, .Other);6430 }, .other);
7289 try w.writeByte(';');6431 try w.writeByte(';');
7290 try f.object.newline();6432 try f.newline();
72916433
7292 const v = try Vectorize.start(f, inst, w, operand_ty);6434 const v = try Vectorize.start(f, inst, w, operand_ty);
7293 try f.writeCValue(w, accum, .Other);6435 try f.writeCValue(w, accum, .other);
7294 switch (op) {6436 switch (op) {
7295 .builtin => |func| {6437 .builtin => |func| {
7296 try w.print(" = zig_{s}_", .{func.operation});6438 try w.print(" = zig_{s}_", .{func.operation});
7297 try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);6439 try f.dg.renderTypeForBuiltinFnName(w, scalar_ty);
7298 try w.writeByte('(');6440 try w.writeByte('(');
7299 try f.writeCValue(w, accum, .FunctionArgument);6441 try f.writeCValue(w, accum, .other);
7300 try w.writeAll(", ");6442 try w.writeAll(", ");
7301 try f.writeCValue(w, operand, .Other);6443 try f.writeCValue(w, operand, .other);
7302 try v.elem(f, w);6444 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);
7304 try w.writeByte(')');6446 try w.writeByte(')');
7305 },6447 },
7306 .infix => |ass| {6448 .infix => |ass| {
7307 try w.writeAll(ass);6449 try w.writeAll(ass);
7308 try f.writeCValue(w, operand, .Other);6450 try f.writeCValue(w, operand, .other);
7309 try v.elem(f, w);6451 try v.elem(f, w);
7310 },6452 },
7311 .ternary => |cmp| {6453 .ternary => |cmp| {
7312 try w.writeAll(" = ");6454 try w.writeAll(" = ");
7313 try f.writeCValue(w, accum, .Other);6455 try f.writeCValue(w, accum, .other);
7314 try w.writeAll(cmp);6456 try w.writeAll(cmp);
7315 try f.writeCValue(w, operand, .Other);6457 try f.writeCValue(w, operand, .other);
7316 try v.elem(f, w);6458 try v.elem(f, w);
7317 try w.writeAll(" ? ");6459 try w.writeAll(" ? ");
7318 try f.writeCValue(w, accum, .Other);6460 try f.writeCValue(w, accum, .other);
7319 try w.writeAll(" : ");6461 try w.writeAll(" : ");
7320 try f.writeCValue(w, operand, .Other);6462 try f.writeCValue(w, operand, .other);
7321 try v.elem(f, w);6463 try v.elem(f, w);
7322 },6464 },
7323 }6465 }
7324 try w.writeByte(';');6466 try w.writeByte(';');
7325 try f.object.newline();6467 try f.newline();
7326 try v.end(f, inst, w);6468 try v.end(f, inst, w);
73276469
7328 return accum;6470 return accum;
7329}6471}
73306472
7331fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {6473fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
7332 const pt = f.object.dg.pt;6474 const pt = f.dg.pt;
7333 const zcu = pt.zcu;6475 const zcu = pt.zcu;
7334 const ip = &zcu.intern_pool;6476 const ip = &zcu.intern_pool;
7335 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6477 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
7336 const inst_ty = f.typeOfIndex(inst);6478 const inst_ty = f.typeOfIndex(inst);
7337 const len: usize = @intCast(inst_ty.arrayLen(zcu));6479 const len: usize = @intCast(inst_ty.arrayLen(zcu));
7338 const elements: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[ty_pl.payload..][0..len]);6480 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;
7340 const resolved_elements = try gpa.alloc(CValue, elements.len);6482 const resolved_elements = try gpa.alloc(CValue, elements.len);
7341 defer gpa.free(resolved_elements);6483 defer gpa.free(resolved_elements);
7342 for (resolved_elements, elements) |*resolved_element, element| {6484 for (resolved_elements, elements) |*resolved_element, element| {
...@@ -7349,28 +6491,23 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7349,28 +6491,23 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
7349 }6491 }
7350 }6492 }
73516493
7352 const w = &f.object.code.writer;6494 const w = &f.code.writer;
7353 const local = try f.allocLocal(inst, inst_ty);6495 const local = try f.allocLocal(inst, inst_ty);
7354 switch (ip.indexToKey(inst_ty.toIntern())) {6496 switch (ip.indexToKey(inst_ty.toIntern())) {
7355 inline .array_type, .vector_type => |info, tag| {6497 inline .array_type, .vector_type => |info, tag| {
7356 const a: Assignment = .{
7357 .ctype = try f.ctypeFromType(.fromInterned(info.child), .complete),
7358 };
7359 for (resolved_elements, 0..) |element, i| {6498 for (resolved_elements, 0..) |element, i| {
7360 try a.restart(f, w);6499 try f.writeCValueMember(w, local, .{ .identifier = "array" });
7361 try f.writeCValue(w, local, .Other);6500 try w.print("[{d}] = ", .{i});
7362 try w.print("[{d}]", .{i});6501 try f.writeCValue(w, element, .other);
7363 try a.assign(f, w);6502 try w.writeByte(';');
7364 try f.writeCValue(w, element, .Other);6503 try f.newline();
7365 try a.end(f, w);
7366 }6504 }
7367 if (tag == .array_type and info.sentinel != .none) {6505 if (tag == .array_type and info.sentinel != .none) {
7368 try a.restart(f, w);6506 try f.writeCValueMember(w, local, .{ .identifier = "array" });
7369 try f.writeCValue(w, local, .Other);6507 try w.print("[{d}] = ", .{info.len});
7370 try w.print("[{d}]", .{info.len});6508 try f.dg.renderValue(w, Value.fromInterned(info.sentinel), .other);
7371 try a.assign(f, w);6509 try w.writeByte(';');
7372 try f.object.dg.renderValue(w, Value.fromInterned(info.sentinel), .Other);6510 try f.newline();
7373 try a.end(f, w);
7374 }6511 }
7375 },6512 },
7376 .struct_type => {6513 .struct_type => {
...@@ -7382,11 +6519,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7382,11 +6519,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
7382 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);6519 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
7383 if (!field_ty.hasRuntimeBits(zcu)) continue;6520 if (!field_ty.hasRuntimeBits(zcu)) continue;
73846521
7385 const a = try Assignment.start(f, w, try f.ctypeFromType(field_ty, .complete));
7386 try f.writeCValueMember(w, local, .{ .identifier = loaded_struct.field_names.get(ip)[field_index].toSlice(ip) });6522 try f.writeCValueMember(w, local, .{ .identifier = loaded_struct.field_names.get(ip)[field_index].toSlice(ip) });
7387 try a.assign(f, w);6523 try w.writeAll(" = ");
7388 try f.writeCValue(w, resolved_elements[field_index], .Other);6524 try f.writeCValue(w, resolved_elements[field_index], .other);
7389 try a.end(f, w);6525 try w.writeByte(';');
6526 try f.newline();
7390 }6527 }
7391 },6528 },
7392 .@"packed" => unreachable, // `Air.Legalize.Feature.expand_packed_struct_init` handles this case6529 .@"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 {...@@ -7397,11 +6534,11 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
7397 const field_ty: Type = .fromInterned(tuple_info.types.get(ip)[field_index]);6534 const field_ty: Type = .fromInterned(tuple_info.types.get(ip)[field_index]);
7398 if (!field_ty.hasRuntimeBits(zcu)) continue;6535 if (!field_ty.hasRuntimeBits(zcu)) continue;
73996536
7400 const a = try Assignment.start(f, w, try f.ctypeFromType(field_ty, .complete));
7401 try f.writeCValueMember(w, local, .{ .field = field_index });6537 try f.writeCValueMember(w, local, .{ .field = field_index });
7402 try a.assign(f, w);6538 try w.writeAll(" = ");
7403 try f.writeCValue(w, resolved_elements[field_index], .Other);6539 try f.writeCValue(w, resolved_elements[field_index], .other);
7404 try a.end(f, w);6540 try w.writeByte(';');
6541 try f.newline();
7405 },6542 },
7406 else => unreachable,6543 else => unreachable,
7407 }6544 }
...@@ -7410,46 +6547,52 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7410,46 +6547,52 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
7410}6547}
74116548
7412fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {6549fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
7413 const pt = f.object.dg.pt;6550 const pt = f.dg.pt;
7414 const zcu = pt.zcu;6551 const zcu = pt.zcu;
7415 const ip = &zcu.intern_pool;6552 const ip = &zcu.intern_pool;
7416 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;6553 const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
7417 const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data;6554 const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data;
6555 const field_index = extra.field_index;
74186556
7419 const union_ty = f.typeOfIndex(inst);6557 const union_ty = f.typeOfIndex(inst);
7420 const loaded_union = ip.loadUnionType(union_ty.toIntern());6558 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];6559 const loaded_enum = ip.loadEnumType(loaded_union.enum_tag_type);
7422 const payload_ty = f.typeOf(extra.init);6560
7423 const payload = try f.resolveInst(extra.init);6561 const payload = try f.resolveInst(extra.init);
7424 try reap(f, inst, &.{extra.init});6562 try reap(f, inst, &.{extra.init});
74256563
7426 const w = &f.object.code.writer;6564 const w = &f.code.writer;
7427 if (loaded_union.layout == .@"packed") return f.moveCValue(inst, union_ty, payload);6565 if (loaded_union.layout == .@"packed") return f.moveCValue(inst, union_ty, payload);
74286566
7429 const local = try f.allocLocal(inst, union_ty);6567 const local = try f.allocLocal(inst, union_ty);
74306568
7431 const field: CValue = if (union_ty.unionTagTypeRuntime(zcu)) |tag_ty| field: {6569 if (loaded_union.has_runtime_tag) {
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));
7436 try f.writeCValueMember(w, local, .{ .identifier = "tag" });6570 try f.writeCValueMember(w, local, .{ .identifier = "tag" });
7437 try a.assign(f, w);6571 if (loaded_enum.field_values.len == 0) {
7438 try w.print("{f}", .{try f.fmtIntLiteralDec(tag_val.intFromEnum(zcu))});6572 // auto-numbered
7439 try a.end(f, w);6573 try w.print(" = {d};", .{field_index});
7440 break :field .{ .payload_identifier = field_name.toSlice(ip) };6574 } else {
7441 } else .{ .identifier = field_name.toSlice(ip) };6575 const tag_int_val: Value = .fromInterned(loaded_enum.field_values.get(ip)[field_index]);
74426576 try w.print(" = {f};", .{try f.fmtIntLiteralDec(tag_int_val)});
7443 const a = try Assignment.start(f, w, try f.ctypeFromType(payload_ty, .complete));6577 }
7444 try f.writeCValueMember(w, local, field);6578 try f.newline();
7445 try a.assign(f, w);6579 }
7446 try f.writeCValue(w, payload, .Other);6580
7447 try a.end(f, w);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();
7448 return local;6591 return local;
7449}6592}
74506593
7451fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {6594fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
7452 const pt = f.object.dg.pt;6595 const pt = f.dg.pt;
7453 const zcu = pt.zcu;6596 const zcu = pt.zcu;
7454 const prefetch = f.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;6597 const prefetch = f.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
74556598
...@@ -7457,16 +6600,16 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7457,16 +6600,16 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
7457 const ptr = try f.resolveInst(prefetch.ptr);6600 const ptr = try f.resolveInst(prefetch.ptr);
7458 try reap(f, inst, &.{prefetch.ptr});6601 try reap(f, inst, &.{prefetch.ptr});
74596602
7460 const w = &f.object.code.writer;6603 const w = &f.code.writer;
7461 switch (prefetch.cache) {6604 switch (prefetch.cache) {
7462 .data => {6605 .data => {
7463 try w.writeAll("zig_prefetch(");6606 try w.writeAll("zig_prefetch(");
7464 if (ptr_ty.isSlice(zcu))6607 if (ptr_ty.isSlice(zcu))
7465 try f.writeCValueMember(w, ptr, .{ .identifier = "ptr" })6608 try f.writeCValueMember(w, ptr, .{ .identifier = "ptr" })
7466 else6609 else
7467 try f.writeCValue(w, ptr, .FunctionArgument);6610 try f.writeCValue(w, ptr, .other);
7468 try w.print(", {d}, {d});", .{ @intFromEnum(prefetch.rw), prefetch.locality });6611 try w.print(", {d}, {d});", .{ @intFromEnum(prefetch.rw), prefetch.locality });
7469 try f.object.newline();6612 try f.newline();
7470 },6613 },
7471 // The available prefetch intrinsics do not accept a cache argument; only6614 // The available prefetch intrinsics do not accept a cache argument; only
7472 // address, rw, and locality.6615 // address, rw, and locality.
...@@ -7479,14 +6622,14 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7479,14 +6622,14 @@ fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
7479fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {6622fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
7480 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6623 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;
7483 const inst_ty = f.typeOfIndex(inst);6626 const inst_ty = f.typeOfIndex(inst);
7484 const local = try f.allocLocal(inst, inst_ty);6627 const local = try f.allocLocal(inst, inst_ty);
7485 try f.writeCValue(w, local, .Other);6628 try f.writeCValue(w, local, .other);
74866629
7487 try w.writeAll(" = ");6630 try w.writeAll(" = ");
7488 try w.print("zig_wasm_memory_size({d});", .{pl_op.payload});6631 try w.print("zig_wasm_memory_size({d});", .{pl_op.payload});
7489 try f.object.newline();6632 try f.newline();
74906633
7491 return local;6634 return local;
7492}6635}
...@@ -7494,23 +6637,23 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7494,23 +6637,23 @@ fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
7494fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {6637fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
7495 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6638 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;
7498 const inst_ty = f.typeOfIndex(inst);6641 const inst_ty = f.typeOfIndex(inst);
7499 const operand = try f.resolveInst(pl_op.operand);6642 const operand = try f.resolveInst(pl_op.operand);
7500 try reap(f, inst, &.{pl_op.operand});6643 try reap(f, inst, &.{pl_op.operand});
7501 const local = try f.allocLocal(inst, inst_ty);6644 const local = try f.allocLocal(inst, inst_ty);
7502 try f.writeCValue(w, local, .Other);6645 try f.writeCValue(w, local, .other);
75036646
7504 try w.writeAll(" = ");6647 try w.writeAll(" = ");
7505 try w.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload});6648 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);
7507 try w.writeAll(");");6650 try w.writeAll(");");
7508 try f.object.newline();6651 try f.newline();
7509 return local;6652 return local;
7510}6653}
75116654
7512fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {6655fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
7513 const pt = f.object.dg.pt;6656 const pt = f.dg.pt;
7514 const zcu = pt.zcu;6657 const zcu = pt.zcu;
7515 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;6658 const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
7516 const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data;6659 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 {...@@ -7523,24 +6666,24 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
7523 const inst_ty = f.typeOfIndex(inst);6666 const inst_ty = f.typeOfIndex(inst);
7524 const inst_scalar_ty = inst_ty.scalarType(zcu);6667 const inst_scalar_ty = inst_ty.scalarType(zcu);
75256668
7526 const w = &f.object.code.writer;6669 const w = &f.code.writer;
7527 const local = try f.allocLocal(inst, inst_ty);6670 const local = try f.allocLocal(inst, inst_ty);
7528 const v = try Vectorize.start(f, inst, w, inst_ty);6671 const v = try Vectorize.start(f, inst, w, inst_ty);
7529 try f.writeCValue(w, local, .Other);6672 try f.writeCValue(w, local, .other);
7530 try v.elem(f, w);6673 try v.elem(f, w);
7531 try w.writeAll(" = zig_fma_");6674 try w.writeAll(" = zig_fma_");
7532 try f.object.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty);6675 try f.dg.renderTypeForBuiltinFnName(w, inst_scalar_ty);
7533 try w.writeByte('(');6676 try w.writeByte('(');
7534 try f.writeCValue(w, mulend1, .FunctionArgument);6677 try f.writeCValue(w, mulend1, .other);
7535 try v.elem(f, w);6678 try v.elem(f, w);
7536 try w.writeAll(", ");6679 try w.writeAll(", ");
7537 try f.writeCValue(w, mulend2, .FunctionArgument);6680 try f.writeCValue(w, mulend2, .other);
7538 try v.elem(f, w);6681 try v.elem(f, w);
7539 try w.writeAll(", ");6682 try w.writeAll(", ");
7540 try f.writeCValue(w, addend, .FunctionArgument);6683 try f.writeCValue(w, addend, .other);
7541 try v.elem(f, w);6684 try v.elem(f, w);
7542 try w.writeAll(");");6685 try w.writeAll(");");
7543 try f.object.newline();6686 try f.newline();
7544 try v.end(f, inst, w);6687 try v.end(f, inst, w);
75456688
7546 return local;6689 return local;
...@@ -7548,34 +6691,33 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7548,34 +6691,33 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
75486691
7549fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue {6692fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue {
7550 const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;6693 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;
7552 const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty));6695 const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty));
7553 try f.writeCValue(w, local, .Other);6696 try f.writeCValue(w, local, .other);
7554 try w.writeAll(" = ");6697 try w.writeAll(" = ");
7555 try f.object.dg.renderNav(w, ty_nav.nav, .Other);6698 try f.dg.renderNav(w, ty_nav.nav, .other);
7556 try w.writeByte(';');6699 try w.writeByte(';');
7557 try f.object.newline();6700 try f.newline();
7558 return local;6701 return local;
7559}6702}
75606703
7561fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {6704fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
7562 const pt = f.object.dg.pt;6705 const pt = f.dg.pt;
7563 const zcu = pt.zcu;6706 const zcu = pt.zcu;
7564 const inst_ty = f.typeOfIndex(inst);6707 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;
7570 const local = try f.allocLocal(inst, inst_ty);6712 const local = try f.allocLocal(inst, inst_ty);
7571 try w.writeAll("va_start(*(va_list *)&");6713 try w.writeAll("va_start(*(va_list *)&");
7572 try f.writeCValue(w, local, .Other);6714 try f.writeCValue(w, local, .other);
7573 if (function_info.param_ctypes.len > 0) {6715 if (f.next_arg_index > 0) {
7574 try w.writeAll(", ");6716 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);
7576 }6718 }
7577 try w.writeAll(");");6719 try w.writeAll(");");
7578 try f.object.newline();6720 try f.newline();
7579 return local;6721 return local;
7580}6722}
75816723
...@@ -7586,15 +6728,15 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7586,15 +6728,15 @@ fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {
7586 const va_list = try f.resolveInst(ty_op.operand);6728 const va_list = try f.resolveInst(ty_op.operand);
7587 try reap(f, inst, &.{ty_op.operand});6729 try reap(f, inst, &.{ty_op.operand});
75886730
7589 const w = &f.object.code.writer;6731 const w = &f.code.writer;
7590 const local = try f.allocLocal(inst, inst_ty);6732 const local = try f.allocLocal(inst, inst_ty);
7591 try f.writeCValue(w, local, .Other);6733 try f.writeCValue(w, local, .other);
7592 try w.writeAll(" = va_arg(*(va_list *)");6734 try w.writeAll(" = va_arg(*(va_list *)");
7593 try f.writeCValue(w, va_list, .Other);6735 try f.writeCValue(w, va_list, .other);
7594 try w.writeAll(", ");6736 try w.writeAll(", ");
7595 try f.renderType(w, ty_op.ty.toType());6737 try f.renderType(w, ty_op.ty.toType());
7596 try w.writeAll(");");6738 try w.writeAll(");");
7597 try f.object.newline();6739 try f.newline();
7598 return local;6740 return local;
7599}6741}
76006742
...@@ -7604,11 +6746,11 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7604,11 +6746,11 @@ fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {
7604 const va_list = try f.resolveInst(un_op);6746 const va_list = try f.resolveInst(un_op);
7605 try reap(f, inst, &.{un_op});6747 try reap(f, inst, &.{un_op});
76066748
7607 const w = &f.object.code.writer;6749 const w = &f.code.writer;
7608 try w.writeAll("va_end(*(va_list *)");6750 try w.writeAll("va_end(*(va_list *)");
7609 try f.writeCValue(w, va_list, .Other);6751 try f.writeCValue(w, va_list, .other);
7610 try w.writeAll(");");6752 try w.writeAll(");");
7611 try f.object.newline();6753 try f.newline();
7612 return .none;6754 return .none;
7613}6755}
76146756
...@@ -7619,14 +6761,14 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -7619,14 +6761,14 @@ fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {
7619 const va_list = try f.resolveInst(ty_op.operand);6761 const va_list = try f.resolveInst(ty_op.operand);
7620 try reap(f, inst, &.{ty_op.operand});6762 try reap(f, inst, &.{ty_op.operand});
76216763
7622 const w = &f.object.code.writer;6764 const w = &f.code.writer;
7623 const local = try f.allocLocal(inst, inst_ty);6765 const local = try f.allocLocal(inst, inst_ty);
7624 try w.writeAll("va_copy(*(va_list *)&");6766 try w.writeAll("va_copy(*(va_list *)&");
7625 try f.writeCValue(w, local, .Other);6767 try f.writeCValue(w, local, .other);
7626 try w.writeAll(", *(va_list *)");6768 try w.writeAll(", *(va_list *)");
7627 try f.writeCValue(w, va_list, .Other);6769 try f.writeCValue(w, va_list, .other);
7628 try w.writeAll(");");6770 try w.writeAll(");");
7629 try f.object.newline();6771 try f.newline();
7630 return local;6772 return local;
7631}6773}
76326774
...@@ -7943,103 +7085,193 @@ fn undefPattern(comptime IntType: type) IntType {...@@ -7943,103 +7085,193 @@ fn undefPattern(comptime IntType: type) IntType {
79437085
7944const FormatIntLiteralContext = struct {7086const FormatIntLiteralContext = struct {
7945 dg: *DeclGen,7087 dg: *DeclGen,
7946 int_info: InternPool.Key.IntType,7088 loc: ValueRenderLocation,
7947 kind: CType.Kind,
7948 ctype: CType,
7949 val: Value,7089 val: Value,
7090 cty: CType,
7950 base: u8,7091 base: u8,
7951 case: std.fmt.Case,7092 case: std.fmt.Case,
7952};7093};
7953fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void {7094fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void {
7954 const pt = data.dg.pt;7095 const dg = data.dg;
7955 const zcu = pt.zcu;7096 const zcu = dg.pt.zcu;
7956 const target = &data.dg.mod.resolved_target.result;7097 const target = &dg.mod.resolved_target.result;
7957 const ctype_pool = &data.dg.ctype_pool;7098
79587099 const val = data.val;
7959 const ExpectedContents = struct {7100 const ty = val.typeOf(zcu);
7960 const base = 10;7101
7961 const bits = 128;7102 assert(!val.isUndef(zcu));
7962 const limbs_count = BigInt.calcTwosCompLimbCount(bits);7103
79637104 var space: Value.BigIntSpace = undefined;
7964 undef_limbs: [limbs_count]BigIntLimb,7105 const val_bigint = val.toBigInt(&space, zcu);
7965 wrap_limbs: [limbs_count]BigIntLimb,7106
7966 to_string_buf: [bits]u8,7107 switch (CType.classifyInt(ty, zcu)) {
7967 to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb,7108 .void => unreachable, // opv
7968 };7109 .small => |int_cty| return FormatInt128.format(.{
7969 var stack align(@alignOf(ExpectedContents)) =7110 .target = zcu.getTarget(),
7970 std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa);7111 .int_cty = int_cty,
7971 const allocator = stack.get();7112 .val = val_bigint,
79727113 .is_global = data.loc == .static_initializer,
7973 var undef_limbs: []BigIntLimb = &.{};7114 .base = data.base,
7974 defer allocator.free(undef_limbs);7115 .case = data.case,
79757116 }, w),
7976 var int_buf: Value.BigIntSpace = undefined;7117 .big => |big| {
7977 const int = if (data.val.isUndef(zcu)) blk: {7118 if (!data.loc.isInitializer()) {
7978 undef_limbs = allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits)) catch return error.WriteFailed;7119 // Use `CType.fmtTypeName` directly to avoid the possibility of `error.OutOfMemory`.
7979 @memset(undef_limbs, undefPattern(BigIntLimb));7120 try w.print("({f})", .{data.cty.fmtTypeName(zcu)});
79807121 }
7981 var undef_int = BigInt.Mutable{7122
7982 .limbs = undef_limbs,7123 try w.writeAll("{{");
7983 .len = undef_limbs.len,7124
7984 .positive = true,7125 var limb_buf: [std.math.big.int.calcTwosCompLimbCount(65535)]std.math.big.Limb = undefined;
7985 };7126 for (0..big.limbs_len) |limb_index| {
7986 undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits);7127 if (limb_index != 0) try w.writeAll(", ");
7987 break :blk undef_int.toConst();7128 const limb_bit_offset: u64 = switch (target.cpu.arch.endian()) {
7988 } else data.val.toBigInt(&int_buf, zcu);7129 .little => limb_index * big.limb_size.bits(),
7989 assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits));7130 .big => (big.limbs_len - limb_index - 1) * big.limb_size.bits(),
79907131 };
7991 const c_bits: usize = @intCast(data.ctype.byteSize(ctype_pool, data.dg.mod) * 8);7132 var limb_bigint: std.math.big.int.Mutable = .{
7992 var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined;7133 .limbs = &limb_buf,
7993 const one = BigInt.Mutable.init(&one_limbs, 1).toConst();7134 .len = undefined,
79947135 .positive = undefined,
7995 var wrap = BigInt.Mutable{7136 };
7996 .limbs = allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits)) catch return error.WriteFailed,7137 limb_bigint.shiftRight(val_bigint, limb_bit_offset);
7997 .len = undefined,7138 limb_bigint.truncate(limb_bigint.toConst(), .unsigned, big.limb_size.bits());
7998 .positive = undefined,7139 try FormatInt128.format(.{
7999 };7140 .target = zcu.getTarget(),
8000 defer allocator.free(wrap.limbs);7141 .int_cty = big.limb_size.unsigned(),
80017142 .val = limb_bigint.toConst(),
8002 const c_limb_info: struct {7143 .is_global = data.loc == .static_initializer,
8003 ctype: CType,7144 .base = data.base,
8004 count: usize,7145 .case = data.case,
8005 endian: std.builtin.Endian,7146 }, w);
8006 homogeneous: bool,7147 }
8007 } = switch (data.ctype.info(ctype_pool)) {7148
8008 .basic => |basic_info| switch (basic_info) {7149 try w.writeAll("}}");
8009 else => .{7150 },
8010 .ctype = .void,7151 }
8011 .count = 1,7152}
8012 .endian = .little,7153const FormatInt128 = struct {
8013 .homogeneous = true,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 });
8014 },7206 },
8015 .zig_u128, .zig_i128 => .{7207
8016 .ctype = .u64,7208 .zig_i128 => {
8017 .count = 2,7209 const raw = val.toInt(i128) catch unreachable;
8018 .endian = .big,7210 const lo: u64 = @truncate(@as(u128, @bitCast(raw)));
8019 .homogeneous = false,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 });
8020 },7218 },
8021 },7219 }
8022 .array => |array_info| .{7220 }
8023 .ctype = array_info.elem_ctype,7221};
8024 .count = @intCast(array_info.len),7222fn fmtUnsignedIntLiteralSmall(
8025 .endian = target.cpu.arch.endian(),7223 target: *const std.Target,
8026 .homogeneous = true,7224 int_cty: CType.Int,
8027 },7225 val: u64,
8028 else => unreachable,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,
8029 };7237 };
8030 if (c_limb_info.count == 1) {7238}
8031 if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or7239fn fmtSignedIntLiteralSmall(
8032 data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits))7240 target: *const std.Target,
8033 return w.print("{s}_{s}", .{7241 int_cty: CType.Int,
8034 data.ctype.getStandardDefineAbbrev() orelse return w.print("zig_{s}Int_{c}{d}", .{7242 val: i64,
8035 if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits,7243 is_global: bool,
8036 }),7244 base: u8,
8037 if (int.positive) "MAX" else "MIN",7245 case: std.fmt.Case,
8038 });7246) FormatSignedIntLiteralSmall {
80397247 return .{
8040 if (!int.positive) try w.writeByte('-');7248 .target = target,
8041 try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool);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));
8043 switch (data.base) {7275 switch (data.base) {
8044 2 => try w.writeAll("0b"),7276 2 => try w.writeAll("0b"),
8045 8 => try w.writeByte('0'),7277 8 => try w.writeByte('0'),
...@@ -8047,68 +7279,131 @@ fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void...@@ -8047,68 +7279,131 @@ fn formatIntLiteral(data: FormatIntLiteralContext, w: *Writer) Writer.Error!void
8047 16 => try w.writeAll("0x"),7279 16 => try w.writeAll("0x"),
8048 else => unreachable,7280 else => unreachable,
8049 }7281 }
8050 const string = int.abs().toStringAlloc(allocator, data.base, data.case) catch7282 // This `@abs` is safe thanks to the `min_int` case above.
8051 return error.WriteFailed;7283 try w.printInt(@abs(data.val), data.base, data.case, .{});
8052 defer allocator.free(string);7284 try w.writeAll(intLiteralSuffix(data.int_cty));
8053 try w.writeAll(string);7285 }
8054 } else {7286};
8055 try data.ctype.renderLiteralPrefix(w, data.kind, ctype_pool);7287const FormatUnsignedIntLiteralSmall = struct {
8056 wrap.truncate(int, .unsigned, c_bits);7288 target: *const std.Target,
8057 @memset(wrap.limbs[wrap.len..], 0);7289 int_cty: CType.Int,
8058 wrap.len = wrap.limbs.len;7290 val: u64,
8059 const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);7291 is_global: bool,
80607292 base: u8,
8061 var c_limb_int_info: std.builtin.Type.Int = .{7293 case: std.fmt.Case,
8062 .signedness = undefined,7294 pub fn format(data: FormatUnsignedIntLiteralSmall, w: *Writer) Writer.Error!void {
8063 .bits = @intCast(@divExact(c_bits, c_limb_info.count)),7295 const bits = data.int_cty.bits(data.target);
8064 };7296 const max_int: u64 = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - bits);
8065 var c_limb_ctype: CType = undefined;7297 if (data.val == max_int) {
80667298 return w.print("{s}_MAX", .{minMaxMacroPrefix(data.int_cty)});
8067 var limb_offset: usize = 0;7299 }
8068 const most_significant_limb_i = wrap.len - limbs_per_c_limb;7300 try w.writeAll(intLiteralPrefix(data.int_cty, data.is_global));
8069 while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) {7301 switch (data.base) {
8070 const limb_i = switch (c_limb_info.endian) {7302 2 => try w.writeAll("0b"),
8071 .little => limb_offset,7303 8 => try w.writeByte('0'),
8072 .big => most_significant_limb_i - limb_offset,7304 10 => {},
8073 };7305 16 => try w.writeAll("0x"),
8074 var c_limb_mut = BigInt.Mutable{7306 else => unreachable,
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);
8109 }7307 }
7308 try w.printInt(data.val, data.base, data.case, .{});
7309 try w.writeAll(intLiteralSuffix(data.int_cty));
8110 }7310 }
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 };
8112}7407}
81137408
8114const Materialize = struct {7409const Materialize = struct {
...@@ -8123,7 +7418,7 @@ const Materialize = struct {...@@ -8123,7 +7418,7 @@ const Materialize = struct {
8123 }7418 }
81247419
8125 pub fn mat(self: Materialize, f: *Function, w: *Writer) !void {7420 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);
8127 }7422 }
81287423
8129 pub fn end(self: Materialize, f: *Function, inst: Air.Inst.Index) !void {7424 pub fn end(self: Materialize, f: *Function, inst: Air.Inst.Index) !void {
...@@ -8131,95 +7426,52 @@ const Materialize = struct {...@@ -8131,95 +7426,52 @@ const Materialize = struct {
8131 }7426 }
8132};7427};
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
8178const Vectorize = struct {7429const Vectorize = struct {
8179 index: CValue = .none,7430 index: CValue = .none,
81807431
8181 pub fn start(f: *Function, inst: Air.Inst.Index, w: *Writer, ty: Type) !Vectorize {7432 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;
8183 const zcu = pt.zcu;7434 const zcu = pt.zcu;
8184 return if (ty.zigTypeTag(zcu) == .vector) index: {7435 switch (ty.zigTypeTag(zcu)) {
8185 const local = try f.allocLocal(inst, .usize);7436 else => return .{ .index = .none },
81867437 .vector => {
8187 try w.writeAll("for (");7438 const local = try f.allocLocal(inst, .usize);
8188 try f.writeCValue(w, local, .Other);7439 try w.writeAll("for (");
8189 try w.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)});7440 try f.writeCValue(w, local, .other);
8190 try f.writeCValue(w, local, .Other);7441 try w.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)});
8191 try w.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))});7442 try f.writeCValue(w, local, .other);
8192 try f.writeCValue(w, local, .Other);7443 try w.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))});
8193 try w.print(" += {f}) {{\n", .{try f.fmtIntLiteralDec(.one_usize)});7444 try f.writeCValue(w, local, .other);
8194 f.object.indent();7445 try w.print(" += {f}) {{", .{try f.fmtIntLiteralDec(.one_usize)});
8195 try f.object.newline();7446 f.indent();
81967447 try f.newline();
8197 break :index .{ .index = local };7448 return .{ .index = local };
8198 } else .{};7449 },
7450 }
8199 }7451 }
82007452
8201 pub fn elem(self: Vectorize, f: *Function, w: *Writer) !void {7453 pub fn elem(self: Vectorize, f: *Function, w: *Writer) !void {
8202 if (self.index != .none) {7454 if (self.index != .none) {
8203 try w.writeByte('[');7455 try w.writeAll(".array[");
8204 try f.writeCValue(w, self.index, .Other);7456 try f.writeCValue(w, self.index, .other);
8205 try w.writeByte(']');7457 try w.writeByte(']');
8206 }7458 }
8207 }7459 }
82087460
8209 pub fn end(self: Vectorize, f: *Function, inst: Air.Inst.Index, w: *Writer) !void {7461 pub fn end(self: Vectorize, f: *Function, inst: Air.Inst.Index, w: *Writer) !void {
8210 if (self.index != .none) {7462 if (self.index != .none) {
8211 try f.object.outdent();7463 try f.outdent();
8212 try w.writeByte('}');7464 try w.writeByte('}');
8213 try f.object.newline();7465 try f.newline();
8214 try freeLocal(f, inst, self.index.new_local, null);7466 try freeLocal(f, inst, self.index.new_local, null);
8215 }7467 }
8216 }7468 }
8217};7469};
82187470
8219fn lowersToArray(ty: Type, zcu: *Zcu) bool {7471fn lowersToBigInt(ty: Type, zcu: *const Zcu) bool {
8220 return switch (ty.zigTypeTag(zcu)) {7472 return switch (ty.zigTypeTag(zcu)) {
8221 .array, .vector => return true,7473 .int, .@"enum", .@"struct", .@"union" => CType.classifyInt(ty, zcu) == .big,
8222 else => return ty.isAbiInt(zcu) and toCIntBits(@as(u32, @intCast(ty.bitSize(zcu)))) == null,7474 else => false,
8223 };7475 };
8224}7476}
82257477
...@@ -8245,8 +7497,8 @@ fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void {...@@ -8245,8 +7497,8 @@ fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void {
8245}7497}
82467498
8247fn freeLocal(f: *Function, inst: ?Air.Inst.Index, local_index: LocalIndex, ref_inst: ?Air.Inst.Index) !void {7499fn freeLocal(f: *Function, inst: ?Air.Inst.Index, local_index: LocalIndex, ref_inst: ?Air.Inst.Index) !void {
8248 const gpa = f.object.dg.gpa;7500 const gpa = f.dg.gpa;
8249 const local = &f.locals.items[local_index];7501 const local = f.locals.items[local_index];
8250 if (inst) |i| {7502 if (inst) |i| {
8251 if (ref_inst) |operand| {7503 if (ref_inst) |operand| {
8252 log.debug("%{d}: freeing t{d} (operand %{d})", .{ @intFromEnum(i), local_index, operand });7504 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...@@ -8260,7 +7512,7 @@ fn freeLocal(f: *Function, inst: ?Air.Inst.Index, local_index: LocalIndex, ref_i
8260 log.debug("freeing t{d}", .{local_index});7512 log.debug("freeing t{d}", .{local_index});
8261 }7513 }
8262 }7514 }
8263 const gop = try f.free_locals_map.getOrPut(gpa, local.getType());7515 const gop = try f.free_locals_map.getOrPut(gpa, local);
8264 if (!gop.found_existing) gop.value_ptr.* = .{};7516 if (!gop.found_existing) gop.value_ptr.* = .{};
8265 if (std.debug.runtime_safety) {7517 if (std.debug.runtime_safety) {
8266 // If this trips, an unfreeable allocation was attempted to be freed.7518 // If this trips, an unfreeable allocation was attempted to be freed.
...@@ -8317,3 +7569,28 @@ fn deinitFreeLocalsMap(gpa: Allocator, map: *LocalsMap) void {...@@ -8317,3 +7569,28 @@ fn deinitFreeLocalsMap(gpa: Allocator, map: *LocalsMap) void {
8317 }7569 }
8318 map.deinit(gpa);7570 map.deinit(gpa);
8319}7571}
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");...@@ -23,7 +23,6 @@ const Package = @import("../Package.zig");
23const Air = @import("../Air.zig");23const Air = @import("../Air.zig");
24const Value = @import("../Value.zig");24const Value = @import("../Value.zig");
25const Type = @import("../Type.zig");25const Type = @import("../Type.zig");
26const DebugConstPool = link.DebugConstPool;
27const codegen = @import("../codegen.zig");26const codegen = @import("../codegen.zig");
28const x86_64_abi = @import("x86_64/abi.zig");27const x86_64_abi = @import("x86_64/abi.zig");
29const wasm_c_abi = @import("wasm/abi.zig");28const wasm_c_abi = @import("wasm/abi.zig");
...@@ -532,8 +531,8 @@ pub const Object = struct {...@@ -532,8 +531,8 @@ pub const Object = struct {
532 debug_file_map: std.AutoHashMapUnmanaged(Zcu.File.Index, Builder.Metadata),531 debug_file_map: std.AutoHashMapUnmanaged(Zcu.File.Index, Builder.Metadata),
533532
534 /// This pool *only* contains types (and does not contain `@as(type, undefined)`).533 /// This pool *only* contains types (and does not contain `@as(type, undefined)`).
535 debug_type_pool: DebugConstPool,534 debug_type_pool: link.ConstPool,
536 /// Keyed on `DebugConstPool.Index`.535 /// Keyed on `link.ConstPool.Index`.
537 debug_types: std.ArrayList(Builder.Metadata),536 debug_types: std.ArrayList(Builder.Metadata),
538 /// Initially `.none`, set if the type `anyerror` is lowered to a debug type. The type will not537 /// Initially `.none`, set if the type `anyerror` is lowered to a debug type. The type will not
539 /// actually be created until `emit`, which must resolve this reference with an appropriate enum538 /// actually be created until `emit`, which must resolve this reference with an appropriate enum
...@@ -1622,10 +1621,7 @@ pub const Object = struct {...@@ -1622,10 +1621,7 @@ pub const Object = struct {
1622 }1621 }
16231622
1624 fn flushPendingDebugTypes(o: *Object, pt: Zcu.PerThread) Allocator.Error!void {1623 fn flushPendingDebugTypes(o: *Object, pt: Zcu.PerThread) Allocator.Error!void {
1625 o.debug_type_pool.flushPending(pt, .{ .llvm = o }) catch |err| switch (err) {1624 try o.debug_type_pool.flushPending(pt, .{ .llvm = o });
1626 error.OutOfMemory => |e| return e,
1627 else => unreachable, // TODO: stop self-hosted backends from returning all of this crap!
1628 };
1629 }1625 }
16301626
1631 pub fn updateExports(1627 pub fn updateExports(
...@@ -1823,17 +1819,14 @@ pub const Object = struct {...@@ -1823,17 +1819,14 @@ pub const Object = struct {
18231819
1824 pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void {1820 pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void {
1825 if (!o.builder.strip) {1821 if (!o.builder.strip) {
1826 o.debug_type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success) catch |err| switch (err) {1822 try o.debug_type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success);
1827 error.OutOfMemory => |e| return e,
1828 else => unreachable, // TODO: stop self-hosted backends from returning all of this crap!
1829 };
1830 }1823 }
1831 }1824 }
18321825
1833 /// Should only be called by the `DebugConstPool` implementation.1826 /// Should only be called by the `link.ConstPool` implementation.
1834 ///1827 ///
1835 /// `val` is always a type because `o.debug_type_pool` only contains types.1828 /// `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 {
1837 const zcu = pt.zcu;1830 const zcu = pt.zcu;
1838 const gpa = zcu.comp.gpa;1831 const gpa = zcu.comp.gpa;
1839 assert(zcu.intern_pool.typeOf(val) == .type_type);1832 assert(zcu.intern_pool.typeOf(val) == .type_type);
...@@ -1846,10 +1839,10 @@ pub const Object = struct {...@@ -1846,10 +1839,10 @@ pub const Object = struct {
1846 o.debug_anyerror_fwd_ref = fwd_ref.toOptional();1839 o.debug_anyerror_fwd_ref = fwd_ref.toOptional();
1847 }1840 }
1848 }1841 }
1849 /// Should only be called by the `DebugConstPool` implementation.1842 /// Should only be called by the `link.ConstPool` implementation.
1850 ///1843 ///
1851 /// `val` is always a type because `o.debug_type_pool` only contains types.1844 /// `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 {
1853 assert(pt.zcu.intern_pool.typeOf(val) == .type_type);1846 assert(pt.zcu.intern_pool.typeOf(val) == .type_type);
1854 const fwd_ref = o.debug_types.items[@intFromEnum(index)];1847 const fwd_ref = o.debug_types.items[@intFromEnum(index)];
1855 assert(val != .anyerror_type);1848 assert(val != .anyerror_type);
...@@ -1857,10 +1850,10 @@ pub const Object = struct {...@@ -1857,10 +1850,10 @@ pub const Object = struct {
1857 const debug_incomplete_type = try o.builder.debugSignedType(name_str, 0);1850 const debug_incomplete_type = try o.builder.debugSignedType(name_str, 0);
1858 o.builder.resolveDebugForwardReference(fwd_ref, debug_incomplete_type);1851 o.builder.resolveDebugForwardReference(fwd_ref, debug_incomplete_type);
1859 }1852 }
1860 /// Should only be called by the `DebugConstPool` implementation.1853 /// Should only be called by the `link.ConstPool` implementation.
1861 ///1854 ///
1862 /// `val` is always a type because `o.debug_type_pool` only contains types.1855 /// `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 {
1864 assert(pt.zcu.intern_pool.typeOf(val) == .type_type);1857 assert(pt.zcu.intern_pool.typeOf(val) == .type_type);
1865 const fwd_ref = o.debug_types.items[@intFromEnum(index)];1858 const fwd_ref = o.debug_types.items[@intFromEnum(index)];
1866 if (val == .anyerror_type) {1859 if (val == .anyerror_type) {
...@@ -1890,10 +1883,7 @@ pub const Object = struct {...@@ -1890,10 +1883,7 @@ pub const Object = struct {
18901883
1891 fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata {1884 fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata {
1892 assert(!o.builder.strip);1885 assert(!o.builder.strip);
1893 const index = o.debug_type_pool.get(pt, .{ .llvm = o }, ty.toIntern()) catch |err| switch (err) {1886 const index = try o.debug_type_pool.get(pt, .{ .llvm = o }, ty.toIntern());
1894 error.OutOfMemory => |e| return e,
1895 else => unreachable, // TODO: stop self-hosted backends from returning all of this crap!
1896 };
1897 return o.debug_types.items[@intFromEnum(index)];1887 return o.debug_types.items[@intFromEnum(index)];
1898 }1888 }
18991889
src/link.zig+2-2
...@@ -29,7 +29,7 @@ const codegen = @import("codegen.zig");...@@ -29,7 +29,7 @@ const codegen = @import("codegen.zig");
29pub const aarch64 = @import("link/aarch64.zig");29pub const aarch64 = @import("link/aarch64.zig");
30pub const LdScript = @import("link/LdScript.zig");30pub const LdScript = @import("link/LdScript.zig");
31pub const Queue = @import("link/Queue.zig");31pub const Queue = @import("link/Queue.zig");
32pub const DebugConstPool = @import("link/DebugConstPool.zig");32pub const ConstPool = @import("link/ConstPool.zig");
3333
34pub const Diags = struct {34pub const Diags = struct {
35 /// Stored here so that function definitions can distinguish between35 /// Stored here so that function definitions can distinguish between
...@@ -804,7 +804,7 @@ pub const File = struct {...@@ -804,7 +804,7 @@ pub const File = struct {
804 switch (base.tag) {804 switch (base.tag) {
805 .lld => unreachable,805 .lld => unreachable,
806 else => {},806 else => {},
807 inline .elf => |tag| {807 inline .elf, .c => |tag| {
808 dev.check(tag.devFeature());808 dev.check(tag.devFeature());
809 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success);809 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty, success);
810 },810 },
src/link/C.zig+1272-627
...@@ -1,3 +1,9 @@...@@ -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
1const std = @import("std");7const std = @import("std");
2const mem = std.mem;8const mem = std.mem;
3const assert = std.debug.assert;9const assert = std.debug.assert;
...@@ -5,7 +11,6 @@ const Allocator = std.mem.Allocator;...@@ -5,7 +11,6 @@ const Allocator = std.mem.Allocator;
5const fs = std.fs;11const fs = std.fs;
6const Path = std.Build.Cache.Path;12const Path = std.Build.Cache.Path;
713
8const C = @This();
9const build_options = @import("build_options");14const build_options = @import("build_options");
10const Zcu = @import("../Zcu.zig");15const Zcu = @import("../Zcu.zig");
11const Module = @import("../Package/Module.zig");16const Module = @import("../Package/Module.zig");
...@@ -19,40 +24,45 @@ const Type = @import("../Type.zig");...@@ -19,40 +24,45 @@ const Type = @import("../Type.zig");
19const Value = @import("../Value.zig");24const Value = @import("../Value.zig");
20const AnyMir = @import("../codegen.zig").AnyMir;25const AnyMir = @import("../codegen.zig").AnyMir;
2126
22pub const zig_h = "#include \"zig.h\"\n";
23
24base: link.File,27base: link.File,
25/// This linker backend does not try to incrementally link output C source code.28
26/// Instead, it tracks all declarations in this table, and iterates over it29/// All the string bytes of rendered C code, all squished into one array. `String` is used to refer
27/// in the flush function, stitching pre-rendered pieces of C code together.30/// to specific slices of this array, used for the rendered C code of an individual UAV/NAV/type.
28navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, AvBlock),31///
29/// All the string bytes of rendered C code, all squished into one array.32/// During code generation for functions, a separate buffer is used, and the contents of that buffer
30/// While in progress, a separate buffer is used, and then when finished, the33/// are copied into `string_bytes` when the function is emitted by `updateFunc`.
31/// buffer is copied into this one.
32string_bytes: std.ArrayList(u8),34string_bytes: std.ArrayList(u8),
33/// Tracks all the anonymous decls that are used by all the decls so they can35
34/// be rendered during flush().36/// Like with `string_bytes`, we concatenate all type dependencies into one array, and slice into it
35uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, AvBlock),37/// for specific groups of dependencies. These values are indices into `type_pool`, and thus also
36/// Sparse set of uavs that are overaligned. Underaligned anon decls are38/// into `types`. We store these instead of `InternPool.Index` because it lets us avoid some hash
37/// lowered the same as ABI-aligned anon decls. The keys here are a subset of39/// map lookups in `flush`.
38/// the keys of `uavs`.40type_dependencies: std.ArrayList(link.ConstPool.Index),
39aligned_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),41/// For storing dependencies on "aligned" versions of types, we must associate each type with a
4042/// bitmask of required alignments. As with `type_dependencies`, we concatenate all such masks into
41exported_navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, ExportedBlock),43/// one array.
42exported_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, ExportedBlock),44align_dependency_masks: std.ArrayList(u64),
4345
44/// Optimization, `updateDecl` reuses this buffer rather than creating a new46/// All NAVs, regardless of whether they are functions or simple constants, are put in this map.
45/// one with every call.47navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, RenderedDecl),
46fwd_decl_buf: []u8,48/// All UAVs which may be referenced are in this map. The UAV alignment is not included in the
47/// Optimization, `updateDecl` reuses this buffer rather than creating a new49/// rendered C code stored here, because we don't know the alignment a UAV needs until `flush`.
48/// one with every call.50uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, RenderedDecl),
49code_header_buf: []u8,51/// Contains all types which are needed by some other rendered code. Does not contain any constants
50/// Optimization, `updateDecl` reuses this buffer rather than creating a new52/// other than types.
51/// one with every call.53type_pool: link.ConstPool,
52code_buf: []u8,54/// Indices are `link.ConstPool.Index` from `type_pool`. Contains rendered C code for every type
53/// Optimization, `flush` reuses this buffer rather than creating a new55/// which may be referenced. Logic in `flush` will perform the appropriate topological sort to emit
54/// one with every call.56/// these type definitions in an order which C allows.
55scratch_buf: []u32,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
57/// A reference into `string_bytes`.67/// A reference into `string_bytes`.
58const String = extern struct {68const String = extern struct {
...@@ -64,50 +74,320 @@ const String = extern struct {...@@ -64,50 +74,320 @@ const String = extern struct {
64 .len = 0,74 .len = 0,
65 };75 };
6676
67 fn concat(lhs: String, rhs: String) String {77 fn get(s: String, c: *C) []const u8 {
68 assert(lhs.start + lhs.len == rhs.start);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..];
69 return .{111 return .{
70 .start = lhs.start,112 .type = types_overlong[0..td.len],
71 .len = lhs.len + rhs.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],
72 };118 };
73 }119 }
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 };
74};130};
75131
76/// Per-declaration data.132const RenderedDecl = struct {
77pub const AvBlock = struct {133 fwd_decl: String,
78 fwd_decl: String = .empty,134 code: String,
79 code: String = .empty,135 ctype_deps: CTypeDependencies,
80 /// Each `Decl` stores a set of used `CType`s. In `flush()`, we iterate136 need_uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
81 /// over each `Decl` and generate the definition for each used `CType` once.137 need_tag_name_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Index, void),
82 ctype_pool: codegen.CType.Pool = .empty,138 need_never_tail_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
83 /// May contain string references to ctype_pool139 need_never_inline_funcs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void),
84 lazy_fns: codegen.LazyFnMap = .{},140
85141 const init: RenderedDecl = .{
86 fn deinit(ab: *AvBlock, gpa: Allocator) void {142 .fwd_decl = .empty,
87 ab.lazy_fns.deinit(gpa);143 .code = .empty,
88 ab.ctype_pool.deinit(gpa);144 .ctype_deps = .empty,
89 ab.* = undefined;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();
90 }169 }
91};170};
92171
93/// Per-exported-symbol data.172const RenderedType = struct {
94pub const ExportedBlock = struct {173 /// If this type lowers to an aggregate, this is a forward declaration of its struct/union tag.
95 fwd_decl: String = .empty,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,
96};202};
97203
98pub fn getString(this: C, s: String) []const u8 {204/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type.
99 return this.string_bytes.items[s.start..][0..s.len];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 }
100}267}
101268
102pub fn addString(this: *C, s: []const u8) Allocator.Error!String {269/// Only called by `link.ConstPool` due to `c.type_pool`, so `val` is always a type.
103 const comp = this.base.comp;270pub fn updateConstIncomplete(
104 const gpa = comp.gpa;271 c: *C,
105 try this.string_bytes.appendSlice(gpa, s);272 pt: Zcu.PerThread,
106 return .{273 index: link.ConstPool.Index,
107 .start = @intCast(this.string_bytes.items.len - s.len),274 val: InternPool.Index,
108 .len = @intCast(s.len),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 };
109 };303 };
110}304}
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
112pub fn open(392pub fn open(
113 arena: Allocator,393 arena: Allocator,
...@@ -156,267 +436,622 @@ pub fn createEmpty(...@@ -156,267 +436,622 @@ pub fn createEmpty(
156 .file = file,436 .file = file,
157 .build_id = options.build_id,437 .build_id = options.build_id,
158 },438 },
159 .navs = .empty,
160 .string_bytes = .empty,439 .string_bytes = .empty,
440 .type_dependencies = .empty,
441 .align_dependency_masks = .empty,
442 .navs = .empty,
161 .uavs = .empty,443 .uavs = .empty,
162 .aligned_uavs = .empty,444 .type_pool = .empty,
445 .types = .empty,
446 .bigint_types = .empty,
163 .exported_navs = .empty,447 .exported_navs = .empty,
164 .exported_uavs = .empty,448 .exported_uavs = .empty,
165 .fwd_decl_buf = &.{},
166 .code_header_buf = &.{},
167 .code_buf = &.{},
168 .scratch_buf = &.{},
169 };449 };
170450
171 return c_file;451 return c_file;
172}452}
173453
174pub fn deinit(self: *C) void {454pub fn deinit(c: *C) void {
175 const gpa = self.base.comp.gpa;455 const gpa = c.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);
187456
188 self.exported_navs.deinit(gpa);457 for (c.navs.values()) |*r| r.deinit(gpa);
189 self.exported_uavs.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);472pub fn updateContainerType(
192 gpa.free(self.fwd_decl_buf);473 c: *C,
193 gpa.free(self.code_header_buf);474 pt: Zcu.PerThread,
194 gpa.free(self.code_buf);475 ty: InternPool.Index,
195 gpa.free(self.scratch_buf);476 success: bool,
477) link.File.UpdateContainerTypeError!void {
478 try c.type_pool.updateContainerType(pt, .{ .c = c }, ty, success);
196}479}
197480
198pub fn updateFunc(481pub fn updateFunc(
199 self: *C,482 c: *C,
200 pt: Zcu.PerThread,483 pt: Zcu.PerThread,
201 func_index: InternPool.Index,484 func_index: InternPool.Index,
202 mir: *AnyMir,485 mir: *AnyMir,
203) link.File.UpdateNavError!void {486) Allocator.Error!void {
204 const zcu = pt.zcu;487 const zcu = pt.zcu;
205 const gpa = zcu.gpa;488 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);491 const rendered_decl: *RenderedDecl = rd: {
209 if (gop.found_existing) gop.value_ptr.deinit(gpa);492 const gop = try c.navs.getOrPut(gpa, nav);
210 gop.value_ptr.* = .{493 if (gop.found_existing) gop.value_ptr.deinit(gpa);
211 .code = .empty,494 break :rd gop.value_ptr;
212 .fwd_decl = .empty,
213 .ctype_pool = mir.c.ctype_pool.move(),
214 .lazy_fns = mir.c.lazy_fns.move(),
215 };495 };
216 gop.value_ptr.fwd_decl = try self.addString(mir.c.fwd_decl);496 c.navs.lockPointers();
217 const code_header = try self.addString(mir.c.code_header);497 defer c.navs.unlockPointers();
218 const code = try self.addString(mir.c.code);498
219 gop.value_ptr.code = code_header.concat(code);499 rendered_decl.* = .{
220 try self.addUavsFromCodegen(&mir.c.uavs);500 .fwd_decl = try c.addString(&.{mir.c.fwd_decl}),
221}501 .code = try c.addString(&.{ mir.c.code_header, mir.c.code }),
222502 .ctype_deps = try c.addCTypeDependencies(pt, &mir.c.ctype_deps),
223fn updateUav(self: *C, pt: Zcu.PerThread, i: usize) link.File.FlushError!void {503 .need_uavs = mir.c.need_uavs.move(),
224 const gpa = self.base.comp.gpa;504 .need_tag_name_funcs = mir.c.need_tag_name_funcs.move(),
225 const uav = self.uavs.keys()[i];505 .need_never_tail_funcs = mir.c.need_never_tail_funcs.move(),
226506 .need_never_inline_funcs = mir.c.need_never_inline_funcs.move(),
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,
266 };507 };
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);521 try c.type_pool.flushPending(pt, .{ .c = c });
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 };
276}522}
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 {
279 const tracy = trace(@src());529 const tracy = trace(@src());
280 defer tracy.end();530 defer tracy.end();
281531
282 const gpa = self.base.comp.gpa;532 const gpa = c.base.comp.gpa;
283 const zcu = pt.zcu;533 const zcu = pt.zcu;
284 const ip = &zcu.intern_pool;534 const ip = &zcu.intern_pool;
285535
286 const nav = ip.getNav(nav_index);536 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)) {
288 .func => return,538 .func => return,
289 .@"extern" => .none,539 .@"extern" => {},
290 .variable => |variable| variable.init,540 else => {
291 else => nav.status.fully_resolved.val,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;
292 };560 };
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);564 {
296 errdefer _ = self.navs.pop();565 var arena: std.heap.ArenaAllocator = .init(gpa);
297 if (!gop.found_existing) gop.value_ptr.* = .{};566 defer arena.deinit();
298 const ctype_pool = &gop.value_ptr.ctype_pool;
299 try ctype_pool.init(gpa);
300 ctype_pool.clearRetainingCapacity();
301567
302 var object: codegen.Object = .{568 var dg: codegen.DeclGen = .{
303 .dg = .{
304 .gpa = gpa,569 .gpa = gpa,
570 .arena = arena.allocator(),
305 .pt = pt,571 .pt = pt,
306 .mod = zcu.navFileScope(nav_index).mod.?,572 .mod = zcu.navFileScope(nav_index).mod.?,
307 .error_msg = null,573 .error_msg = null,
308 .pass = .{ .nav = nav_index },574 .owner_nav = nav_index.toOptional(),
309 .is_naked_fn = false,575 .is_naked_fn = false,
310 .expected_block = null,576 .expected_block = null,
311 .fwd_decl = undefined,577 .ctype_deps = .empty,
312 .ctype_pool = ctype_pool.*,578 .uavs = rendered_decl.need_uavs.move(),
313 .scratch = .initBuffer(self.scratch_buf),579 };
314 .uavs = .empty,580
315 },581 defer {
316 .code_header = undefined,582 rendered_decl.need_uavs = dg.uavs.move();
317 .code = undefined,583 dg.ctype_deps.deinit(gpa);
318 .indent_counter = 0,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,
319 };670 };
320 object.dg.fwd_decl = .initOwnedSlice(gpa, self.fwd_decl_buf);
321 object.code = .initOwnedSlice(gpa, self.code_buf);
322 defer {671 defer {
323 object.dg.uavs.deinit(gpa);672 rendered_decl.need_uavs = dg.uavs.move();
324 ctype_pool.* = object.dg.ctype_pool.move();673 dg.ctype_deps.deinit(gpa);
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();
330 }674 }
331675
332 codegen.genDecl(&object) catch |err| switch (err) {676 rendered_decl.fwd_decl = fwd_decl: {
333 error.AnalysisFail => switch (zcu.codegenFailMsg(nav_index, object.dg.error_msg.?)) {677 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
334 error.CodegenFail => return,678 defer c.string_bytes = aw.toArrayList();
335 error.OutOfMemory => |e| return e,679 const start = aw.written().len;
336 },680 codegen.genDeclValueFwd(&dg, &aw.writer, .{
337 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,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 };
338 };716 };
339 gop.value_ptr.fwd_decl = try self.addString(object.dg.fwd_decl.written());717
340 gop.value_ptr.code = try self.addString(object.code.written());718 rendered_decl.ctype_deps = try c.addCTypeDependencies(pt, &dg.ctype_deps);
341 try self.addUavsFromCodegen(&object.dg.uavs);
342}719}
343720
344pub fn updateLineNumber(self: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {721pub fn updateLineNumber(c: *C, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) error{}!void {
345 // The C backend does not have the ability to fix line numbers without re-generating722 // The C backend does not currently emit "#line" directives. Even if it did, it would not be
346 // the entire Decl.723 // capable of updating those line numbers without re-generating the entire declaration.
347 _ = self;724 _ = c;
348 _ = pt;725 _ = pt;
349 _ = ti_id;726 _ = ti_id;
350}727}
351728
352fn abiDefines(w: *std.Io.Writer, target: *const std.Target) !void {729pub fn flush(c: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!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
365 const tracy = trace(@src());730 const tracy = trace(@src());
366 defer tracy.end();731 defer tracy.end();
367732
368 const sub_prog_node = prog_node.start("Flush Module", 0);733 const sub_prog_node = prog_node.start("Flush Module", 0);
369 defer sub_prog_node.end();734 defer sub_prog_node.end();
370735
371 const comp = self.base.comp;736 const comp = c.base.comp;
372 const diags = &comp.link_diags;737 const diags = &comp.link_diags;
373 const gpa = comp.gpa;738 const gpa = comp.gpa;
374 const io = comp.io;739 const io = comp.io;
375 const zcu = self.base.comp.zcu.?;740 const zcu = c.base.comp.zcu.?;
376 const ip = &zcu.intern_pool;741 const ip = &zcu.intern_pool;
742 const target = zcu.getTarget();
377 const pt: Zcu.PerThread = .activate(zcu, tid);743 const pt: Zcu.PerThread = .activate(zcu, tid);
378 defer pt.deactivate();744 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.
380 {848 {
381 var i: usize = 0;849 var index: usize = 0;
382 while (i < self.uavs.count()) : (i += 1) {850 while (need_uavs.count() > index) : (index += 1) {
383 try self.updateUav(pt, i);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);
384 }861 }
385 }862 }
386863
387 // This code path happens exclusively with -ofmt=c. The flush logic for864 // Finally, C types may reference other C types.
388 // emit-h is in `flushEmitH` below.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 = .{884 if (errunion_index < need_errunion_types.count()) {
391 .ctype_pool = .empty,885 const payload_pool_index = need_errunion_types.keys()[errunion_index];
392 .ctype_global_from_decl_map = .empty,886 const rendered = &c.types.items[@intFromEnum(payload_pool_index)];
393 .ctypes = .empty,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,898 if (aligned_index < need_aligned_types.count()) {
396 .lazy_fns = .empty,899 const pool_index = need_aligned_types.keys()[aligned_index];
397 .lazy_fwd_decl = .empty,900 const rendered = &c.types.items[@intFromEnum(pool_index)];
398 .lazy_code = .empty,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,912 break;
401 .file_size = 0,913 }
402 };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 };
403 defer f.deinit(gpa);978 defer f.deinit(gpa);
404979
405 var abi_defines_aw: std.Io.Writer.Allocating = .init(gpa);980 // We know exactly what we'll be emitting, so can reserve capacity for all of our buffers!
406 defer abi_defines_aw.deinit();981
407 abiDefines(&abi_defines_aw.writer, zcu.getTarget()) catch |err| switch (err) {982 try f.all_buffers.ensureUnusedCapacity(gpa, 3 + // ABI defines and `#include "zig.h"`
408 error.WriteFailed => return error.OutOfMemory,983 1 + // Big-int type definitions
409 };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.1013 // Big-int type definitions
412 try f.all_buffers.ensureUnusedCapacity(gpa, 5);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());1023 // CType definitions
415 f.appendBufAssumeCapacity(zig_h);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;1043 for (need_types.keys()) |pool_index| {
418 f.all_buffers.items.len += 1;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
420 var asm_aw: std.Io.Writer.Allocating = .init(gpa);1055 var asm_aw: std.Io.Writer.Allocating = .init(gpa);
421 defer asm_aw.deinit();1056 defer asm_aw.deinit();
422 codegen.genGlobalAsm(zcu, &asm_aw.writer) catch |err| switch (err) {1057 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...@@ -424,462 +1059,472 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P
424 };1059 };
425 f.appendBufAssumeCapacity(asm_aw.written());1060 f.appendBufAssumeCapacity(asm_aw.written());
4261061
427 const lazy_index = f.all_buffers.items.len;1062 var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
428 f.all_buffers.items.len += 1;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);1075 // UAV export block
431 try self.flushErrDecls(pt, &f);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.1080 // NAV export block
434 // `CType`s, forward decls, and non-functions first.1081 for (c.exported_navs.values()) |code| {
1082 f.appendBufAssumeCapacity(code.get(c));
1083 }
4351084
436 {1085 // UAV forward declarations
437 var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;1086 for (need_uavs.keys()) |val| {
438 defer export_names.deinit(gpa);1087 if (c.exported_uavs.contains(val)) continue; // the export was the declaration
439 try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count()));1088 const fwd_decl = c.uavs.getPtr(val).?.fwd_decl;
440 for (zcu.single_exports.values()) |export_index| {1089 f.appendBufAssumeCapacity(fwd_decl.get(c));
441 export_names.putAssumeCapacity(export_index.ptr(zcu).opts.name, {});1090 }
442 }1091
443 for (zcu.multi_exports.values()) |info| {1092 // NAV forward declarations
444 try export_names.ensureUnusedCapacity(gpa, info.len);1093 for (need_navs.keys()) |nav| {
445 for (zcu.all_exports.items[info.index..][0..info.len]) |@"export"| {1094 if (c.exported_navs.contains(nav)) continue; // the export was the declaration
446 export_names.putAssumeCapacity(@"export".opts.name, {});1095 if (ip.getNav(nav).getExtern(ip)) |e| {
447 }1096 if (export_names.contains(e.name)) continue;
448 }1097 }
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(1102 // Lazy declarations
451 pt,1103 var lazy_decls_aw: std.Io.Writer.Allocating = .init(gpa);
452 zcu.root_mod,1104 defer lazy_decls_aw.deinit();
453 &f,1105 {
454 av_block,1106 var lazy_dg: codegen.DeclGen = .{
455 self.exported_uavs.getPtr(uav),1107 .gpa = gpa,
456 export_names,1108 .arena = arena,
457 .none,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,
458 );1127 );
4591128 const slice_const_u8_sentinel_0_name = try std.fmt.allocPrint(
460 for (self.navs.keys(), self.navs.values()) |nav, *av_block| try self.flushAvBlock(1129 arena,
461 pt,1130 "{f}",
462 zcu.navFileScope(nav).mod.?,1131 .{slice_const_u8_sentinel_0_cty.fmtTypeName(zcu)},
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,
471 );1132 );
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 }
472 }1168 }
4731169 f.appendBufAssumeCapacity(lazy_decls_aw.written());
474 {1170
475 // We need to flush lazy ctypes after flushing all decls but before flushing any decl ctypes.1171 // UAV definitions
476 // This ensures that every lazy CType.Index exactly matches the global CType.Index.1172 for (need_uavs.keys(), need_uavs.values()) |val, overalign| {
477 try f.ctype_pool.init(gpa);1173 const code = c.uavs.getPtr(val).?.code;
478 try self.flushCTypes(zcu, &f, .flush, &f.lazy_ctype_pool);1174 if (code.len == 0) continue;
4791175 if (!c.exported_uavs.contains(val)) {
480 for (self.uavs.keys(), self.uavs.values()) |uav, av_block| {1176 f.appendBufAssumeCapacity("static ");
481 try self.flushCTypes(zcu, &f, .{ .uav = uav }, &av_block.ctype_pool);
482 }1177 }
4831178 if (overalign != .none) {
484 for (self.navs.keys(), self.navs.values()) |nav, av_block| {1179 // As long as `Alignment` isn't too big, it's reasonable to just generate all possible
485 try self.flushCTypes(zcu, &f, .{ .nav = nav }, &av_block.ctype_pool);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()]);
486 }1194 }
1195 f.appendBufAssumeCapacity(code.get(c));
487 }1196 }
4881197
489 f.all_buffers.items[ctypes_index] = f.ctypes.items;1198 // NAV definitions
490 f.file_size += f.ctypes.items.len;1199 for (need_navs.keys()) |nav| {
4911200 const code = c.navs.getPtr(nav).?.code;
492 f.all_buffers.items[lazy_index] = f.lazy_fwd_decl.items;1201 if (code.len == 0) continue;
493 f.file_size += f.lazy_fwd_decl.items.len;1202 if (!c.exported_navs.contains(nav)) {
4941203 const is_extern = ip.getNav(nav).getExtern(ip) != null;
495 // Now the code.1204 f.appendBufAssumeCapacity(if (is_extern) "zig_extern " else "static ");
496 try f.all_buffers.ensureUnusedCapacity(gpa, 1 + (self.uavs.count() + self.navs.count()) * 2);1205 }
497 f.appendBufAssumeCapacity(f.lazy_code.items);1206 f.appendBufAssumeCapacity(code.get(c));
498 for (self.uavs.keys(), self.uavs.values()) |uav, av_block| f.appendCodeAssumeCapacity(1207 }
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));
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.?;
512 file.setLength(io, f.file_size) catch |err| return diags.fail("failed to allocate file: {t}", .{err});1211 file.setLength(io, f.file_size) catch |err| return diags.fail("failed to allocate file: {t}", .{err});
513 var fw = file.writer(io, &.{});1212 var fw = file.writer(io, &.{});
514 var w = &fw.interface;1213 var w = &fw.interface;
515 w.writeVecAll(f.all_buffers.items) catch |err| switch (err) {1214 w.writeVecAll(f.all_buffers.items) catch |err| switch (err) {
516 error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{1215 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.?),
518 }),1217 }),
519 };1218 };
520}1219}
5211220
522const Flush = struct {1221const 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
532 /// We collect a list of buffers to write, and write them all at once with pwritev 😎1222 /// We collect a list of buffers to write, and write them all at once with pwritev 😎
533 all_buffers: std.ArrayList([]const u8),1223 all_buffers: std.ArrayList([]const u8),
534 /// Keeps track of the total bytes of `all_buffers`.1224 /// Keeps track of the total bytes of `all_buffers`.
535 file_size: u64,1225 file_size: u64,
5361226
537 const LazyFns = std.AutoHashMapUnmanaged(codegen.LazyFnKey, void);
538
539 fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void {1227 fn appendBufAssumeCapacity(f: *Flush, buf: []const u8) void {
540 if (buf.len == 0) return;1228 if (buf.len == 0) return;
541 f.all_buffers.appendAssumeCapacity(buf);1229 f.all_buffers.appendAssumeCapacity(buf);
542 f.file_size += buf.len;1230 f.file_size += buf.len;
543 }1231 }
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
555 fn deinit(f: *Flush, gpa: Allocator) void {1233 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);
564 f.all_buffers.deinit(gpa);1234 f.all_buffers.deinit(gpa);
565 }1235 }
566};1236};
5671237
568const FlushDeclError = error{1238pub fn updateExports(
569 OutOfMemory,1239 c: *C,
570};1240 pt: Zcu.PerThread,
5711241 exported: Zcu.Exported,
572fn flushCTypes(1242 export_indices: []const Zcu.Export.Index,
573 self: *C,1243) Allocator.Error!void {
574 zcu: *Zcu,1244 const zcu = pt.zcu;
575 f: *Flush,1245 const gpa = zcu.gpa;
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}
6301246
631fn flushErrDecls(self: *C, pt: Zcu.PerThread, f: *Flush) FlushDeclError!void {1247 var arena: std.heap.ArenaAllocator = .init(gpa);
632 const gpa = self.base.comp.gpa;1248 defer arena.deinit();
6331249
634 var object: codegen.Object = .{1250 var dg: codegen.DeclGen = .{
635 .dg = .{1251 .gpa = gpa,
636 .gpa = gpa,1252 .arena = arena.allocator(),
637 .pt = pt,1253 .pt = pt,
638 .mod = pt.zcu.root_mod,1254 .mod = zcu.root_mod,
639 .error_msg = null,1255 .owner_nav = .none,
640 .pass = .flush,1256 .is_naked_fn = false,
641 .is_naked_fn = false,1257 .expected_block = null,
642 .expected_block = null,1258 .error_msg = null,
643 .fwd_decl = undefined,1259 .ctype_deps = .empty,
644 .ctype_pool = f.lazy_ctype_pool,1260 .uavs = .empty,
645 .scratch = .initBuffer(self.scratch_buf),
646 .uavs = .empty,
647 },
648 .code_header = undefined,
649 .code = undefined,
650 .indent_counter = 0,
651 };1261 };
652 object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl);
653 object.code = .fromArrayList(gpa, &f.lazy_code);
654 defer {1262 defer {
655 object.dg.uavs.deinit(gpa);1263 assert(dg.uavs.count() == 0);
656 f.lazy_ctype_pool = object.dg.ctype_pool.move();1264 dg.ctype_deps.deinit(gpa);
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();
662 }1265 }
6631266
664 codegen.genErrDecls(&object) catch |err| switch (err) {1267 const code: String = code: {
665 error.AnalysisFail => unreachable,1268 var aw: std.Io.Writer.Allocating = .fromArrayList(gpa, &c.string_bytes);
666 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,1269 defer c.string_bytes = aw.toArrayList();
667 };1270 const start = aw.written().len;
6681271 codegen.genExports(&dg, &aw.writer, exported, export_indices) catch |err| switch (err) {
669 try self.addUavsFromCodegen(&object.dg.uavs);1272 error.WriteFailed => return error.OutOfMemory,
670}1273 error.OutOfMemory => |e| return e,
6711274 };
672fn flushLazyFn(1275 break :code .{
673 self: *C,1276 .start = @intCast(start),
674 pt: Zcu.PerThread,1277 .len = @intCast(aw.written().len - start),
675 mod: *Module,1278 };
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,
699 };1279 };
700 object.dg.fwd_decl = .fromArrayList(gpa, &f.lazy_fwd_decl);1280 switch (exported) {
701 object.code = .fromArrayList(gpa, &f.lazy_code);1281 .nav => |nav| try c.exported_navs.put(gpa, nav, code),
702 defer {1282 .uav => |uav| try c.exported_uavs.put(gpa, uav, code),
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();
712 }1283 }
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 };
718}1284}
7191285
720fn flushLazyFns(1286pub fn deleteExport(
721 self: *C,1287 self: *C,
722 pt: Zcu.PerThread,1288 exported: Zcu.Exported,
723 mod: *Module,1289 _: InternPool.NullTerminatedString,
724 f: *Flush,1290) void {
725 lazy_ctype_pool: *const codegen.CType.Pool,1291 switch (exported) {
726 lazy_fns: codegen.LazyFnMap,1292 .nav => |nav| _ = self.exported_navs.swapRemove(nav),
727) FlushDeclError!void {1293 .uav => |uav| _ = self.exported_uavs.swapRemove(uav),
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);
737 }1294 }
738}1295}
7391296
740fn flushAvBlock(1297fn mergeNeededCTypes(
741 self: *C,1298 c: *C,
742 pt: Zcu.PerThread,1299 need_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void),
743 mod: *Module,1300 need_errunion_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, void),
744 f: *Flush,1301 need_aligned_types: *std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, u64),
745 av_block: *const AvBlock,1302 deps: *const CTypeDependencies,
746 exported_block: ?*const ExportedBlock,1303) Allocator.Error!void {
747 export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void),1304 const gpa = c.base.comp.gpa;
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}
7601305
761pub fn flushEmitH(zcu: *Zcu) !void {1306 const resolved = deps.get(c);
762 const tracy = trace(@src());
763 defer tracy.end();
7641307
765 if (true) return; // emit-h is regressed1308 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;1312 for (resolved.type) |index| need_types.putAssumeCapacity(index, {});
768 const io = zcu.comp.io;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 😎1315 for (resolved.errunion_type) |index| need_errunion_types.putAssumeCapacity(index, {});
771 const num_buffers = emit_h.decl_table.count() + 1;1316 for (resolved.errunion_type_fwd) |index| need_errunion_types.putAssumeCapacity(index, {});
772 var all_buffers = try std.array_list.Managed(std.posix.iovec_const).initCapacity(zcu.gpa, num_buffers);
773 defer all_buffers.deinit();
7741317
775 var file_size: u64 = zig_h.len;1318 for (resolved.aligned_type_fwd, resolved.aligned_type_masks) |ty_index, align_mask| {
776 if (zig_h.len != 0) {1319 const gop = need_aligned_types.getOrPutAssumeCapacity(ty_index);
777 all_buffers.appendAssumeCapacity(.{1320 if (!gop.found_existing) gop.value_ptr.* = 0;
778 .base = zig_h,1321 gop.value_ptr.* |= align_mask;
779 .len = zig_h.len,
780 });
781 }1322 }
1323}
7821324
783 for (emit_h.decl_table.keys()) |decl_index| {1325fn mergeNeededUavs(
784 const decl_emit_h = emit_h.declPtr(decl_index);1326 zcu: *const Zcu,
785 const buf = decl_emit_h.fwd_decl.items;1327 global: *std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
786 if (buf.len != 0) {1328 new: *const std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
787 all_buffers.appendAssumeCapacity(.{1329) Allocator.Error!void {
788 .base = buf.ptr,1330 const gpa = zcu.comp.gpa;
789 .len = buf.len,1331
790 });1332 try global.ensureUnusedCapacity(gpa, new.count());
791 file_size += buf.len;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 }
792 }1345 }
793 }1346 }
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);
805}1347}
8061348
807pub fn updateExports(1349fn addCTypeDependencies(
808 self: *C,1350 c: *C,
809 pt: Zcu.PerThread,1351 pt: Zcu.PerThread,
810 exported: Zcu.Exported,1352 deps: *const codegen.CType.Dependencies,
811 export_indices: []const Zcu.Export.Index,1353) Allocator.Error!CTypeDependencies {
812) !void {1354 const gpa = pt.zcu.comp.gpa;
813 const zcu = pt.zcu;1355
814 const gpa = zcu.gpa;1356 try c.bigint_types.ensureUnusedCapacity(gpa, deps.bigint.count());
815 const mod, const pass: codegen.DeclGen.Pass, const decl_block, const exported_block = switch (exported) {1357 for (deps.bigint.keys()) |bigint| c.bigint_types.putAssumeCapacity(bigint, {});
816 .nav => |nav| .{1358
817 zcu.navFileScope(nav).mod.?,1359 const type_start = c.type_dependencies.items.len;
818 .{ .nav = nav },1360 const errunion_type_start = type_start + deps.type.count();
819 self.navs.getPtr(nav).?,1361 const type_fwd_start = errunion_type_start + deps.errunion_type.count();
820 (try self.exported_navs.getOrPut(gpa, nav)).value_ptr,1362 const errunion_type_fwd_start = type_fwd_start + deps.type_fwd.count();
821 },1363 const aligned_type_fwd_start = errunion_type_fwd_start + deps.errunion_type_fwd.count();
822 .uav => |uav| .{1364 try c.type_dependencies.appendNTimes(gpa, undefined, deps.type.count() +
823 zcu.root_mod,1365 deps.errunion_type.count() +
824 .{ .uav = uav },1366 deps.type_fwd.count() +
825 self.uavs.getPtr(uav).?,1367 deps.errunion_type_fwd.count() +
826 (try self.exported_uavs.getOrPut(gpa, uav)).value_ptr,1368 deps.aligned_type_fwd.count());
827 },1369
828 };1370 const align_mask_start = c.align_dependency_masks.items.len;
829 const ctype_pool = &decl_block.ctype_pool;1371 try c.align_dependency_masks.appendSlice(gpa, deps.aligned_type_fwd.values());
830 var dg: codegen.DeclGen = .{1372
831 .gpa = gpa,1373 for (deps.type.keys(), type_start..) |ty, i| {
832 .pt = pt,1374 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
833 .mod = mod,1375 c.type_dependencies.items[i] = pool_index;
834 .error_msg = null,1376 }
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);
8481377
849 self.fwd_decl_buf = dg.fwd_decl.toArrayList().allocatedSlice();1378 for (deps.errunion_type.keys(), errunion_type_start..) |ty, i| {
850 self.scratch_buf = dg.scratch.allocatedSlice();1379 const pool_index = try c.type_pool.get(pt, .{ .c = c }, ty);
1380 c.type_dependencies.items[i] = pool_index;
851 }1381 }
852 codegen.genExports(&dg, exported, export_indices) catch |err| switch (err) {1382
853 error.WriteFailed, error.OutOfMemory => return error.OutOfMemory,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),
854 };1406 };
855 exported_block.* = .{ .fwd_decl = try self.addString(dg.fwd_decl.written()) };
856}1407}
8571408
858pub fn deleteExport(1409fn updateNewUavs(c: *C, pt: Zcu.PerThread, old_uavs_len: usize) Allocator.Error!void {
859 self: *C,1410 const gpa = pt.zcu.comp.gpa;
860 exported: Zcu.Exported,1411 var index = old_uavs_len;
861 _: InternPool.NullTerminatedString,1412 while (index < c.uavs.count()) : (index += 1) {
862) void {1413 // `new_uavs` is UAVs discovered while lowering *this* UAV.
863 switch (exported) {1414 const new_uavs: []const InternPool.Index = new: {
864 .nav => |nav| _ = self.exported_navs.swapRemove(nav),1415 c.uavs.lockPointers();
865 .uav => |uav| _ = self.exported_uavs.swapRemove(uav),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 }
866 }1430 }
867}1431}
8681432
869fn addUavsFromCodegen(c: *C, uavs: *const std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment)) Allocator.Error!void {1433const FlushTypes = struct {
870 const gpa = c.base.comp.gpa;1434 c: *C,
871 try c.uavs.ensureUnusedCapacity(gpa, uavs.count());1435 f: *Flush,
872 try c.aligned_uavs.ensureUnusedCapacity(gpa, uavs.count());1436
873 for (uavs.keys(), uavs.values()) |uav_val, uav_align| {1437 aligned_types: *const std.AutoArrayHashMapUnmanaged(link.ConstPool.Index, u64),
874 {1438 aligned_type_strings: []const []const u8,
875 const gop = c.uavs.getOrPutAssumeCapacity(uav_val);1439
876 if (!gop.found_existing) gop.value_ptr.* = .{};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]);
877 }1468 }
878 if (uav_align != .none) {1469 ft.aligned_status.putAssumeCapacity(pool_index, {});
879 const gop = c.aligned_uavs.getOrPutAssumeCapacity(uav_val);1470 }
880 gop.value_ptr.* = if (gop.found_existing) max: {1471 fn doTypeFwd(ft: *FlushTypes, pool_index: link.ConstPool.Index) void {
881 break :max gop.value_ptr.*.maxStrict(uav_align);1472 const c = ft.c;
882 } else uav_align;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 }
883 }1485 }
884 }1486 }
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");...@@ -18,7 +18,6 @@ const codegen = @import("../codegen.zig");
18const dev = @import("../dev.zig");18const dev = @import("../dev.zig");
19const link = @import("../link.zig");19const link = @import("../link.zig");
20const target_info = @import("../target.zig");20const target_info = @import("../target.zig");
21const DebugConstPool = link.DebugConstPool;
2221
23gpa: Allocator,22gpa: Allocator,
24bin_file: *link.File,23bin_file: *link.File,
...@@ -26,10 +25,10 @@ format: DW.Format,...@@ -26,10 +25,10 @@ format: DW.Format,
26endian: std.builtin.Endian,25endian: std.builtin.Endian,
27address_size: AddressSize,26address_size: AddressSize,
2827
29const_pool: DebugConstPool,28const_pool: link.ConstPool,
3029
31mods: std.AutoArrayHashMapUnmanaged(*Module, ModInfo),30mods: std.AutoArrayHashMapUnmanaged(*Module, ModInfo),
32/// Indices are `DebugConstPool.Index`.31/// Indices are `link.ConstPool.Index`.
33values: std.ArrayList(struct { Unit.Index, Entry.Index }),32values: std.ArrayList(struct { Unit.Index, Entry.Index }),
34navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Entry.Index),33navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, Entry.Index),
35decls: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, Entry.Index),34decls: std.AutoArrayHashMapUnmanaged(InternPool.TrackedInst.Index, Entry.Index),
...@@ -1038,7 +1037,7 @@ const Entry = struct {...@@ -1038,7 +1037,7 @@ const Entry = struct {
1038 const zcu = dwarf.bin_file.comp.zcu.?;1037 const zcu = dwarf.bin_file.comp.zcu.?;
1039 const ip = &zcu.intern_pool;1038 const ip = &zcu.intern_pool;
1040 for (0.., dwarf.values.items) |raw_index, unit_and_entry| {1039 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);
1042 const val = index.val(&dwarf.const_pool);1041 const val = index.val(&dwarf.const_pool);
1043 const val_unit, const val_entry = unit_and_entry;1042 const val_unit, const val_entry = unit_and_entry;
1044 if (sec.getUnit(val_unit) == unit and unit.getEntry(val_entry) == entry)1043 if (sec.getUnit(val_unit) == unit and unit.getEntry(val_entry) == entry)
...@@ -3291,8 +3290,14 @@ pub fn updateContainerType(...@@ -3291,8 +3290,14 @@ pub fn updateContainerType(
3291) !void {3290) !void {
3292 try dwarf.const_pool.updateContainerType(pt, .{ .dwarf = dwarf }, ty, success);3291 try dwarf.const_pool.updateContainerType(pt, .{ .dwarf = dwarf }, ty, success);
3293}3292}
3294/// Should only be called by the `DebugConstPool` implementation.3293/// Should only be called by the `link.ConstPool` implementation.
3295pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: DebugConstPool.Index, val: InternPool.Index) !void {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 {
3296 const zcu = pt.zcu;3301 const zcu = pt.zcu;
3297 const ip = &zcu.intern_pool;3302 const ip = &zcu.intern_pool;
32983303
...@@ -3321,11 +3326,17 @@ pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: DebugConstPool.Index, v...@@ -3321,11 +3326,17 @@ pub fn addConst(dwarf: *Dwarf, pt: Zcu.PerThread, index: DebugConstPool.Index, v
3321 assert(@intFromEnum(index) == dwarf.values.items.len);3326 assert(@intFromEnum(index) == dwarf.values.items.len);
3322 try dwarf.values.append(dwarf.gpa, .{ unit, entry });3327 try dwarf.values.append(dwarf.gpa, .{ unit, entry });
3323}3328}
3324/// Should only be called by the `DebugConstPool` implementation.3329/// Should only be called by the `link.ConstPool` implementation.
3325///3330///
3326/// Emits a "dummy" DIE for the given comptime-only value (which may be a type). For types, this is3331/// Emits a "dummy" DIE for the given comptime-only value (which may be a type). For types, this is
3327/// an opaque type. Otherwise, it is an undefined value of the value's type.3332/// 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 {
3329 const zcu = pt.zcu;3340 const zcu = pt.zcu;
33303341
3331 const val: Value = .fromInterned(value_index);3342 const val: Value = .fromInterned(value_index);
...@@ -3380,10 +3391,16 @@ pub fn updateConstIncomplete(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index...@@ -3380,10 +3391,16 @@ pub fn updateConstIncomplete(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index
3380 try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written());3391 try dwarf.debug_info.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_info.written());
3381 try dwarf.debug_loclists.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_loclists.written());3392 try dwarf.debug_loclists.section.replaceEntry(unit, entry, dwarf, wip_nav.debug_loclists.written());
3382}3393}
3383/// Should only be called by the `DebugConstPool` implementation.3394/// Should only be called by the `link.ConstPool` implementation.
3384///3395///
3385/// Emits a DIE for the given comptime-only value (which may be a type).3396/// 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 {
3387 const zcu = pt.zcu;3404 const zcu = pt.zcu;
3388 const ip = &zcu.intern_pool;3405 const ip = &zcu.intern_pool;
33893406
src/link/Elf.zig-11
...@@ -1716,19 +1716,8 @@ pub fn updateContainerType(...@@ -1716,19 +1716,8 @@ pub fn updateContainerType(
1716 if (build_options.skip_non_native and builtin.object_format != .elf) {1716 if (build_options.skip_non_native and builtin.object_format != .elf) {
1717 @panic("Attempted to compile for object format that was disabled by build configuration");1717 @panic("Attempted to compile for object format that was disabled by build configuration");
1718 }1718 }
1719 const zcu = pt.zcu;
1720 const gpa = zcu.gpa;
1721 return self.zigObjectPtr().?.updateContainerType(pt, ty, success) catch |err| switch (err) {1719 return self.zigObjectPtr().?.updateContainerType(pt, ty, success) catch |err| switch (err) {
1722 error.OutOfMemory => return error.OutOfMemory,1720 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 },
1732 };1721 };
1733}1722}
17341723