zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 2b2df72a (353557B) - Raw


      1 const std = @import("std");
      2 const builtin = @import("builtin");
      3 const assert = std.debug.assert;
      4 const mem = std.mem;
      5 const log = std.log.scoped(.c);
      6 const Allocator = mem.Allocator;
      7 
      8 const dev = @import("../dev.zig");
      9 const link = @import("../link.zig");
     10 const Zcu = @import("../Zcu.zig");
     11 const Module = @import("../Package/Module.zig");
     12 const Compilation = @import("../Compilation.zig");
     13 const Value = @import("../Value.zig");
     14 const Type = @import("../Type.zig");
     15 const C = link.File.C;
     16 const Decl = Zcu.Decl;
     17 const trace = @import("../tracy.zig").trace;
     18 const Air = @import("../Air.zig");
     19 const InternPool = @import("../InternPool.zig");
     20 const Alignment = InternPool.Alignment;
     21 
     22 const BigIntLimb = std.math.big.Limb;
     23 const BigInt = std.math.big.int;
     24 
     25 pub fn legalizeFeatures(_: *const std.Target) ?*const Air.Legalize.Features {
     26     return comptime switch (dev.env.supports(.legalize)) {
     27         inline false, true => |supports_legalize| &.init(.{
     28             // we don't currently ask zig1 to use safe optimization modes
     29             .expand_intcast_safe = supports_legalize,
     30             .expand_int_from_float_safe = supports_legalize,
     31             .expand_int_from_float_optimized_safe = supports_legalize,
     32             .expand_add_safe = supports_legalize,
     33             .expand_sub_safe = supports_legalize,
     34             .expand_mul_safe = supports_legalize,
     35 
     36             .expand_packed_load = true,
     37             .expand_packed_store = true,
     38             .expand_packed_struct_field_val = true,
     39         }),
     40     };
     41 }
     42 
     43 /// For most backends, MIR is basically a sequence of machine code instructions, perhaps with some
     44 /// "pseudo instructions" thrown in. For the C backend, it is instead the generated C code for a
     45 /// single function. We also need to track some information to get merged into the global `link.C`
     46 /// state, including:
     47 /// * The UAVs used, so declarations can be emitted in `flush`
     48 /// * The types used, so declarations can be emitted in `flush`
     49 /// * The lazy functions used, so definitions can be emitted in `flush`
     50 pub const Mir = struct {
     51     /// This map contains all the UAVs we saw generating this function.
     52     /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields.
     53     /// Key is the value of the UAV; value is the UAV's alignment, or
     54     /// `.none` for natural alignment. The specified alignment is never
     55     /// less than the natural alignment.
     56     uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
     57     // These remaining fields are essentially just an owned version of `link.C.AvBlock`.
     58     code: []u8,
     59     fwd_decl: []u8,
     60     ctype_pool: CType.Pool,
     61     lazy_fns: LazyFnMap,
     62 
     63     pub fn deinit(mir: *Mir, gpa: Allocator) void {
     64         mir.uavs.deinit(gpa);
     65         gpa.free(mir.code);
     66         gpa.free(mir.fwd_decl);
     67         mir.ctype_pool.deinit(gpa);
     68         mir.lazy_fns.deinit(gpa);
     69     }
     70 };
     71 
     72 pub const CType = @import("c/Type.zig");
     73 
     74 pub const CValue = union(enum) {
     75     none: void,
     76     new_local: LocalIndex,
     77     local: LocalIndex,
     78     /// Address of a local.
     79     local_ref: LocalIndex,
     80     /// A constant instruction, to be rendered inline.
     81     constant: Value,
     82     /// Index into the parameters
     83     arg: usize,
     84     /// The array field of a parameter
     85     arg_array: usize,
     86     /// Index into a tuple's fields
     87     field: usize,
     88     /// By-value
     89     nav: InternPool.Nav.Index,
     90     nav_ref: InternPool.Nav.Index,
     91     /// An undefined value (cannot be dereferenced)
     92     undef: Type,
     93     /// Rendered as an identifier (using fmtIdent)
     94     identifier: []const u8,
     95     /// Rendered as "payload." followed by as identifier (using fmtIdent)
     96     payload_identifier: []const u8,
     97     /// Rendered with fmtCTypePoolString
     98     ctype_pool_string: CType.Pool.String,
     99 
    100     fn eql(lhs: CValue, rhs: CValue) bool {
    101         return switch (lhs) {
    102             .none => rhs == .none,
    103             .new_local, .local => |lhs_local| switch (rhs) {
    104                 .new_local, .local => |rhs_local| lhs_local == rhs_local,
    105                 else => false,
    106             },
    107             .local_ref => |lhs_local| switch (rhs) {
    108                 .local_ref => |rhs_local| lhs_local == rhs_local,
    109                 else => false,
    110             },
    111             .constant => |lhs_val| switch (rhs) {
    112                 .constant => |rhs_val| lhs_val.toIntern() == rhs_val.toIntern(),
    113                 else => false,
    114             },
    115             .arg => |lhs_arg_index| switch (rhs) {
    116                 .arg => |rhs_arg_index| lhs_arg_index == rhs_arg_index,
    117                 else => false,
    118             },
    119             .arg_array => |lhs_arg_index| switch (rhs) {
    120                 .arg_array => |rhs_arg_index| lhs_arg_index == rhs_arg_index,
    121                 else => false,
    122             },
    123             .field => |lhs_field_index| switch (rhs) {
    124                 .field => |rhs_field_index| lhs_field_index == rhs_field_index,
    125                 else => false,
    126             },
    127             .nav => |lhs_nav| switch (rhs) {
    128                 .nav => |rhs_nav| lhs_nav == rhs_nav,
    129                 else => false,
    130             },
    131             .nav_ref => |lhs_nav| switch (rhs) {
    132                 .nav_ref => |rhs_nav| lhs_nav == rhs_nav,
    133                 else => false,
    134             },
    135             .undef => |lhs_ty| switch (rhs) {
    136                 .undef => |rhs_ty| lhs_ty.toIntern() == rhs_ty.toIntern(),
    137                 else => false,
    138             },
    139             .identifier => |lhs_id| switch (rhs) {
    140                 .identifier => |rhs_id| std.mem.eql(u8, lhs_id, rhs_id),
    141                 else => false,
    142             },
    143             .payload_identifier => |lhs_id| switch (rhs) {
    144                 .payload_identifier => |rhs_id| std.mem.eql(u8, lhs_id, rhs_id),
    145                 else => false,
    146             },
    147             .ctype_pool_string => |lhs_str| switch (rhs) {
    148                 .ctype_pool_string => |rhs_str| lhs_str.index == rhs_str.index,
    149                 else => false,
    150             },
    151         };
    152     }
    153 };
    154 
    155 const BlockData = struct {
    156     block_id: u32,
    157     result: CValue,
    158 };
    159 
    160 pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue);
    161 
    162 pub const LazyFnKey = union(enum) {
    163     tag_name: InternPool.Index,
    164     never_tail: InternPool.Nav.Index,
    165     never_inline: InternPool.Nav.Index,
    166 };
    167 pub const LazyFnValue = struct {
    168     fn_name: CType.Pool.String,
    169 };
    170 pub const LazyFnMap = std.AutoArrayHashMapUnmanaged(LazyFnKey, LazyFnValue);
    171 
    172 const Local = struct {
    173     ctype: CType,
    174     flags: packed struct(u32) {
    175         alignas: CType.AlignAs,
    176         _: u20 = undefined,
    177     },
    178 
    179     fn getType(local: Local) LocalType {
    180         return .{ .ctype = local.ctype, .alignas = local.flags.alignas };
    181     }
    182 };
    183 
    184 const LocalIndex = u16;
    185 const LocalType = struct { ctype: CType, alignas: CType.AlignAs };
    186 const LocalsList = std.AutoArrayHashMapUnmanaged(LocalIndex, void);
    187 const LocalsMap = std.AutoArrayHashMapUnmanaged(LocalType, LocalsList);
    188 
    189 const ValueRenderLocation = enum {
    190     FunctionArgument,
    191     Initializer,
    192     StaticInitializer,
    193     Other,
    194 
    195     fn isInitializer(loc: ValueRenderLocation) bool {
    196         return switch (loc) {
    197             .Initializer, .StaticInitializer => true,
    198             else => false,
    199         };
    200     }
    201 
    202     fn toCTypeKind(loc: ValueRenderLocation) CType.Kind {
    203         return switch (loc) {
    204             .FunctionArgument => .parameter,
    205             .Initializer, .Other => .complete,
    206             .StaticInitializer => .global,
    207         };
    208     }
    209 };
    210 
    211 const BuiltinInfo = enum { none, bits };
    212 
    213 const reserved_idents = std.StaticStringMap(void).initComptime(.{
    214     // C language
    215     .{ "alignas", {
    216         @setEvalBranchQuota(4000);
    217     } },
    218     .{ "alignof", {} },
    219     .{ "asm", {} },
    220     .{ "atomic_bool", {} },
    221     .{ "atomic_char", {} },
    222     .{ "atomic_char16_t", {} },
    223     .{ "atomic_char32_t", {} },
    224     .{ "atomic_int", {} },
    225     .{ "atomic_int_fast16_t", {} },
    226     .{ "atomic_int_fast32_t", {} },
    227     .{ "atomic_int_fast64_t", {} },
    228     .{ "atomic_int_fast8_t", {} },
    229     .{ "atomic_int_least16_t", {} },
    230     .{ "atomic_int_least32_t", {} },
    231     .{ "atomic_int_least64_t", {} },
    232     .{ "atomic_int_least8_t", {} },
    233     .{ "atomic_intmax_t", {} },
    234     .{ "atomic_intptr_t", {} },
    235     .{ "atomic_llong", {} },
    236     .{ "atomic_long", {} },
    237     .{ "atomic_ptrdiff_t", {} },
    238     .{ "atomic_schar", {} },
    239     .{ "atomic_short", {} },
    240     .{ "atomic_size_t", {} },
    241     .{ "atomic_uchar", {} },
    242     .{ "atomic_uint", {} },
    243     .{ "atomic_uint_fast16_t", {} },
    244     .{ "atomic_uint_fast32_t", {} },
    245     .{ "atomic_uint_fast64_t", {} },
    246     .{ "atomic_uint_fast8_t", {} },
    247     .{ "atomic_uint_least16_t", {} },
    248     .{ "atomic_uint_least32_t", {} },
    249     .{ "atomic_uint_least64_t", {} },
    250     .{ "atomic_uint_least8_t", {} },
    251     .{ "atomic_uintmax_t", {} },
    252     .{ "atomic_uintptr_t", {} },
    253     .{ "atomic_ullong", {} },
    254     .{ "atomic_ulong", {} },
    255     .{ "atomic_ushort", {} },
    256     .{ "atomic_wchar_t", {} },
    257     .{ "auto", {} },
    258     .{ "break", {} },
    259     .{ "case", {} },
    260     .{ "char", {} },
    261     .{ "complex", {} },
    262     .{ "const", {} },
    263     .{ "continue", {} },
    264     .{ "default", {} },
    265     .{ "do", {} },
    266     .{ "double", {} },
    267     .{ "else", {} },
    268     .{ "enum", {} },
    269     .{ "extern", {} },
    270     .{ "float", {} },
    271     .{ "for", {} },
    272     .{ "fortran", {} },
    273     .{ "goto", {} },
    274     .{ "if", {} },
    275     .{ "imaginary", {} },
    276     .{ "inline", {} },
    277     .{ "int", {} },
    278     .{ "int16_t", {} },
    279     .{ "int32_t", {} },
    280     .{ "int64_t", {} },
    281     .{ "int8_t", {} },
    282     .{ "intptr_t", {} },
    283     .{ "long", {} },
    284     .{ "noreturn", {} },
    285     .{ "register", {} },
    286     .{ "restrict", {} },
    287     .{ "return", {} },
    288     .{ "short", {} },
    289     .{ "signed", {} },
    290     .{ "size_t", {} },
    291     .{ "sizeof", {} },
    292     .{ "ssize_t", {} },
    293     .{ "static", {} },
    294     .{ "static_assert", {} },
    295     .{ "struct", {} },
    296     .{ "switch", {} },
    297     .{ "thread_local", {} },
    298     .{ "typedef", {} },
    299     .{ "typeof", {} },
    300     .{ "uint16_t", {} },
    301     .{ "uint32_t", {} },
    302     .{ "uint64_t", {} },
    303     .{ "uint8_t", {} },
    304     .{ "uintptr_t", {} },
    305     .{ "union", {} },
    306     .{ "unsigned", {} },
    307     .{ "void", {} },
    308     .{ "volatile", {} },
    309     .{ "while", {} },
    310 
    311     // stdarg.h
    312     .{ "va_start", {} },
    313     .{ "va_arg", {} },
    314     .{ "va_end", {} },
    315     .{ "va_copy", {} },
    316 
    317     // stdbool.h
    318     .{ "bool", {} },
    319     .{ "false", {} },
    320     .{ "true", {} },
    321 
    322     // stddef.h
    323     .{ "offsetof", {} },
    324 
    325     // windows.h
    326     .{ "max", {} },
    327     .{ "min", {} },
    328 });
    329 
    330 fn isReservedIdent(ident: []const u8) bool {
    331     if (ident.len >= 2 and ident[0] == '_') { // C language
    332         switch (ident[1]) {
    333             'A'...'Z', '_' => return true,
    334             else => return false,
    335         }
    336     } else if (mem.startsWith(u8, ident, "DUMMYSTRUCTNAME") or
    337         mem.startsWith(u8, ident, "DUMMYUNIONNAME"))
    338     { // windows.h
    339         return true;
    340     } else return reserved_idents.has(ident);
    341 }
    342 
    343 fn formatIdentSolo(ident: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
    344     return formatIdentOptions(ident, writer, true);
    345 }
    346 
    347 fn formatIdentUnsolo(ident: []const u8, writer: *std.io.Writer) std.io.Writer.Error!void {
    348     return formatIdentOptions(ident, writer, false);
    349 }
    350 
    351 fn formatIdentOptions(ident: []const u8, writer: *std.io.Writer, solo: bool) std.io.Writer.Error!void {
    352     if (solo and isReservedIdent(ident)) {
    353         try writer.writeAll("zig_e_");
    354     }
    355     for (ident, 0..) |c, i| {
    356         switch (c) {
    357             'a'...'z', 'A'...'Z', '_' => try writer.writeByte(c),
    358             '.' => try writer.writeByte('_'),
    359             '0'...'9' => if (i == 0) {
    360                 try writer.print("_{x:2}", .{c});
    361             } else {
    362                 try writer.writeByte(c);
    363             },
    364             else => try writer.print("_{x:2}", .{c}),
    365         }
    366     }
    367 }
    368 
    369 pub fn fmtIdentSolo(ident: []const u8) std.fmt.Formatter([]const u8, formatIdentSolo) {
    370     return .{ .data = ident };
    371 }
    372 
    373 pub fn fmtIdentUnsolo(ident: []const u8) std.fmt.Formatter([]const u8, formatIdentUnsolo) {
    374     return .{ .data = ident };
    375 }
    376 
    377 const CTypePoolStringFormatData = struct {
    378     ctype_pool_string: CType.Pool.String,
    379     ctype_pool: *const CType.Pool,
    380     solo: bool,
    381 };
    382 fn formatCTypePoolString(data: CTypePoolStringFormatData, writer: *std.io.Writer) std.io.Writer.Error!void {
    383     if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice|
    384         try formatIdentOptions(slice, writer, data.solo)
    385     else
    386         try writer.print("{}", .{data.ctype_pool_string.fmt(data.ctype_pool)});
    387 }
    388 pub fn fmtCTypePoolString(
    389     ctype_pool_string: CType.Pool.String,
    390     ctype_pool: *const CType.Pool,
    391     solo: bool,
    392 ) std.fmt.Formatter(CTypePoolStringFormatData, formatCTypePoolString) {
    393     return .{ .data = .{
    394         .ctype_pool_string = ctype_pool_string,
    395         .ctype_pool = ctype_pool,
    396         .solo = solo,
    397     } };
    398 }
    399 
    400 // Returns true if `formatIdent` would make any edits to ident.
    401 // This must be kept in sync with `formatIdent`.
    402 pub fn isMangledIdent(ident: []const u8, solo: bool) bool {
    403     if (solo and isReservedIdent(ident)) return true;
    404     for (ident, 0..) |c, i| {
    405         switch (c) {
    406             'a'...'z', 'A'...'Z', '_' => {},
    407             '0'...'9' => if (i == 0) return true,
    408             else => return true,
    409         }
    410     }
    411     return false;
    412 }
    413 
    414 /// This data is available when outputting .c code for a `InternPool.Index`
    415 /// that corresponds to `func`.
    416 /// It is not available when generating .h file.
    417 pub const Function = struct {
    418     air: Air,
    419     liveness: Air.Liveness,
    420     value_map: CValueMap,
    421     blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty,
    422     next_arg_index: u32 = 0,
    423     next_block_index: u32 = 0,
    424     object: Object,
    425     lazy_fns: LazyFnMap,
    426     func_index: InternPool.Index,
    427     /// All the locals, to be emitted at the top of the function.
    428     locals: std.ArrayListUnmanaged(Local) = .empty,
    429     /// Which locals are available for reuse, based on Type.
    430     free_locals_map: LocalsMap = .{},
    431     /// Locals which will not be freed by Liveness. This is used after a
    432     /// Function body is lowered in order to make `free_locals_map` have
    433     /// 100% of the locals within so that it can be used to render the block
    434     /// of variable declarations at the top of a function, sorted descending
    435     /// by type alignment.
    436     /// The value is whether the alloc needs to be emitted in the header.
    437     allocs: std.AutoArrayHashMapUnmanaged(LocalIndex, bool) = .empty,
    438     /// Maps from `loop_switch_br` instructions to the allocated local used
    439     /// for the switch cond. Dispatches should set this local to the new cond.
    440     loop_switch_conds: std.AutoHashMapUnmanaged(Air.Inst.Index, LocalIndex) = .empty,
    441 
    442     fn resolveInst(f: *Function, ref: Air.Inst.Ref) !CValue {
    443         const gop = try f.value_map.getOrPut(ref);
    444         if (gop.found_existing) return gop.value_ptr.*;
    445 
    446         const pt = f.object.dg.pt;
    447         const val = (try f.air.value(ref, pt)).?;
    448         const ty = f.typeOf(ref);
    449 
    450         const result: CValue = if (lowersToArray(ty, pt)) result: {
    451             const writer = f.object.codeHeaderWriter();
    452             const decl_c_value = try f.allocLocalValue(.{
    453                 .ctype = try f.ctypeFromType(ty, .complete),
    454                 .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(pt.zcu)),
    455             });
    456             const gpa = f.object.dg.gpa;
    457             try f.allocs.put(gpa, decl_c_value.new_local, false);
    458             try writer.writeAll("static ");
    459             try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, Const, .none, .complete);
    460             try writer.writeAll(" = ");
    461             try f.object.dg.renderValue(writer, val, .StaticInitializer);
    462             try writer.writeAll(";\n ");
    463             break :result .{ .local = decl_c_value.new_local };
    464         } else .{ .constant = val };
    465 
    466         gop.value_ptr.* = result;
    467         return result;
    468     }
    469 
    470     fn wantSafety(f: *Function) bool {
    471         return switch (f.object.dg.pt.zcu.optimizeMode()) {
    472             .Debug, .ReleaseSafe => true,
    473             .ReleaseFast, .ReleaseSmall => false,
    474         };
    475     }
    476 
    477     /// Skips the reuse logic. This function should be used for any persistent allocation, i.e.
    478     /// those which go into `allocs`. This function does not add the resulting local into `allocs`;
    479     /// that responsibility lies with the caller.
    480     fn allocLocalValue(f: *Function, local_type: LocalType) !CValue {
    481         try f.locals.ensureUnusedCapacity(f.object.dg.gpa, 1);
    482         defer f.locals.appendAssumeCapacity(.{
    483             .ctype = local_type.ctype,
    484             .flags = .{ .alignas = local_type.alignas },
    485         });
    486         return .{ .new_local = @intCast(f.locals.items.len) };
    487     }
    488 
    489     fn allocLocal(f: *Function, inst: ?Air.Inst.Index, ty: Type) !CValue {
    490         return f.allocAlignedLocal(inst, .{
    491             .ctype = try f.ctypeFromType(ty, .complete),
    492             .alignas = CType.AlignAs.fromAbiAlignment(ty.abiAlignment(f.object.dg.pt.zcu)),
    493         });
    494     }
    495 
    496     /// Only allocates the local; does not print anything. Will attempt to re-use locals, so should
    497     /// not be used for persistent locals (i.e. those in `allocs`).
    498     fn allocAlignedLocal(f: *Function, inst: ?Air.Inst.Index, local_type: LocalType) !CValue {
    499         const result: CValue = result: {
    500             if (f.free_locals_map.getPtr(local_type)) |locals_list| {
    501                 if (locals_list.pop()) |local_entry| {
    502                     break :result .{ .new_local = local_entry.key };
    503                 }
    504             }
    505             break :result try f.allocLocalValue(local_type);
    506         };
    507         if (inst) |i| {
    508             log.debug("%{d}: allocating t{d}", .{ i, result.new_local });
    509         } else {
    510             log.debug("allocating t{d}", .{result.new_local});
    511         }
    512         return result;
    513     }
    514 
    515     fn writeCValue(f: *Function, w: anytype, c_value: CValue, location: ValueRenderLocation) !void {
    516         switch (c_value) {
    517             .none => unreachable,
    518             .new_local, .local => |i| try w.print("t{d}", .{i}),
    519             .local_ref => |i| try w.print("&t{d}", .{i}),
    520             .constant => |val| try f.object.dg.renderValue(w, val, location),
    521             .arg => |i| try w.print("a{d}", .{i}),
    522             .arg_array => |i| try f.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }),
    523             .undef => |ty| try f.object.dg.renderUndefValue(w, ty, location),
    524             else => try f.object.dg.writeCValue(w, c_value),
    525         }
    526     }
    527 
    528     fn writeCValueDeref(f: *Function, w: anytype, c_value: CValue) !void {
    529         switch (c_value) {
    530             .none => unreachable,
    531             .new_local, .local, .constant => {
    532                 try w.writeAll("(*");
    533                 try f.writeCValue(w, c_value, .Other);
    534                 try w.writeByte(')');
    535             },
    536             .local_ref => |i| try w.print("t{d}", .{i}),
    537             .arg => |i| try w.print("(*a{d})", .{i}),
    538             .arg_array => |i| {
    539                 try w.writeAll("(*");
    540                 try f.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" });
    541                 try w.writeByte(')');
    542             },
    543             else => try f.object.dg.writeCValueDeref(w, c_value),
    544         }
    545     }
    546 
    547     fn writeCValueMember(
    548         f: *Function,
    549         writer: anytype,
    550         c_value: CValue,
    551         member: CValue,
    552     ) error{ OutOfMemory, AnalysisFail }!void {
    553         switch (c_value) {
    554             .new_local, .local, .local_ref, .constant, .arg, .arg_array => {
    555                 try f.writeCValue(writer, c_value, .Other);
    556                 try writer.writeByte('.');
    557                 try f.writeCValue(writer, member, .Other);
    558             },
    559             else => return f.object.dg.writeCValueMember(writer, c_value, member),
    560         }
    561     }
    562 
    563     fn writeCValueDerefMember(f: *Function, writer: anytype, c_value: CValue, member: CValue) !void {
    564         switch (c_value) {
    565             .new_local, .local, .arg, .arg_array => {
    566                 try f.writeCValue(writer, c_value, .Other);
    567                 try writer.writeAll("->");
    568             },
    569             .constant => {
    570                 try writer.writeByte('(');
    571                 try f.writeCValue(writer, c_value, .Other);
    572                 try writer.writeAll(")->");
    573             },
    574             .local_ref => {
    575                 try f.writeCValueDeref(writer, c_value);
    576                 try writer.writeByte('.');
    577             },
    578             else => return f.object.dg.writeCValueDerefMember(writer, c_value, member),
    579         }
    580         try f.writeCValue(writer, member, .Other);
    581     }
    582 
    583     fn fail(f: *Function, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
    584         return f.object.dg.fail(format, args);
    585     }
    586 
    587     fn ctypeFromType(f: *Function, ty: Type, kind: CType.Kind) !CType {
    588         return f.object.dg.ctypeFromType(ty, kind);
    589     }
    590 
    591     fn byteSize(f: *Function, ctype: CType) u64 {
    592         return f.object.dg.byteSize(ctype);
    593     }
    594 
    595     fn renderType(f: *Function, w: anytype, ctype: Type) !void {
    596         return f.object.dg.renderType(w, ctype);
    597     }
    598 
    599     fn renderCType(f: *Function, w: anytype, ctype: CType) !void {
    600         return f.object.dg.renderCType(w, ctype);
    601     }
    602 
    603     fn renderIntCast(f: *Function, w: anytype, dest_ty: Type, src: CValue, v: Vectorize, src_ty: Type, location: ValueRenderLocation) !void {
    604         return f.object.dg.renderIntCast(w, dest_ty, .{ .c_value = .{ .f = f, .value = src, .v = v } }, src_ty, location);
    605     }
    606 
    607     fn fmtIntLiteralDec(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
    608         return f.object.dg.fmtIntLiteralDec(val, .Other);
    609     }
    610 
    611     fn fmtIntLiteralHex(f: *Function, val: Value) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
    612         return f.object.dg.fmtIntLiteralHex(val, .Other);
    613     }
    614 
    615     fn getLazyFnName(f: *Function, key: LazyFnKey) ![]const u8 {
    616         const gpa = f.object.dg.gpa;
    617         const pt = f.object.dg.pt;
    618         const zcu = pt.zcu;
    619         const ip = &zcu.intern_pool;
    620         const ctype_pool = &f.object.dg.ctype_pool;
    621 
    622         const gop = try f.lazy_fns.getOrPut(gpa, key);
    623         if (!gop.found_existing) {
    624             errdefer _ = f.lazy_fns.pop();
    625 
    626             gop.value_ptr.* = .{
    627                 .fn_name = switch (key) {
    628                     .tag_name,
    629                     => |enum_ty| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{
    630                         @tagName(key),
    631                         fmtIdentUnsolo(ip.loadEnumType(enum_ty).name.toSlice(ip)),
    632                         @intFromEnum(enum_ty),
    633                     }),
    634                     .never_tail,
    635                     .never_inline,
    636                     => |owner_nav| try ctype_pool.fmt(gpa, "zig_{s}_{f}__{d}", .{
    637                         @tagName(key),
    638                         fmtIdentUnsolo(ip.getNav(owner_nav).name.toSlice(ip)),
    639                         @intFromEnum(owner_nav),
    640                     }),
    641                 },
    642             };
    643         }
    644         return gop.value_ptr.fn_name.toSlice(ctype_pool).?;
    645     }
    646 
    647     pub fn deinit(f: *Function) void {
    648         const gpa = f.object.dg.gpa;
    649         f.allocs.deinit(gpa);
    650         f.locals.deinit(gpa);
    651         deinitFreeLocalsMap(gpa, &f.free_locals_map);
    652         f.blocks.deinit(gpa);
    653         f.value_map.deinit();
    654         f.lazy_fns.deinit(gpa);
    655         f.loop_switch_conds.deinit(gpa);
    656     }
    657 
    658     fn typeOf(f: *Function, inst: Air.Inst.Ref) Type {
    659         return f.air.typeOf(inst, &f.object.dg.pt.zcu.intern_pool);
    660     }
    661 
    662     fn typeOfIndex(f: *Function, inst: Air.Inst.Index) Type {
    663         return f.air.typeOfIndex(inst, &f.object.dg.pt.zcu.intern_pool);
    664     }
    665 
    666     fn copyCValue(f: *Function, ctype: CType, dst: CValue, src: CValue) !void {
    667         switch (dst) {
    668             .new_local, .local => |dst_local_index| switch (src) {
    669                 .new_local, .local => |src_local_index| if (dst_local_index == src_local_index) return,
    670                 else => {},
    671             },
    672             else => {},
    673         }
    674         const writer = f.object.writer();
    675         const a = try Assignment.start(f, writer, ctype);
    676         try f.writeCValue(writer, dst, .Other);
    677         try a.assign(f, writer);
    678         try f.writeCValue(writer, src, .Other);
    679         try a.end(f, writer);
    680     }
    681 
    682     fn moveCValue(f: *Function, inst: Air.Inst.Index, ty: Type, src: CValue) !CValue {
    683         switch (src) {
    684             // Move the freshly allocated local to be owned by this instruction,
    685             // by returning it here instead of freeing it.
    686             .new_local => return src,
    687             else => {
    688                 try freeCValue(f, inst, src);
    689                 const dst = try f.allocLocal(inst, ty);
    690                 try f.copyCValue(try f.ctypeFromType(ty, .complete), dst, src);
    691                 return dst;
    692             },
    693         }
    694     }
    695 
    696     fn freeCValue(f: *Function, inst: ?Air.Inst.Index, val: CValue) !void {
    697         switch (val) {
    698             .new_local => |local_index| try freeLocal(f, inst, local_index, null),
    699             else => {},
    700         }
    701     }
    702 };
    703 
    704 /// This data is available when outputting .c code for a `Zcu`.
    705 /// It is not available when generating .h file.
    706 pub const Object = struct {
    707     dg: DeclGen,
    708     /// This is a borrowed reference from `link.C`.
    709     code: std.ArrayList(u8),
    710     /// Goes before code. Initialized and deinitialized in `genFunc`.
    711     code_header: std.ArrayList(u8) = undefined,
    712     indent_writer: IndentWriter(std.ArrayList(u8).Writer),
    713 
    714     fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
    715         return o.indent_writer.writer();
    716     }
    717 
    718     fn codeHeaderWriter(o: *Object) ArrayListWriter {
    719         return arrayListWriter(&o.code_header);
    720     }
    721 };
    722 
    723 /// This data is available both when outputting .c code and when outputting an .h file.
    724 pub const DeclGen = struct {
    725     gpa: Allocator,
    726     pt: Zcu.PerThread,
    727     mod: *Module,
    728     pass: Pass,
    729     is_naked_fn: bool,
    730     expected_block: ?u32,
    731     /// This is a borrowed reference from `link.C`.
    732     fwd_decl: std.ArrayList(u8),
    733     error_msg: ?*Zcu.ErrorMsg,
    734     ctype_pool: CType.Pool,
    735     scratch: std.ArrayListUnmanaged(u32),
    736     /// This map contains all the UAVs we saw generating this function.
    737     /// `link.C` will merge them into its `uavs`/`aligned_uavs` fields.
    738     /// Key is the value of the UAV; value is the UAV's alignment, or
    739     /// `.none` for natural alignment. The specified alignment is never
    740     /// less than the natural alignment.
    741     uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment),
    742 
    743     pub const Pass = union(enum) {
    744         nav: InternPool.Nav.Index,
    745         uav: InternPool.Index,
    746         flush,
    747     };
    748 
    749     fn fwdDeclWriter(dg: *DeclGen) ArrayListWriter {
    750         return arrayListWriter(&dg.fwd_decl);
    751     }
    752 
    753     fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
    754         @branchHint(.cold);
    755         const zcu = dg.pt.zcu;
    756         const src_loc = zcu.navSrcLoc(dg.pass.nav);
    757         dg.error_msg = try Zcu.ErrorMsg.create(dg.gpa, src_loc, format, args);
    758         return error.AnalysisFail;
    759     }
    760 
    761     fn renderUav(
    762         dg: *DeclGen,
    763         writer: anytype,
    764         uav: InternPool.Key.Ptr.BaseAddr.Uav,
    765         location: ValueRenderLocation,
    766     ) error{ OutOfMemory, AnalysisFail }!void {
    767         const pt = dg.pt;
    768         const zcu = pt.zcu;
    769         const ip = &zcu.intern_pool;
    770         const ctype_pool = &dg.ctype_pool;
    771         const uav_val = Value.fromInterned(uav.val);
    772         const uav_ty = uav_val.typeOf(zcu);
    773 
    774         // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
    775         const ptr_ty: Type = .fromInterned(uav.orig_ty);
    776         if (ptr_ty.isPtrAtRuntime(zcu) and !uav_ty.isFnOrHasRuntimeBits(zcu)) {
    777             return dg.writeCValue(writer, .{ .undef = ptr_ty });
    778         }
    779 
    780         // Chase function values in order to be able to reference the original function.
    781         switch (ip.indexToKey(uav.val)) {
    782             .variable => unreachable,
    783             .func => |func| return dg.renderNav(writer, func.owner_nav, location),
    784             .@"extern" => |@"extern"| return dg.renderNav(writer, @"extern".owner_nav, location),
    785             else => {},
    786         }
    787 
    788         // We shouldn't cast C function pointers as this is UB (when you call
    789         // them).  The analysis until now should ensure that the C function
    790         // pointers are compatible.  If they are not, then there is a bug
    791         // somewhere and we should let the C compiler tell us about it.
    792         const ptr_ctype = try dg.ctypeFromType(ptr_ty, .complete);
    793         const elem_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;
    794         const uav_ctype = try dg.ctypeFromType(uav_ty, .complete);
    795         const need_cast = !elem_ctype.eql(uav_ctype) and
    796             (elem_ctype.info(ctype_pool) != .function or uav_ctype.info(ctype_pool) != .function);
    797         if (need_cast) {
    798             try writer.writeAll("((");
    799             try dg.renderCType(writer, ptr_ctype);
    800             try writer.writeByte(')');
    801         }
    802         try writer.writeByte('&');
    803         try renderUavName(writer, uav_val);
    804         if (need_cast) try writer.writeByte(')');
    805 
    806         // Indicate that the anon decl should be rendered to the output so that
    807         // our reference above is not undefined.
    808         const ptr_type = ip.indexToKey(uav.orig_ty).ptr_type;
    809         const gop = try dg.uavs.getOrPut(dg.gpa, uav.val);
    810         if (!gop.found_existing) gop.value_ptr.* = .none;
    811         // If there is an explicit alignment, greater than the current one, use it.
    812         // Note that we intentionally start at `.none`, so `gop.value_ptr.*` is never
    813         // underaligned, so we don't need to worry about the `.none` case here.
    814         if (ptr_type.flags.alignment != .none) {
    815             // Resolve the current alignment so we can choose the bigger one.
    816             const cur_alignment: Alignment = if (gop.value_ptr.* == .none) abi: {
    817                 break :abi Type.fromInterned(ptr_type.child).abiAlignment(zcu);
    818             } else gop.value_ptr.*;
    819             gop.value_ptr.* = cur_alignment.maxStrict(ptr_type.flags.alignment);
    820         }
    821     }
    822 
    823     fn renderNav(
    824         dg: *DeclGen,
    825         writer: anytype,
    826         nav_index: InternPool.Nav.Index,
    827         location: ValueRenderLocation,
    828     ) error{ OutOfMemory, AnalysisFail }!void {
    829         _ = location;
    830         const pt = dg.pt;
    831         const zcu = pt.zcu;
    832         const ip = &zcu.intern_pool;
    833         const ctype_pool = &dg.ctype_pool;
    834 
    835         // Chase function values in order to be able to reference the original function.
    836         const owner_nav = switch (ip.getNav(nav_index).status) {
    837             .unresolved => unreachable,
    838             .type_resolved => nav_index, // this can't be an extern or a function
    839             .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
    840                 .func => |f| f.owner_nav,
    841                 .@"extern" => |e| e.owner_nav,
    842                 else => nav_index,
    843             },
    844         };
    845 
    846         // Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
    847         const nav_ty: Type = .fromInterned(ip.getNav(owner_nav).typeOf(ip));
    848         const ptr_ty = try pt.navPtrType(owner_nav);
    849         if (!nav_ty.isFnOrHasRuntimeBits(zcu)) {
    850             return dg.writeCValue(writer, .{ .undef = ptr_ty });
    851         }
    852 
    853         // We shouldn't cast C function pointers as this is UB (when you call
    854         // them).  The analysis until now should ensure that the C function
    855         // pointers are compatible.  If they are not, then there is a bug
    856         // somewhere and we should let the C compiler tell us about it.
    857         const ctype = try dg.ctypeFromType(ptr_ty, .complete);
    858         const elem_ctype = ctype.info(ctype_pool).pointer.elem_ctype;
    859         const nav_ctype = try dg.ctypeFromType(nav_ty, .complete);
    860         const need_cast = !elem_ctype.eql(nav_ctype) and
    861             (elem_ctype.info(ctype_pool) != .function or nav_ctype.info(ctype_pool) != .function);
    862         if (need_cast) {
    863             try writer.writeAll("((");
    864             try dg.renderCType(writer, ctype);
    865             try writer.writeByte(')');
    866         }
    867         try writer.writeByte('&');
    868         try dg.renderNavName(writer, owner_nav);
    869         if (need_cast) try writer.writeByte(')');
    870     }
    871 
    872     fn renderPointer(
    873         dg: *DeclGen,
    874         writer: anytype,
    875         derivation: Value.PointerDeriveStep,
    876         location: ValueRenderLocation,
    877     ) error{ OutOfMemory, AnalysisFail }!void {
    878         const pt = dg.pt;
    879         const zcu = pt.zcu;
    880         switch (derivation) {
    881             .comptime_alloc_ptr, .comptime_field_ptr => unreachable,
    882             .int => |int| {
    883                 const ptr_ctype = try dg.ctypeFromType(int.ptr_ty, .complete);
    884                 const addr_val = try pt.intValue(.usize, int.addr);
    885                 try writer.writeByte('(');
    886                 try dg.renderCType(writer, ptr_ctype);
    887                 try writer.print("){f}", .{try dg.fmtIntLiteralHex(addr_val, .Other)});
    888             },
    889 
    890             .nav_ptr => |nav| try dg.renderNav(writer, nav, location),
    891             .uav_ptr => |uav| try dg.renderUav(writer, uav, location),
    892 
    893             inline .eu_payload_ptr, .opt_payload_ptr => |info| {
    894                 try writer.writeAll("&(");
    895                 try dg.renderPointer(writer, info.parent.*, location);
    896                 try writer.writeAll(")->payload");
    897             },
    898 
    899             .field_ptr => |field| {
    900                 const parent_ptr_ty = try field.parent.ptrType(pt);
    901 
    902                 // Ensure complete type definition is available before accessing fields.
    903                 _ = try dg.ctypeFromType(parent_ptr_ty.childType(zcu), .complete);
    904 
    905                 switch (fieldLocation(parent_ptr_ty, field.result_ptr_ty, field.field_idx, pt)) {
    906                     .begin => {
    907                         const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete);
    908                         try writer.writeByte('(');
    909                         try dg.renderCType(writer, ptr_ctype);
    910                         try writer.writeByte(')');
    911                         try dg.renderPointer(writer, field.parent.*, location);
    912                     },
    913                     .field => |name| {
    914                         try writer.writeAll("&(");
    915                         try dg.renderPointer(writer, field.parent.*, location);
    916                         try writer.writeAll(")->");
    917                         try dg.writeCValue(writer, name);
    918                     },
    919                     .byte_offset => |byte_offset| {
    920                         const ptr_ctype = try dg.ctypeFromType(field.result_ptr_ty, .complete);
    921                         try writer.writeByte('(');
    922                         try dg.renderCType(writer, ptr_ctype);
    923                         try writer.writeByte(')');
    924                         const offset_val = try pt.intValue(.usize, byte_offset);
    925                         try writer.writeAll("((char *)");
    926                         try dg.renderPointer(writer, field.parent.*, location);
    927                         try writer.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)});
    928                     },
    929                 }
    930             },
    931 
    932             .elem_ptr => |elem| if (!(try elem.parent.ptrType(pt)).childType(zcu).hasRuntimeBits(zcu)) {
    933                 // Element type is zero-bit, so lowers to `void`. The index is irrelevant; just cast the pointer.
    934                 const ptr_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete);
    935                 try writer.writeByte('(');
    936                 try dg.renderCType(writer, ptr_ctype);
    937                 try writer.writeByte(')');
    938                 try dg.renderPointer(writer, elem.parent.*, location);
    939             } else {
    940                 const index_val = try pt.intValue(.usize, elem.elem_idx);
    941                 // We want to do pointer arithmetic on a pointer to the element type.
    942                 // We might have a pointer-to-array. In this case, we must cast first.
    943                 const result_ctype = try dg.ctypeFromType(elem.result_ptr_ty, .complete);
    944                 const parent_ctype = try dg.ctypeFromType(try elem.parent.ptrType(pt), .complete);
    945                 if (result_ctype.eql(parent_ctype)) {
    946                     // The pointer already has an appropriate type - just do the arithmetic.
    947                     try writer.writeByte('(');
    948                     try dg.renderPointer(writer, elem.parent.*, location);
    949                     try writer.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)});
    950                 } else {
    951                     // We probably have an array pointer `T (*)[n]`. Cast to an element pointer,
    952                     // and *then* apply the index.
    953                     try writer.writeAll("((");
    954                     try dg.renderCType(writer, result_ctype);
    955                     try writer.writeByte(')');
    956                     try dg.renderPointer(writer, elem.parent.*, location);
    957                     try writer.print(" + {f})", .{try dg.fmtIntLiteralDec(index_val, .Other)});
    958                 }
    959             },
    960 
    961             .offset_and_cast => |oac| {
    962                 const ptr_ctype = try dg.ctypeFromType(oac.new_ptr_ty, .complete);
    963                 try writer.writeByte('(');
    964                 try dg.renderCType(writer, ptr_ctype);
    965                 try writer.writeByte(')');
    966                 if (oac.byte_offset == 0) {
    967                     try dg.renderPointer(writer, oac.parent.*, location);
    968                 } else {
    969                     const offset_val = try pt.intValue(.usize, oac.byte_offset);
    970                     try writer.writeAll("((char *)");
    971                     try dg.renderPointer(writer, oac.parent.*, location);
    972                     try writer.print(" + {f})", .{try dg.fmtIntLiteralDec(offset_val, .Other)});
    973                 }
    974             },
    975         }
    976     }
    977 
    978     fn renderErrorName(dg: *DeclGen, writer: anytype, err_name: InternPool.NullTerminatedString) !void {
    979         const ip = &dg.pt.zcu.intern_pool;
    980         try writer.print("zig_error_{}", .{fmtIdentUnsolo(err_name.toSlice(ip))});
    981     }
    982 
    983     fn renderValue(
    984         dg: *DeclGen,
    985         writer: anytype,
    986         val: Value,
    987         location: ValueRenderLocation,
    988     ) error{ OutOfMemory, AnalysisFail }!void {
    989         const pt = dg.pt;
    990         const zcu = pt.zcu;
    991         const ip = &zcu.intern_pool;
    992         const target = &dg.mod.resolved_target.result;
    993         const ctype_pool = &dg.ctype_pool;
    994 
    995         const initializer_type: ValueRenderLocation = switch (location) {
    996             .StaticInitializer => .StaticInitializer,
    997             else => .Initializer,
    998         };
    999 
   1000         const ty = val.typeOf(zcu);
   1001         if (val.isUndefDeep(zcu)) return dg.renderUndefValue(writer, ty, location);
   1002         const ctype = try dg.ctypeFromType(ty, location.toCTypeKind());
   1003         switch (ip.indexToKey(val.toIntern())) {
   1004             // types, not values
   1005             .int_type,
   1006             .ptr_type,
   1007             .array_type,
   1008             .vector_type,
   1009             .opt_type,
   1010             .anyframe_type,
   1011             .error_union_type,
   1012             .simple_type,
   1013             .struct_type,
   1014             .tuple_type,
   1015             .union_type,
   1016             .opaque_type,
   1017             .enum_type,
   1018             .func_type,
   1019             .error_set_type,
   1020             .inferred_error_set_type,
   1021             // memoization, not values
   1022             .memoized_call,
   1023             => unreachable,
   1024 
   1025             .undef => unreachable, // handled above
   1026             .simple_value => |simple_value| switch (simple_value) {
   1027                 // non-runtime values
   1028                 .undefined => unreachable,
   1029                 .void => unreachable,
   1030                 .null => unreachable,
   1031                 .empty_tuple => unreachable,
   1032                 .@"unreachable" => unreachable,
   1033 
   1034                 .false => try writer.writeAll("false"),
   1035                 .true => try writer.writeAll("true"),
   1036             },
   1037             .variable,
   1038             .@"extern",
   1039             .func,
   1040             .enum_literal,
   1041             .empty_enum_value,
   1042             => unreachable, // non-runtime values
   1043             .int => |int| switch (int.storage) {
   1044                 .u64, .i64, .big_int => try writer.print("{f}", .{try dg.fmtIntLiteralDec(val, location)}),
   1045                 .lazy_align, .lazy_size => {
   1046                     try writer.writeAll("((");
   1047                     try dg.renderCType(writer, ctype);
   1048                     try writer.print("){f})", .{try dg.fmtIntLiteralHex(
   1049                         try pt.intValue(.usize, val.toUnsignedInt(zcu)),
   1050                         .Other,
   1051                     )});
   1052                 },
   1053             },
   1054             .err => |err| try dg.renderErrorName(writer, err.name),
   1055             .error_union => |error_union| switch (ctype.info(ctype_pool)) {
   1056                 .basic => switch (error_union.val) {
   1057                     .err_name => |err_name| try dg.renderErrorName(writer, err_name),
   1058                     .payload => try writer.writeAll("0"),
   1059                 },
   1060                 .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   1061                 .aggregate => |aggregate| {
   1062                     if (!location.isInitializer()) {
   1063                         try writer.writeByte('(');
   1064                         try dg.renderCType(writer, ctype);
   1065                         try writer.writeByte(')');
   1066                     }
   1067                     try writer.writeByte('{');
   1068                     for (0..aggregate.fields.len) |field_index| {
   1069                         if (field_index > 0) try writer.writeByte(',');
   1070                         switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
   1071                             .@"error" => switch (error_union.val) {
   1072                                 .err_name => |err_name| try dg.renderErrorName(writer, err_name),
   1073                                 .payload => try writer.writeByte('0'),
   1074                             },
   1075                             .payload => switch (error_union.val) {
   1076                                 .err_name => try dg.renderUndefValue(
   1077                                     writer,
   1078                                     ty.errorUnionPayload(zcu),
   1079                                     initializer_type,
   1080                                 ),
   1081                                 .payload => |payload| try dg.renderValue(
   1082                                     writer,
   1083                                     Value.fromInterned(payload),
   1084                                     initializer_type,
   1085                                 ),
   1086                             },
   1087                             else => unreachable,
   1088                         }
   1089                     }
   1090                     try writer.writeByte('}');
   1091                 },
   1092             },
   1093             .enum_tag => |enum_tag| try dg.renderValue(writer, Value.fromInterned(enum_tag.int), location),
   1094             .float => {
   1095                 const bits = ty.floatBits(target);
   1096                 const f128_val = val.toFloat(f128, zcu);
   1097 
   1098                 // All unsigned ints matching float types are pre-allocated.
   1099                 const repr_ty = pt.intType(.unsigned, bits) catch unreachable;
   1100 
   1101                 assert(bits <= 128);
   1102                 var repr_val_limbs: [BigInt.calcTwosCompLimbCount(128)]BigIntLimb = undefined;
   1103                 var repr_val_big = BigInt.Mutable{
   1104                     .limbs = &repr_val_limbs,
   1105                     .len = undefined,
   1106                     .positive = undefined,
   1107                 };
   1108 
   1109                 switch (bits) {
   1110                     16 => repr_val_big.set(@as(u16, @bitCast(val.toFloat(f16, zcu)))),
   1111                     32 => repr_val_big.set(@as(u32, @bitCast(val.toFloat(f32, zcu)))),
   1112                     64 => repr_val_big.set(@as(u64, @bitCast(val.toFloat(f64, zcu)))),
   1113                     80 => repr_val_big.set(@as(u80, @bitCast(val.toFloat(f80, zcu)))),
   1114                     128 => repr_val_big.set(@as(u128, @bitCast(f128_val))),
   1115                     else => unreachable,
   1116                 }
   1117 
   1118                 var empty = true;
   1119                 if (std.math.isFinite(f128_val)) {
   1120                     try writer.writeAll("zig_make_");
   1121                     try dg.renderTypeForBuiltinFnName(writer, ty);
   1122                     try writer.writeByte('(');
   1123                     switch (bits) {
   1124                         16 => try writer.print("{x}", .{val.toFloat(f16, zcu)}),
   1125                         32 => try writer.print("{x}", .{val.toFloat(f32, zcu)}),
   1126                         64 => try writer.print("{x}", .{val.toFloat(f64, zcu)}),
   1127                         80 => try writer.print("{x}", .{val.toFloat(f80, zcu)}),
   1128                         128 => try writer.print("{x}", .{f128_val}),
   1129                         else => unreachable,
   1130                     }
   1131                     try writer.writeAll(", ");
   1132                     empty = false;
   1133                 } else {
   1134                     // isSignalNan is equivalent to isNan currently, and MSVC doesn't have nans, so prefer nan
   1135                     const operation = if (std.math.isNan(f128_val))
   1136                         "nan"
   1137                     else if (std.math.isSignalNan(f128_val))
   1138                         "nans"
   1139                     else if (std.math.isInf(f128_val))
   1140                         "inf"
   1141                     else
   1142                         unreachable;
   1143 
   1144                     if (location == .StaticInitializer) {
   1145                         if (!std.math.isNan(f128_val) and std.math.isSignalNan(f128_val))
   1146                             return dg.fail("TODO: C backend: implement nans rendering in static initializers", .{});
   1147 
   1148                         // MSVC doesn't have a way to define a custom or signaling NaN value in a constant expression
   1149 
   1150                         // TODO: Re-enable this check, otherwise we're writing qnan bit patterns on msvc incorrectly
   1151                         // if (std.math.isNan(f128_val) and f128_val != std.math.nan(f128))
   1152                         //     return dg.fail("Only quiet nans are supported in global variable initializers", .{});
   1153                     }
   1154 
   1155                     try writer.writeAll("zig_");
   1156                     try writer.writeAll(if (location == .StaticInitializer) "init" else "make");
   1157                     try writer.writeAll("_special_");
   1158                     try dg.renderTypeForBuiltinFnName(writer, ty);
   1159                     try writer.writeByte('(');
   1160                     if (std.math.signbit(f128_val)) try writer.writeByte('-');
   1161                     try writer.writeAll(", ");
   1162                     try writer.writeAll(operation);
   1163                     try writer.writeAll(", ");
   1164                     if (std.math.isNan(f128_val)) switch (bits) {
   1165                         // We only actually need to pass the significand, but it will get
   1166                         // properly masked anyway, so just pass the whole value.
   1167                         16 => try writer.print("\"0x{x}\"", .{@as(u16, @bitCast(val.toFloat(f16, zcu)))}),
   1168                         32 => try writer.print("\"0x{x}\"", .{@as(u32, @bitCast(val.toFloat(f32, zcu)))}),
   1169                         64 => try writer.print("\"0x{x}\"", .{@as(u64, @bitCast(val.toFloat(f64, zcu)))}),
   1170                         80 => try writer.print("\"0x{x}\"", .{@as(u80, @bitCast(val.toFloat(f80, zcu)))}),
   1171                         128 => try writer.print("\"0x{x}\"", .{@as(u128, @bitCast(f128_val))}),
   1172                         else => unreachable,
   1173                     };
   1174                     try writer.writeAll(", ");
   1175                     empty = false;
   1176                 }
   1177                 try writer.print("{f}", .{try dg.fmtIntLiteralHex(
   1178                     try pt.intValue_big(repr_ty, repr_val_big.toConst()),
   1179                     location,
   1180                 )});
   1181                 if (!empty) try writer.writeByte(')');
   1182             },
   1183             .slice => |slice| {
   1184                 const aggregate = ctype.info(ctype_pool).aggregate;
   1185                 if (!location.isInitializer()) {
   1186                     try writer.writeByte('(');
   1187                     try dg.renderCType(writer, ctype);
   1188                     try writer.writeByte(')');
   1189                 }
   1190                 try writer.writeByte('{');
   1191                 for (0..aggregate.fields.len) |field_index| {
   1192                     if (field_index > 0) try writer.writeByte(',');
   1193                     try dg.renderValue(writer, Value.fromInterned(
   1194                         switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
   1195                             .ptr => slice.ptr,
   1196                             .len => slice.len,
   1197                             else => unreachable,
   1198                         },
   1199                     ), initializer_type);
   1200                 }
   1201                 try writer.writeByte('}');
   1202             },
   1203             .ptr => {
   1204                 var arena = std.heap.ArenaAllocator.init(zcu.gpa);
   1205                 defer arena.deinit();
   1206                 const derivation = try val.pointerDerivation(arena.allocator(), pt);
   1207                 try dg.renderPointer(writer, derivation, location);
   1208             },
   1209             .opt => |opt| switch (ctype.info(ctype_pool)) {
   1210                 .basic => if (ctype.isBool()) try writer.writeAll(switch (opt.val) {
   1211                     .none => "true",
   1212                     else => "false",
   1213                 }) else switch (opt.val) {
   1214                     .none => try writer.writeAll("0"),
   1215                     else => |payload| switch (ip.indexToKey(payload)) {
   1216                         .undef => |err_ty| try dg.renderUndefValue(
   1217                             writer,
   1218                             .fromInterned(err_ty),
   1219                             location,
   1220                         ),
   1221                         .err => |err| try dg.renderErrorName(writer, err.name),
   1222                         else => unreachable,
   1223                     },
   1224                 },
   1225                 .pointer => switch (opt.val) {
   1226                     .none => try writer.writeAll("NULL"),
   1227                     else => |payload| try dg.renderValue(writer, Value.fromInterned(payload), location),
   1228                 },
   1229                 .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   1230                 .aggregate => |aggregate| {
   1231                     switch (opt.val) {
   1232                         .none => {},
   1233                         else => |payload| switch (aggregate.fields.at(0, ctype_pool).name.index) {
   1234                             .is_null, .payload => {},
   1235                             .ptr, .len => return dg.renderValue(
   1236                                 writer,
   1237                                 Value.fromInterned(payload),
   1238                                 location,
   1239                             ),
   1240                             else => unreachable,
   1241                         },
   1242                     }
   1243                     if (!location.isInitializer()) {
   1244                         try writer.writeByte('(');
   1245                         try dg.renderCType(writer, ctype);
   1246                         try writer.writeByte(')');
   1247                     }
   1248                     try writer.writeByte('{');
   1249                     for (0..aggregate.fields.len) |field_index| {
   1250                         if (field_index > 0) try writer.writeByte(',');
   1251                         switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
   1252                             .is_null => try writer.writeAll(switch (opt.val) {
   1253                                 .none => "true",
   1254                                 else => "false",
   1255                             }),
   1256                             .payload => switch (opt.val) {
   1257                                 .none => try dg.renderUndefValue(
   1258                                     writer,
   1259                                     ty.optionalChild(zcu),
   1260                                     initializer_type,
   1261                                 ),
   1262                                 else => |payload| try dg.renderValue(
   1263                                     writer,
   1264                                     Value.fromInterned(payload),
   1265                                     initializer_type,
   1266                                 ),
   1267                             },
   1268                             .ptr => try writer.writeAll("NULL"),
   1269                             .len => try dg.renderUndefValue(writer, .usize, initializer_type),
   1270                             else => unreachable,
   1271                         }
   1272                     }
   1273                     try writer.writeByte('}');
   1274                 },
   1275             },
   1276             .aggregate => switch (ip.indexToKey(ty.toIntern())) {
   1277                 .array_type, .vector_type => {
   1278                     if (location == .FunctionArgument) {
   1279                         try writer.writeByte('(');
   1280                         try dg.renderCType(writer, ctype);
   1281                         try writer.writeByte(')');
   1282                     }
   1283                     const ai = ty.arrayInfo(zcu);
   1284                     if (ai.elem_type.eql(.u8, zcu)) {
   1285                         var literal: StringLiteral = .init(writer, ty.arrayLenIncludingSentinel(zcu));
   1286                         try literal.start();
   1287                         var index: usize = 0;
   1288                         while (index < ai.len) : (index += 1) {
   1289                             const elem_val = try val.elemValue(pt, index);
   1290                             const elem_val_u8: u8 = if (elem_val.isUndef(zcu))
   1291                                 undefPattern(u8)
   1292                             else
   1293                                 @intCast(elem_val.toUnsignedInt(zcu));
   1294                             try literal.writeChar(elem_val_u8);
   1295                         }
   1296                         if (ai.sentinel) |s| {
   1297                             const s_u8: u8 = @intCast(s.toUnsignedInt(zcu));
   1298                             if (s_u8 != 0) try literal.writeChar(s_u8);
   1299                         }
   1300                         try literal.end();
   1301                     } else {
   1302                         try writer.writeByte('{');
   1303                         var index: usize = 0;
   1304                         while (index < ai.len) : (index += 1) {
   1305                             if (index != 0) try writer.writeByte(',');
   1306                             const elem_val = try val.elemValue(pt, index);
   1307                             try dg.renderValue(writer, elem_val, initializer_type);
   1308                         }
   1309                         if (ai.sentinel) |s| {
   1310                             if (index != 0) try writer.writeByte(',');
   1311                             try dg.renderValue(writer, s, initializer_type);
   1312                         }
   1313                         try writer.writeByte('}');
   1314                     }
   1315                 },
   1316                 .tuple_type => |tuple| {
   1317                     if (!location.isInitializer()) {
   1318                         try writer.writeByte('(');
   1319                         try dg.renderCType(writer, ctype);
   1320                         try writer.writeByte(')');
   1321                     }
   1322 
   1323                     try writer.writeByte('{');
   1324                     var empty = true;
   1325                     for (0..tuple.types.len) |field_index| {
   1326                         const comptime_val = tuple.values.get(ip)[field_index];
   1327                         if (comptime_val != .none) continue;
   1328                         const field_ty: Type = .fromInterned(tuple.types.get(ip)[field_index]);
   1329                         if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1330 
   1331                         if (!empty) try writer.writeByte(',');
   1332 
   1333                         const field_val = Value.fromInterned(
   1334                             switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
   1335                                 .bytes => |bytes| try pt.intern(.{ .int = .{
   1336                                     .ty = field_ty.toIntern(),
   1337                                     .storage = .{ .u64 = bytes.at(field_index, ip) },
   1338                                 } }),
   1339                                 .elems => |elems| elems[field_index],
   1340                                 .repeated_elem => |elem| elem,
   1341                             },
   1342                         );
   1343                         try dg.renderValue(writer, field_val, initializer_type);
   1344 
   1345                         empty = false;
   1346                     }
   1347                     try writer.writeByte('}');
   1348                 },
   1349                 .struct_type => {
   1350                     const loaded_struct = ip.loadStructType(ty.toIntern());
   1351                     switch (loaded_struct.layout) {
   1352                         .auto, .@"extern" => {
   1353                             if (!location.isInitializer()) {
   1354                                 try writer.writeByte('(');
   1355                                 try dg.renderCType(writer, ctype);
   1356                                 try writer.writeByte(')');
   1357                             }
   1358 
   1359                             try writer.writeByte('{');
   1360                             var field_it = loaded_struct.iterateRuntimeOrder(ip);
   1361                             var need_comma = false;
   1362                             while (field_it.next()) |field_index| {
   1363                                 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
   1364                                 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1365 
   1366                                 if (need_comma) try writer.writeByte(',');
   1367                                 need_comma = true;
   1368                                 const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
   1369                                     .bytes => |bytes| try pt.intern(.{ .int = .{
   1370                                         .ty = field_ty.toIntern(),
   1371                                         .storage = .{ .u64 = bytes.at(field_index, ip) },
   1372                                     } }),
   1373                                     .elems => |elems| elems[field_index],
   1374                                     .repeated_elem => |elem| elem,
   1375                                 };
   1376                                 try dg.renderValue(writer, Value.fromInterned(field_val), initializer_type);
   1377                             }
   1378                             try writer.writeByte('}');
   1379                         },
   1380                         .@"packed" => {
   1381                             const int_info = ty.intInfo(zcu);
   1382 
   1383                             const bits = Type.smallestUnsignedBits(int_info.bits - 1);
   1384                             const bit_offset_ty = try pt.intType(.unsigned, bits);
   1385 
   1386                             var bit_offset: u64 = 0;
   1387                             var eff_num_fields: usize = 0;
   1388 
   1389                             for (0..loaded_struct.field_types.len) |field_index| {
   1390                                 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
   1391                                 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1392                                 eff_num_fields += 1;
   1393                             }
   1394 
   1395                             if (eff_num_fields == 0) {
   1396                                 try writer.writeByte('(');
   1397                                 try dg.renderUndefValue(writer, ty, location);
   1398                                 try writer.writeByte(')');
   1399                             } else if (ty.bitSize(zcu) > 64) {
   1400                                 // zig_or_u128(zig_or_u128(zig_shl_u128(a, a_off), zig_shl_u128(b, b_off)), zig_shl_u128(c, c_off))
   1401                                 var num_or = eff_num_fields - 1;
   1402                                 while (num_or > 0) : (num_or -= 1) {
   1403                                     try writer.writeAll("zig_or_");
   1404                                     try dg.renderTypeForBuiltinFnName(writer, ty);
   1405                                     try writer.writeByte('(');
   1406                                 }
   1407 
   1408                                 var eff_index: usize = 0;
   1409                                 var needs_closing_paren = false;
   1410                                 for (0..loaded_struct.field_types.len) |field_index| {
   1411                                     const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
   1412                                     if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1413 
   1414                                     const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
   1415                                         .bytes => |bytes| try pt.intern(.{ .int = .{
   1416                                             .ty = field_ty.toIntern(),
   1417                                             .storage = .{ .u64 = bytes.at(field_index, ip) },
   1418                                         } }),
   1419                                         .elems => |elems| elems[field_index],
   1420                                         .repeated_elem => |elem| elem,
   1421                                     };
   1422                                     const cast_context = IntCastContext{ .value = .{ .value = Value.fromInterned(field_val) } };
   1423                                     if (bit_offset != 0) {
   1424                                         try writer.writeAll("zig_shl_");
   1425                                         try dg.renderTypeForBuiltinFnName(writer, ty);
   1426                                         try writer.writeByte('(');
   1427                                         try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument);
   1428                                         try writer.writeAll(", ");
   1429                                         try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument);
   1430                                         try writer.writeByte(')');
   1431                                     } else {
   1432                                         try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument);
   1433                                     }
   1434 
   1435                                     if (needs_closing_paren) try writer.writeByte(')');
   1436                                     if (eff_index != eff_num_fields - 1) try writer.writeAll(", ");
   1437 
   1438                                     bit_offset += field_ty.bitSize(zcu);
   1439                                     needs_closing_paren = true;
   1440                                     eff_index += 1;
   1441                                 }
   1442                             } else {
   1443                                 try writer.writeByte('(');
   1444                                 // a << a_off | b << b_off | c << c_off
   1445                                 var empty = true;
   1446                                 for (0..loaded_struct.field_types.len) |field_index| {
   1447                                     const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
   1448                                     if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1449 
   1450                                     if (!empty) try writer.writeAll(" | ");
   1451                                     try writer.writeByte('(');
   1452                                     try dg.renderCType(writer, ctype);
   1453                                     try writer.writeByte(')');
   1454 
   1455                                     const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
   1456                                         .bytes => |bytes| try pt.intern(.{ .int = .{
   1457                                             .ty = field_ty.toIntern(),
   1458                                             .storage = .{ .u64 = bytes.at(field_index, ip) },
   1459                                         } }),
   1460                                         .elems => |elems| elems[field_index],
   1461                                         .repeated_elem => |elem| elem,
   1462                                     };
   1463 
   1464                                     const field_int_info: std.builtin.Type.Int = if (field_ty.isAbiInt(zcu))
   1465                                         field_ty.intInfo(zcu)
   1466                                     else
   1467                                         .{ .signedness = .unsigned, .bits = undefined };
   1468                                     switch (field_int_info.signedness) {
   1469                                         .signed => {
   1470                                             try writer.writeByte('(');
   1471                                             try dg.renderValue(writer, Value.fromInterned(field_val), .Other);
   1472                                             try writer.writeAll(" & ");
   1473                                             const field_uint_ty = try pt.intType(.unsigned, field_int_info.bits);
   1474                                             try dg.renderValue(writer, try field_uint_ty.maxIntScalar(pt, field_uint_ty), .Other);
   1475                                             try writer.writeByte(')');
   1476                                         },
   1477                                         .unsigned => try dg.renderValue(writer, Value.fromInterned(field_val), .Other),
   1478                                     }
   1479                                     if (bit_offset != 0) {
   1480                                         try writer.writeAll(" << ");
   1481                                         try dg.renderValue(writer, try pt.intValue(bit_offset_ty, bit_offset), .FunctionArgument);
   1482                                     }
   1483 
   1484                                     bit_offset += field_ty.bitSize(zcu);
   1485                                     empty = false;
   1486                                 }
   1487                                 try writer.writeByte(')');
   1488                             }
   1489                         },
   1490                     }
   1491                 },
   1492                 else => unreachable,
   1493             },
   1494             .un => |un| {
   1495                 const loaded_union = ip.loadUnionType(ty.toIntern());
   1496                 if (un.tag == .none) {
   1497                     const backing_ty = try ty.unionBackingType(pt);
   1498                     switch (loaded_union.flagsUnordered(ip).layout) {
   1499                         .@"packed" => {
   1500                             if (!location.isInitializer()) {
   1501                                 try writer.writeByte('(');
   1502                                 try dg.renderType(writer, backing_ty);
   1503                                 try writer.writeByte(')');
   1504                             }
   1505                             try dg.renderValue(writer, Value.fromInterned(un.val), location);
   1506                         },
   1507                         .@"extern" => {
   1508                             if (location == .StaticInitializer) {
   1509                                 return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});
   1510                             }
   1511 
   1512                             const ptr_ty = try pt.singleConstPtrType(ty);
   1513                             try writer.writeAll("*((");
   1514                             try dg.renderType(writer, ptr_ty);
   1515                             try writer.writeAll(")(");
   1516                             try dg.renderType(writer, backing_ty);
   1517                             try writer.writeAll("){");
   1518                             try dg.renderValue(writer, Value.fromInterned(un.val), location);
   1519                             try writer.writeAll("})");
   1520                         },
   1521                         else => unreachable,
   1522                     }
   1523                 } else {
   1524                     if (!location.isInitializer()) {
   1525                         try writer.writeByte('(');
   1526                         try dg.renderCType(writer, ctype);
   1527                         try writer.writeByte(')');
   1528                     }
   1529 
   1530                     const field_index = zcu.unionTagFieldIndex(loaded_union, Value.fromInterned(un.tag)).?;
   1531                     const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
   1532                     const field_name = loaded_union.loadTagType(ip).names.get(ip)[field_index];
   1533                     if (loaded_union.flagsUnordered(ip).layout == .@"packed") {
   1534                         if (field_ty.hasRuntimeBits(zcu)) {
   1535                             if (field_ty.isPtrAtRuntime(zcu)) {
   1536                                 try writer.writeByte('(');
   1537                                 try dg.renderCType(writer, ctype);
   1538                                 try writer.writeByte(')');
   1539                             } else if (field_ty.zigTypeTag(zcu) == .float) {
   1540                                 try writer.writeByte('(');
   1541                                 try dg.renderCType(writer, ctype);
   1542                                 try writer.writeByte(')');
   1543                             }
   1544                             try dg.renderValue(writer, Value.fromInterned(un.val), location);
   1545                         } else try writer.writeAll("0");
   1546                         return;
   1547                     }
   1548 
   1549                     const has_tag = loaded_union.hasTag(ip);
   1550                     if (has_tag) try writer.writeByte('{');
   1551                     const aggregate = ctype.info(ctype_pool).aggregate;
   1552                     for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| {
   1553                         if (outer_field_index > 0) try writer.writeByte(',');
   1554                         switch (if (has_tag)
   1555                             aggregate.fields.at(outer_field_index, ctype_pool).name.index
   1556                         else
   1557                             .payload) {
   1558                             .tag => try dg.renderValue(
   1559                                 writer,
   1560                                 Value.fromInterned(un.tag),
   1561                                 initializer_type,
   1562                             ),
   1563                             .payload => {
   1564                                 try writer.writeByte('{');
   1565                                 if (field_ty.hasRuntimeBits(zcu)) {
   1566                                     try writer.print(" .{f} = ", .{fmtIdentSolo(field_name.toSlice(ip))});
   1567                                     try dg.renderValue(
   1568                                         writer,
   1569                                         Value.fromInterned(un.val),
   1570                                         initializer_type,
   1571                                     );
   1572                                     try writer.writeByte(' ');
   1573                                 } else for (0..loaded_union.field_types.len) |inner_field_index| {
   1574                                     const inner_field_ty: Type = .fromInterned(
   1575                                         loaded_union.field_types.get(ip)[inner_field_index],
   1576                                     );
   1577                                     if (!inner_field_ty.hasRuntimeBits(zcu)) continue;
   1578                                     try dg.renderUndefValue(writer, inner_field_ty, initializer_type);
   1579                                     break;
   1580                                 }
   1581                                 try writer.writeByte('}');
   1582                             },
   1583                             else => unreachable,
   1584                         }
   1585                     }
   1586                     if (has_tag) try writer.writeByte('}');
   1587                 }
   1588             },
   1589         }
   1590     }
   1591 
   1592     fn renderUndefValue(
   1593         dg: *DeclGen,
   1594         writer: anytype,
   1595         ty: Type,
   1596         location: ValueRenderLocation,
   1597     ) error{ OutOfMemory, AnalysisFail }!void {
   1598         const pt = dg.pt;
   1599         const zcu = pt.zcu;
   1600         const ip = &zcu.intern_pool;
   1601         const target = &dg.mod.resolved_target.result;
   1602         const ctype_pool = &dg.ctype_pool;
   1603 
   1604         const initializer_type: ValueRenderLocation = switch (location) {
   1605             .StaticInitializer => .StaticInitializer,
   1606             else => .Initializer,
   1607         };
   1608 
   1609         const safety_on = switch (zcu.optimizeMode()) {
   1610             .Debug, .ReleaseSafe => true,
   1611             .ReleaseFast, .ReleaseSmall => false,
   1612         };
   1613 
   1614         const ctype = try dg.ctypeFromType(ty, location.toCTypeKind());
   1615         switch (ty.toIntern()) {
   1616             .c_longdouble_type,
   1617             .f16_type,
   1618             .f32_type,
   1619             .f64_type,
   1620             .f80_type,
   1621             .f128_type,
   1622             => {
   1623                 const bits = ty.floatBits(target);
   1624                 // All unsigned ints matching float types are pre-allocated.
   1625                 const repr_ty = dg.pt.intType(.unsigned, bits) catch unreachable;
   1626 
   1627                 try writer.writeAll("zig_make_");
   1628                 try dg.renderTypeForBuiltinFnName(writer, ty);
   1629                 try writer.writeByte('(');
   1630                 switch (bits) {
   1631                     16 => try writer.print("{x}", .{@as(f16, @bitCast(undefPattern(i16)))}),
   1632                     32 => try writer.print("{x}", .{@as(f32, @bitCast(undefPattern(i32)))}),
   1633                     64 => try writer.print("{x}", .{@as(f64, @bitCast(undefPattern(i64)))}),
   1634                     80 => try writer.print("{x}", .{@as(f80, @bitCast(undefPattern(i80)))}),
   1635                     128 => try writer.print("{x}", .{@as(f128, @bitCast(undefPattern(i128)))}),
   1636                     else => unreachable,
   1637                 }
   1638                 try writer.writeAll(", ");
   1639                 try dg.renderUndefValue(writer, repr_ty, .FunctionArgument);
   1640                 return writer.writeByte(')');
   1641             },
   1642             .bool_type => try writer.writeAll(if (safety_on) "0xaa" else "false"),
   1643             else => switch (ip.indexToKey(ty.toIntern())) {
   1644                 .simple_type,
   1645                 .int_type,
   1646                 .enum_type,
   1647                 .error_set_type,
   1648                 .inferred_error_set_type,
   1649                 => return writer.print("{f}", .{
   1650                     try dg.fmtIntLiteralHex(try pt.undefValue(ty), location),
   1651                 }),
   1652                 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
   1653                     .one, .many, .c => {
   1654                         try writer.writeAll("((");
   1655                         try dg.renderCType(writer, ctype);
   1656                         return writer.print("){f})", .{
   1657                             try dg.fmtIntLiteralHex(.undef_usize, .Other),
   1658                         });
   1659                     },
   1660                     .slice => {
   1661                         if (!location.isInitializer()) {
   1662                             try writer.writeByte('(');
   1663                             try dg.renderCType(writer, ctype);
   1664                             try writer.writeByte(')');
   1665                         }
   1666 
   1667                         try writer.writeAll("{(");
   1668                         const ptr_ty = ty.slicePtrFieldType(zcu);
   1669                         try dg.renderType(writer, ptr_ty);
   1670                         return writer.print("){f}, {0fx}}}", .{
   1671                             try dg.fmtIntLiteralHex(.undef_usize, .Other),
   1672                         });
   1673                     },
   1674                 },
   1675                 .opt_type => |child_type| switch (ctype.info(ctype_pool)) {
   1676                     .basic, .pointer => try dg.renderUndefValue(
   1677                         writer,
   1678                         .fromInterned(if (ctype.isBool()) .bool_type else child_type),
   1679                         location,
   1680                     ),
   1681                     .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   1682                     .aggregate => |aggregate| {
   1683                         switch (aggregate.fields.at(0, ctype_pool).name.index) {
   1684                             .is_null, .payload => {},
   1685                             .ptr, .len => return dg.renderUndefValue(
   1686                                 writer,
   1687                                 .fromInterned(child_type),
   1688                                 location,
   1689                             ),
   1690                             else => unreachable,
   1691                         }
   1692                         if (!location.isInitializer()) {
   1693                             try writer.writeByte('(');
   1694                             try dg.renderCType(writer, ctype);
   1695                             try writer.writeByte(')');
   1696                         }
   1697                         try writer.writeByte('{');
   1698                         for (0..aggregate.fields.len) |field_index| {
   1699                             if (field_index > 0) try writer.writeByte(',');
   1700                             try dg.renderUndefValue(writer, .fromInterned(
   1701                                 switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
   1702                                     .is_null => .bool_type,
   1703                                     .payload => child_type,
   1704                                     else => unreachable,
   1705                                 },
   1706                             ), initializer_type);
   1707                         }
   1708                         try writer.writeByte('}');
   1709                     },
   1710                 },
   1711                 .struct_type => {
   1712                     const loaded_struct = ip.loadStructType(ty.toIntern());
   1713                     switch (loaded_struct.layout) {
   1714                         .auto, .@"extern" => {
   1715                             if (!location.isInitializer()) {
   1716                                 try writer.writeByte('(');
   1717                                 try dg.renderCType(writer, ctype);
   1718                                 try writer.writeByte(')');
   1719                             }
   1720 
   1721                             try writer.writeByte('{');
   1722                             var field_it = loaded_struct.iterateRuntimeOrder(ip);
   1723                             var need_comma = false;
   1724                             while (field_it.next()) |field_index| {
   1725                                 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
   1726                                 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1727 
   1728                                 if (need_comma) try writer.writeByte(',');
   1729                                 need_comma = true;
   1730                                 try dg.renderUndefValue(writer, field_ty, initializer_type);
   1731                             }
   1732                             return writer.writeByte('}');
   1733                         },
   1734                         .@"packed" => return writer.print("{f}", .{
   1735                             try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other),
   1736                         }),
   1737                     }
   1738                 },
   1739                 .tuple_type => |tuple_info| {
   1740                     if (!location.isInitializer()) {
   1741                         try writer.writeByte('(');
   1742                         try dg.renderCType(writer, ctype);
   1743                         try writer.writeByte(')');
   1744                     }
   1745 
   1746                     try writer.writeByte('{');
   1747                     var need_comma = false;
   1748                     for (0..tuple_info.types.len) |field_index| {
   1749                         if (tuple_info.values.get(ip)[field_index] != .none) continue;
   1750                         const field_ty: Type = .fromInterned(tuple_info.types.get(ip)[field_index]);
   1751                         if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1752 
   1753                         if (need_comma) try writer.writeByte(',');
   1754                         need_comma = true;
   1755                         try dg.renderUndefValue(writer, field_ty, initializer_type);
   1756                     }
   1757                     return writer.writeByte('}');
   1758                 },
   1759                 .union_type => {
   1760                     const loaded_union = ip.loadUnionType(ty.toIntern());
   1761                     switch (loaded_union.flagsUnordered(ip).layout) {
   1762                         .auto, .@"extern" => {
   1763                             if (!location.isInitializer()) {
   1764                                 try writer.writeByte('(');
   1765                                 try dg.renderCType(writer, ctype);
   1766                                 try writer.writeByte(')');
   1767                             }
   1768 
   1769                             const has_tag = loaded_union.hasTag(ip);
   1770                             if (has_tag) try writer.writeByte('{');
   1771                             const aggregate = ctype.info(ctype_pool).aggregate;
   1772                             for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| {
   1773                                 if (outer_field_index > 0) try writer.writeByte(',');
   1774                                 switch (if (has_tag)
   1775                                     aggregate.fields.at(outer_field_index, ctype_pool).name.index
   1776                                 else
   1777                                     .payload) {
   1778                                     .tag => try dg.renderUndefValue(
   1779                                         writer,
   1780                                         .fromInterned(loaded_union.enum_tag_ty),
   1781                                         initializer_type,
   1782                                     ),
   1783                                     .payload => {
   1784                                         try writer.writeByte('{');
   1785                                         for (0..loaded_union.field_types.len) |inner_field_index| {
   1786                                             const inner_field_ty: Type = .fromInterned(
   1787                                                 loaded_union.field_types.get(ip)[inner_field_index],
   1788                                             );
   1789                                             if (!inner_field_ty.hasRuntimeBits(pt.zcu)) continue;
   1790                                             try dg.renderUndefValue(
   1791                                                 writer,
   1792                                                 inner_field_ty,
   1793                                                 initializer_type,
   1794                                             );
   1795                                             break;
   1796                                         }
   1797                                         try writer.writeByte('}');
   1798                                     },
   1799                                     else => unreachable,
   1800                                 }
   1801                             }
   1802                             if (has_tag) try writer.writeByte('}');
   1803                         },
   1804                         .@"packed" => return writer.print("{f}", .{
   1805                             try dg.fmtIntLiteralHex(try pt.undefValue(ty), .Other),
   1806                         }),
   1807                     }
   1808                 },
   1809                 .error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) {
   1810                     .basic => try dg.renderUndefValue(
   1811                         writer,
   1812                         .fromInterned(error_union_type.error_set_type),
   1813                         location,
   1814                     ),
   1815                     .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   1816                     .aggregate => |aggregate| {
   1817                         if (!location.isInitializer()) {
   1818                             try writer.writeByte('(');
   1819                             try dg.renderCType(writer, ctype);
   1820                             try writer.writeByte(')');
   1821                         }
   1822                         try writer.writeByte('{');
   1823                         for (0..aggregate.fields.len) |field_index| {
   1824                             if (field_index > 0) try writer.writeByte(',');
   1825                             try dg.renderUndefValue(
   1826                                 writer,
   1827                                 .fromInterned(
   1828                                     switch (aggregate.fields.at(field_index, ctype_pool).name.index) {
   1829                                         .@"error" => error_union_type.error_set_type,
   1830                                         .payload => error_union_type.payload_type,
   1831                                         else => unreachable,
   1832                                     },
   1833                                 ),
   1834                                 initializer_type,
   1835                             );
   1836                         }
   1837                         try writer.writeByte('}');
   1838                     },
   1839                 },
   1840                 .array_type, .vector_type => {
   1841                     const ai = ty.arrayInfo(zcu);
   1842                     if (ai.elem_type.eql(.u8, zcu)) {
   1843                         const c_len = ty.arrayLenIncludingSentinel(zcu);
   1844                         var literal: StringLiteral = .init(writer, c_len);
   1845                         try literal.start();
   1846                         var index: u64 = 0;
   1847                         while (index < c_len) : (index += 1)
   1848                             try literal.writeChar(0xaa);
   1849                         return literal.end();
   1850                     } else {
   1851                         if (!location.isInitializer()) {
   1852                             try writer.writeByte('(');
   1853                             try dg.renderCType(writer, ctype);
   1854                             try writer.writeByte(')');
   1855                         }
   1856 
   1857                         try writer.writeByte('{');
   1858                         const c_len = ty.arrayLenIncludingSentinel(zcu);
   1859                         var index: u64 = 0;
   1860                         while (index < c_len) : (index += 1) {
   1861                             if (index > 0) try writer.writeAll(", ");
   1862                             try dg.renderUndefValue(writer, ty.childType(zcu), initializer_type);
   1863                         }
   1864                         return writer.writeByte('}');
   1865                     }
   1866                 },
   1867                 .anyframe_type,
   1868                 .opaque_type,
   1869                 .func_type,
   1870                 => unreachable,
   1871 
   1872                 .undef,
   1873                 .simple_value,
   1874                 .variable,
   1875                 .@"extern",
   1876                 .func,
   1877                 .int,
   1878                 .err,
   1879                 .error_union,
   1880                 .enum_literal,
   1881                 .enum_tag,
   1882                 .empty_enum_value,
   1883                 .float,
   1884                 .ptr,
   1885                 .slice,
   1886                 .opt,
   1887                 .aggregate,
   1888                 .un,
   1889                 .memoized_call,
   1890                 => unreachable, // values, not types
   1891             },
   1892         }
   1893     }
   1894 
   1895     fn renderFunctionSignature(
   1896         dg: *DeclGen,
   1897         w: anytype,
   1898         fn_val: Value,
   1899         fn_align: InternPool.Alignment,
   1900         kind: CType.Kind,
   1901         name: union(enum) {
   1902             nav: InternPool.Nav.Index,
   1903             fmt_ctype_pool_string: std.fmt.Formatter(CTypePoolStringFormatData, formatCTypePoolString),
   1904             @"export": struct {
   1905                 main_name: InternPool.NullTerminatedString,
   1906                 extern_name: InternPool.NullTerminatedString,
   1907             },
   1908         },
   1909     ) !void {
   1910         const zcu = dg.pt.zcu;
   1911         const ip = &zcu.intern_pool;
   1912 
   1913         const fn_ty = fn_val.typeOf(zcu);
   1914         const fn_ctype = try dg.ctypeFromType(fn_ty, kind);
   1915 
   1916         const fn_info = zcu.typeToFunc(fn_ty).?;
   1917         if (fn_info.cc == .naked) {
   1918             switch (kind) {
   1919                 .forward => try w.writeAll("zig_naked_decl "),
   1920                 .complete => try w.writeAll("zig_naked "),
   1921                 else => unreachable,
   1922             }
   1923         }
   1924 
   1925         if (fn_val.getFunction(zcu)) |func| {
   1926             const func_analysis = func.analysisUnordered(ip);
   1927 
   1928             if (func_analysis.branch_hint == .cold)
   1929                 try w.writeAll("zig_cold ");
   1930 
   1931             if (kind == .complete and func_analysis.disable_intrinsics or dg.mod.no_builtin)
   1932                 try w.writeAll("zig_no_builtin ");
   1933         }
   1934 
   1935         if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn ");
   1936 
   1937         var trailing = try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, fn_ctype, .suffix, .{});
   1938 
   1939         if (toCallingConvention(fn_info.cc, zcu)) |call_conv| {
   1940             try w.print("{}zig_callconv({s})", .{ trailing, call_conv });
   1941             trailing = .maybe_space;
   1942         }
   1943 
   1944         try w.print("{}", .{trailing});
   1945         switch (name) {
   1946             .nav => |nav| try dg.renderNavName(w, nav),
   1947             .fmt_ctype_pool_string => |fmt| try w.print("{f}", .{fmt}),
   1948             .@"export" => |@"export"| try w.print("{f}", .{fmtIdentSolo(@"export".extern_name.toSlice(ip))}),
   1949         }
   1950 
   1951         try renderTypeSuffix(
   1952             dg.pass,
   1953             &dg.ctype_pool,
   1954             zcu,
   1955             w,
   1956             fn_ctype,
   1957             .suffix,
   1958             CQualifiers.init(.{ .@"const" = switch (kind) {
   1959                 .forward => false,
   1960                 .complete => true,
   1961                 else => unreachable,
   1962             } }),
   1963         );
   1964 
   1965         switch (kind) {
   1966             .forward => {
   1967                 if (fn_align.toByteUnits()) |a| try w.print(" zig_align_fn({})", .{a});
   1968                 switch (name) {
   1969                     .nav, .fmt_ctype_pool_string => {},
   1970                     .@"export" => |@"export"| {
   1971                         const extern_name = @"export".extern_name.toSlice(ip);
   1972                         const is_mangled = isMangledIdent(extern_name, true);
   1973                         const is_export = @"export".extern_name != @"export".main_name;
   1974                         if (is_mangled and is_export) {
   1975                             try w.print(" zig_mangled_export({f}, {f}, {f})", .{
   1976                                 fmtIdentSolo(extern_name),
   1977                                 fmtStringLiteral(extern_name, null),
   1978                                 fmtStringLiteral(@"export".main_name.toSlice(ip), null),
   1979                             });
   1980                         } else if (is_mangled) {
   1981                             try w.print(" zig_mangled({f}, {f})", .{
   1982                                 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),
   1983                             });
   1984                         } else if (is_export) {
   1985                             try w.print(" zig_export({f}, {f})", .{
   1986                                 fmtStringLiteral(@"export".main_name.toSlice(ip), null),
   1987                                 fmtStringLiteral(extern_name, null),
   1988                             });
   1989                         }
   1990                     },
   1991                 }
   1992             },
   1993             .complete => {},
   1994             else => unreachable,
   1995         }
   1996     }
   1997 
   1998     fn ctypeFromType(dg: *DeclGen, ty: Type, kind: CType.Kind) !CType {
   1999         defer std.debug.assert(dg.scratch.items.len == 0);
   2000         return dg.ctype_pool.fromType(dg.gpa, &dg.scratch, ty, dg.pt, dg.mod, kind);
   2001     }
   2002 
   2003     fn byteSize(dg: *DeclGen, ctype: CType) u64 {
   2004         return ctype.byteSize(&dg.ctype_pool, dg.mod);
   2005     }
   2006 
   2007     /// Renders a type as a single identifier, generating intermediate typedefs
   2008     /// if necessary.
   2009     ///
   2010     /// This is guaranteed to be valid in both typedefs and declarations/definitions.
   2011     ///
   2012     /// There are three type formats in total that we support rendering:
   2013     ///   | Function            | Example 1 (*u8) | Example 2 ([10]*u8) |
   2014     ///   |---------------------|-----------------|---------------------|
   2015     ///   | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
   2016     ///   | `renderType`        | "uint8_t *"     | "uint8_t *[10]"     |
   2017     ///
   2018     fn renderType(dg: *DeclGen, w: anytype, t: Type) error{OutOfMemory}!void {
   2019         try dg.renderCType(w, try dg.ctypeFromType(t, .complete));
   2020     }
   2021 
   2022     fn renderCType(dg: *DeclGen, w: anytype, ctype: CType) error{OutOfMemory}!void {
   2023         _ = try renderTypePrefix(dg.pass, &dg.ctype_pool, dg.pt.zcu, w, ctype, .suffix, .{});
   2024         try renderTypeSuffix(dg.pass, &dg.ctype_pool, dg.pt.zcu, w, ctype, .suffix, .{});
   2025     }
   2026 
   2027     const IntCastContext = union(enum) {
   2028         c_value: struct {
   2029             f: *Function,
   2030             value: CValue,
   2031             v: Vectorize,
   2032         },
   2033         value: struct {
   2034             value: Value,
   2035         },
   2036 
   2037         pub fn writeValue(self: *const IntCastContext, dg: *DeclGen, w: anytype, location: ValueRenderLocation) !void {
   2038             switch (self.*) {
   2039                 .c_value => |v| {
   2040                     try v.f.writeCValue(w, v.value, location);
   2041                     try v.v.elem(v.f, w);
   2042                 },
   2043                 .value => |v| try dg.renderValue(w, v.value, location),
   2044             }
   2045         }
   2046     };
   2047     fn intCastIsNoop(dg: *DeclGen, dest_ty: Type, src_ty: Type) bool {
   2048         const pt = dg.pt;
   2049         const zcu = pt.zcu;
   2050         const dest_bits = dest_ty.bitSize(zcu);
   2051         const dest_int_info = dest_ty.intInfo(pt.zcu);
   2052 
   2053         const src_is_ptr = src_ty.isPtrAtRuntime(pt.zcu);
   2054         const src_eff_ty: Type = if (src_is_ptr) switch (dest_int_info.signedness) {
   2055             .unsigned => .usize,
   2056             .signed => .isize,
   2057         } else src_ty;
   2058 
   2059         const src_bits = src_eff_ty.bitSize(zcu);
   2060         const src_int_info = if (src_eff_ty.isAbiInt(pt.zcu)) src_eff_ty.intInfo(pt.zcu) else null;
   2061         if (dest_bits <= 64 and src_bits <= 64) {
   2062             const needs_cast = src_int_info == null or
   2063                 (toCIntBits(dest_int_info.bits) != toCIntBits(src_int_info.?.bits) or
   2064                     dest_int_info.signedness != src_int_info.?.signedness);
   2065             return !needs_cast and !src_is_ptr;
   2066         } else return false;
   2067     }
   2068     /// Renders a cast to an int type, from either an int or a pointer.
   2069     ///
   2070     /// Some platforms don't have 128 bit integers, so we need to use
   2071     /// the zig_make_ and zig_lo_ macros in those cases.
   2072     ///
   2073     ///   | Dest type bits   | Src type         | Result
   2074     ///   |------------------|------------------|---------------------------|
   2075     ///   | < 64 bit integer | pointer          | (zig_<dest_ty>)(zig_<u|i>size)src
   2076     ///   | < 64 bit integer | < 64 bit integer | (zig_<dest_ty>)src
   2077     ///   | < 64 bit integer | > 64 bit integer | zig_lo(src)
   2078     ///   | > 64 bit integer | pointer          | zig_make_<dest_ty>(0, (zig_<u|i>size)src)
   2079     ///   | > 64 bit integer | < 64 bit integer | zig_make_<dest_ty>(0, src)
   2080     ///   | > 64 bit integer | > 64 bit integer | zig_make_<dest_ty>(zig_hi_<src_ty>(src), zig_lo_<src_ty>(src))
   2081     fn renderIntCast(
   2082         dg: *DeclGen,
   2083         w: anytype,
   2084         dest_ty: Type,
   2085         context: IntCastContext,
   2086         src_ty: Type,
   2087         location: ValueRenderLocation,
   2088     ) !void {
   2089         const pt = dg.pt;
   2090         const zcu = pt.zcu;
   2091         const dest_bits = dest_ty.bitSize(zcu);
   2092         const dest_int_info = dest_ty.intInfo(zcu);
   2093 
   2094         const src_is_ptr = src_ty.isPtrAtRuntime(zcu);
   2095         const src_eff_ty: Type = if (src_is_ptr) switch (dest_int_info.signedness) {
   2096             .unsigned => .usize,
   2097             .signed => .isize,
   2098         } else src_ty;
   2099 
   2100         const src_bits = src_eff_ty.bitSize(zcu);
   2101         const src_int_info = if (src_eff_ty.isAbiInt(zcu)) src_eff_ty.intInfo(zcu) else null;
   2102         if (dest_bits <= 64 and src_bits <= 64) {
   2103             const needs_cast = src_int_info == null or
   2104                 (toCIntBits(dest_int_info.bits) != toCIntBits(src_int_info.?.bits) or
   2105                     dest_int_info.signedness != src_int_info.?.signedness);
   2106 
   2107             if (needs_cast) {
   2108                 try w.writeByte('(');
   2109                 try dg.renderType(w, dest_ty);
   2110                 try w.writeByte(')');
   2111             }
   2112             if (src_is_ptr) {
   2113                 try w.writeByte('(');
   2114                 try dg.renderType(w, src_eff_ty);
   2115                 try w.writeByte(')');
   2116             }
   2117             try context.writeValue(dg, w, location);
   2118         } else if (dest_bits <= 64 and src_bits > 64) {
   2119             assert(!src_is_ptr);
   2120             if (dest_bits < 64) {
   2121                 try w.writeByte('(');
   2122                 try dg.renderType(w, dest_ty);
   2123                 try w.writeByte(')');
   2124             }
   2125             try w.writeAll("zig_lo_");
   2126             try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
   2127             try w.writeByte('(');
   2128             try context.writeValue(dg, w, .FunctionArgument);
   2129             try w.writeByte(')');
   2130         } else if (dest_bits > 64 and src_bits <= 64) {
   2131             try w.writeAll("zig_make_");
   2132             try dg.renderTypeForBuiltinFnName(w, dest_ty);
   2133             try w.writeAll("(0, ");
   2134             if (src_is_ptr) {
   2135                 try w.writeByte('(');
   2136                 try dg.renderType(w, src_eff_ty);
   2137                 try w.writeByte(')');
   2138             }
   2139             try context.writeValue(dg, w, .FunctionArgument);
   2140             try w.writeByte(')');
   2141         } else {
   2142             assert(!src_is_ptr);
   2143             try w.writeAll("zig_make_");
   2144             try dg.renderTypeForBuiltinFnName(w, dest_ty);
   2145             try w.writeAll("(zig_hi_");
   2146             try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
   2147             try w.writeByte('(');
   2148             try context.writeValue(dg, w, .FunctionArgument);
   2149             try w.writeAll("), zig_lo_");
   2150             try dg.renderTypeForBuiltinFnName(w, src_eff_ty);
   2151             try w.writeByte('(');
   2152             try context.writeValue(dg, w, .FunctionArgument);
   2153             try w.writeAll("))");
   2154         }
   2155     }
   2156 
   2157     /// Renders a type and name in field declaration/definition format.
   2158     ///
   2159     /// There are three type formats in total that we support rendering:
   2160     ///   | Function            | Example 1 (*u8) | Example 2 ([10]*u8) |
   2161     ///   |---------------------|-----------------|---------------------|
   2162     ///   | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" |
   2163     ///   | `renderType`        | "uint8_t *"     | "uint8_t *[10]"     |
   2164     ///
   2165     fn renderTypeAndName(
   2166         dg: *DeclGen,
   2167         w: anytype,
   2168         ty: Type,
   2169         name: CValue,
   2170         qualifiers: CQualifiers,
   2171         alignment: Alignment,
   2172         kind: CType.Kind,
   2173     ) error{ OutOfMemory, AnalysisFail }!void {
   2174         try dg.renderCTypeAndName(
   2175             w,
   2176             try dg.ctypeFromType(ty, kind),
   2177             name,
   2178             qualifiers,
   2179             CType.AlignAs.fromAlignment(.{
   2180                 .@"align" = alignment,
   2181                 .abi = ty.abiAlignment(dg.pt.zcu),
   2182             }),
   2183         );
   2184     }
   2185 
   2186     fn renderCTypeAndName(
   2187         dg: *DeclGen,
   2188         w: anytype,
   2189         ctype: CType,
   2190         name: CValue,
   2191         qualifiers: CQualifiers,
   2192         alignas: CType.AlignAs,
   2193     ) error{ OutOfMemory, AnalysisFail }!void {
   2194         const zcu = dg.pt.zcu;
   2195         switch (alignas.abiOrder()) {
   2196             .lt => try w.print("zig_under_align({}) ", .{alignas.toByteUnits()}),
   2197             .eq => {},
   2198             .gt => try w.print("zig_align({}) ", .{alignas.toByteUnits()}),
   2199         }
   2200 
   2201         try w.print("{}", .{
   2202             try renderTypePrefix(dg.pass, &dg.ctype_pool, zcu, w, ctype, .suffix, qualifiers),
   2203         });
   2204         try dg.writeName(w, name);
   2205         try renderTypeSuffix(dg.pass, &dg.ctype_pool, zcu, w, ctype, .suffix, .{});
   2206     }
   2207 
   2208     fn writeName(dg: *DeclGen, w: anytype, c_value: CValue) !void {
   2209         switch (c_value) {
   2210             .new_local, .local => |i| try w.print("t{d}", .{i}),
   2211             .constant => |uav| try renderUavName(w, uav),
   2212             .nav => |nav| try dg.renderNavName(w, nav),
   2213             .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
   2214             else => unreachable,
   2215         }
   2216     }
   2217 
   2218     fn writeCValue(dg: *DeclGen, w: anytype, c_value: CValue) !void {
   2219         switch (c_value) {
   2220             .none, .new_local, .local, .local_ref => unreachable,
   2221             .constant => |uav| try renderUavName(w, uav),
   2222             .arg, .arg_array => unreachable,
   2223             .field => |i| try w.print("f{d}", .{i}),
   2224             .nav => |nav| try dg.renderNavName(w, nav),
   2225             .nav_ref => |nav| {
   2226                 try w.writeByte('&');
   2227                 try dg.renderNavName(w, nav);
   2228             },
   2229             .undef => |ty| try dg.renderUndefValue(w, ty, .Other),
   2230             .identifier => |ident| try w.print("{f}", .{fmtIdentSolo(ident)}),
   2231             .payload_identifier => |ident| try w.print("{f}.{f}", .{
   2232                 fmtIdentSolo("payload"),
   2233                 fmtIdentSolo(ident),
   2234             }),
   2235             .ctype_pool_string => |string| try w.print("{f}", .{
   2236                 fmtCTypePoolString(string, &dg.ctype_pool, true),
   2237             }),
   2238         }
   2239     }
   2240 
   2241     fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void {
   2242         switch (c_value) {
   2243             .none,
   2244             .new_local,
   2245             .local,
   2246             .local_ref,
   2247             .constant,
   2248             .arg,
   2249             .arg_array,
   2250             .ctype_pool_string,
   2251             => unreachable,
   2252             .field => |i| try w.print("f{d}", .{i}),
   2253             .nav => |nav| {
   2254                 try w.writeAll("(*");
   2255                 try dg.renderNavName(w, nav);
   2256                 try w.writeByte(')');
   2257             },
   2258             .nav_ref => |nav| try dg.renderNavName(w, nav),
   2259             .undef => unreachable,
   2260             .identifier => |ident| try w.print("(*{f})", .{fmtIdentSolo(ident)}),
   2261             .payload_identifier => |ident| try w.print("(*{f}.{f})", .{
   2262                 fmtIdentSolo("payload"),
   2263                 fmtIdentSolo(ident),
   2264             }),
   2265         }
   2266     }
   2267 
   2268     fn writeCValueMember(
   2269         dg: *DeclGen,
   2270         writer: anytype,
   2271         c_value: CValue,
   2272         member: CValue,
   2273     ) error{ OutOfMemory, AnalysisFail }!void {
   2274         try dg.writeCValue(writer, c_value);
   2275         try writer.writeByte('.');
   2276         try dg.writeCValue(writer, member);
   2277     }
   2278 
   2279     fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void {
   2280         switch (c_value) {
   2281             .none,
   2282             .new_local,
   2283             .local,
   2284             .local_ref,
   2285             .constant,
   2286             .field,
   2287             .undef,
   2288             .arg,
   2289             .arg_array,
   2290             .ctype_pool_string,
   2291             => unreachable,
   2292             .nav, .identifier, .payload_identifier => {
   2293                 try dg.writeCValue(writer, c_value);
   2294                 try writer.writeAll("->");
   2295             },
   2296             .nav_ref => {
   2297                 try dg.writeCValueDeref(writer, c_value);
   2298                 try writer.writeByte('.');
   2299             },
   2300         }
   2301         try dg.writeCValue(writer, member);
   2302     }
   2303 
   2304     fn renderFwdDecl(
   2305         dg: *DeclGen,
   2306         nav_index: InternPool.Nav.Index,
   2307         flags: packed struct {
   2308             is_const: bool,
   2309             is_threadlocal: bool,
   2310             linkage: std.builtin.GlobalLinkage,
   2311             visibility: std.builtin.SymbolVisibility,
   2312         },
   2313     ) !void {
   2314         const zcu = dg.pt.zcu;
   2315         const ip = &zcu.intern_pool;
   2316         const nav = ip.getNav(nav_index);
   2317         const fwd = dg.fwdDeclWriter();
   2318         try fwd.writeAll(switch (flags.linkage) {
   2319             .internal => "static ",
   2320             .strong, .weak, .link_once => "zig_extern ",
   2321         });
   2322         switch (flags.linkage) {
   2323             .internal, .strong => {},
   2324             .weak => try fwd.writeAll("zig_weak_linkage "),
   2325             .link_once => return dg.fail("TODO: CBE: implement linkonce linkage?", .{}),
   2326         }
   2327         switch (flags.linkage) {
   2328             .internal => {},
   2329             .strong, .weak, .link_once => try fwd.print("zig_visibility({s}) ", .{@tagName(flags.visibility)}),
   2330         }
   2331         if (flags.is_threadlocal and !dg.mod.single_threaded) try fwd.writeAll("zig_threadlocal ");
   2332         try dg.renderTypeAndName(
   2333             fwd,
   2334             .fromInterned(nav.typeOf(ip)),
   2335             .{ .nav = nav_index },
   2336             CQualifiers.init(.{ .@"const" = flags.is_const }),
   2337             nav.getAlignment(),
   2338             .complete,
   2339         );
   2340         try fwd.writeAll(";\n");
   2341     }
   2342 
   2343     fn renderNavName(dg: *DeclGen, writer: anytype, nav_index: InternPool.Nav.Index) !void {
   2344         const zcu = dg.pt.zcu;
   2345         const ip = &zcu.intern_pool;
   2346         const nav = ip.getNav(nav_index);
   2347         if (nav.getExtern(ip)) |@"extern"| {
   2348             try writer.print("{f}", .{
   2349                 fmtIdentSolo(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
   2350             });
   2351         } else {
   2352             // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
   2353             // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
   2354             const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
   2355             try writer.print("{}__{d}", .{
   2356                 fmtIdentUnsolo(fqn_slice[0..@min(fqn_slice.len, 100)]),
   2357                 @intFromEnum(nav_index),
   2358             });
   2359         }
   2360     }
   2361 
   2362     fn renderUavName(writer: anytype, uav: Value) !void {
   2363         try writer.print("__anon_{d}", .{@intFromEnum(uav.toIntern())});
   2364     }
   2365 
   2366     fn renderTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ty: Type) !void {
   2367         try dg.renderCTypeForBuiltinFnName(writer, try dg.ctypeFromType(ty, .complete));
   2368     }
   2369 
   2370     fn renderCTypeForBuiltinFnName(dg: *DeclGen, writer: anytype, ctype: CType) !void {
   2371         switch (ctype.info(&dg.ctype_pool)) {
   2372             else => |ctype_info| try writer.print("{c}{d}", .{
   2373                 if (ctype.isBool())
   2374                     signAbbrev(.unsigned)
   2375                 else if (ctype.isInteger())
   2376                     signAbbrev(ctype.signedness(dg.mod))
   2377                 else if (ctype.isFloat())
   2378                     @as(u8, 'f')
   2379                 else if (ctype_info == .pointer)
   2380                     @as(u8, 'p')
   2381                 else
   2382                     return dg.fail("TODO: CBE: implement renderTypeForBuiltinFnName for {s} type", .{@tagName(ctype_info)}),
   2383                 if (ctype.isFloat()) ctype.floatActiveBits(dg.mod) else dg.byteSize(ctype) * 8,
   2384             }),
   2385             .array => try writer.writeAll("big"),
   2386         }
   2387     }
   2388 
   2389     fn renderBuiltinInfo(dg: *DeclGen, writer: anytype, ty: Type, info: BuiltinInfo) !void {
   2390         const ctype = try dg.ctypeFromType(ty, .complete);
   2391         const is_big = ctype.info(&dg.ctype_pool) == .array;
   2392         switch (info) {
   2393             .none => if (!is_big) return,
   2394             .bits => {},
   2395         }
   2396 
   2397         const pt = dg.pt;
   2398         const zcu = pt.zcu;
   2399         const int_info: std.builtin.Type.Int = if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else .{
   2400             .signedness = .unsigned,
   2401             .bits = @intCast(ty.bitSize(zcu)),
   2402         };
   2403 
   2404         if (is_big) try writer.print(", {}", .{int_info.signedness == .signed});
   2405         try writer.print(", {f}", .{try dg.fmtIntLiteralDec(
   2406             try pt.intValue(if (is_big) .u16 else .u8, int_info.bits),
   2407             .FunctionArgument,
   2408         )});
   2409     }
   2410 
   2411     fn fmtIntLiteral(
   2412         dg: *DeclGen,
   2413         val: Value,
   2414         loc: ValueRenderLocation,
   2415         base: u8,
   2416         case: std.fmt.Case,
   2417     ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
   2418         const zcu = dg.pt.zcu;
   2419         const kind = loc.toCTypeKind();
   2420         const ty = val.typeOf(zcu);
   2421         return .{ .data = .{
   2422             .dg = dg,
   2423             .int_info = ty.intInfo(zcu),
   2424             .kind = kind,
   2425             .ctype = try dg.ctypeFromType(ty, kind),
   2426             .val = val,
   2427             .base = base,
   2428             .case = case,
   2429         } };
   2430     }
   2431 
   2432     fn fmtIntLiteralDec(
   2433         dg: *DeclGen,
   2434         val: Value,
   2435         loc: ValueRenderLocation,
   2436     ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
   2437         return fmtIntLiteral(dg, val, loc, 10, .lower);
   2438     }
   2439 
   2440     fn fmtIntLiteralHex(
   2441         dg: *DeclGen,
   2442         val: Value,
   2443         loc: ValueRenderLocation,
   2444     ) !std.fmt.Formatter(FormatIntLiteralContext, formatIntLiteral) {
   2445         return fmtIntLiteral(dg, val, loc, 16, .lower);
   2446     }
   2447 };
   2448 
   2449 const CTypeFix = enum { prefix, suffix };
   2450 const CQualifiers = std.enums.EnumSet(enum { @"const", @"volatile", restrict });
   2451 const Const = CQualifiers.init(.{ .@"const" = true });
   2452 const RenderCTypeTrailing = enum {
   2453     no_space,
   2454     maybe_space,
   2455 
   2456     pub fn format(
   2457         self: @This(),
   2458         comptime fmt: []const u8,
   2459         _: std.fmt.FormatOptions,
   2460         w: anytype,
   2461     ) @TypeOf(w).Error!void {
   2462         if (fmt.len != 0)
   2463             @compileError("invalid format string '" ++ fmt ++ "' for type '" ++
   2464                 @typeName(@This()) ++ "'");
   2465         comptime assert(fmt.len == 0);
   2466         switch (self) {
   2467             .no_space => {},
   2468             .maybe_space => try w.writeByte(' '),
   2469         }
   2470     }
   2471 };
   2472 fn renderAlignedTypeName(w: anytype, ctype: CType) !void {
   2473     try w.print("anon__aligned_{d}", .{@intFromEnum(ctype.index)});
   2474 }
   2475 fn renderFwdDeclTypeName(
   2476     zcu: *Zcu,
   2477     w: anytype,
   2478     ctype: CType,
   2479     fwd_decl: CType.Info.FwdDecl,
   2480     attributes: []const u8,
   2481 ) !void {
   2482     const ip = &zcu.intern_pool;
   2483     try w.print("{s} {s}", .{ @tagName(fwd_decl.tag), attributes });
   2484     switch (fwd_decl.name) {
   2485         .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}),
   2486         .index => |index| try w.print("{}__{d}", .{
   2487             fmtIdentUnsolo(Type.fromInterned(index).containerTypeName(ip).toSlice(&zcu.intern_pool)),
   2488             @intFromEnum(index),
   2489         }),
   2490     }
   2491 }
   2492 fn renderTypePrefix(
   2493     pass: DeclGen.Pass,
   2494     ctype_pool: *const CType.Pool,
   2495     zcu: *Zcu,
   2496     w: anytype,
   2497     ctype: CType,
   2498     parent_fix: CTypeFix,
   2499     qualifiers: CQualifiers,
   2500 ) @TypeOf(w).Error!RenderCTypeTrailing {
   2501     var trailing = RenderCTypeTrailing.maybe_space;
   2502     switch (ctype.info(ctype_pool)) {
   2503         .basic => |basic_info| try w.writeAll(@tagName(basic_info)),
   2504 
   2505         .pointer => |pointer_info| {
   2506             try w.print("{}*", .{try renderTypePrefix(
   2507                 pass,
   2508                 ctype_pool,
   2509                 zcu,
   2510                 w,
   2511                 pointer_info.elem_ctype,
   2512                 .prefix,
   2513                 CQualifiers.init(.{
   2514                     .@"const" = pointer_info.@"const",
   2515                     .@"volatile" = pointer_info.@"volatile",
   2516                 }),
   2517             )});
   2518             trailing = .no_space;
   2519         },
   2520 
   2521         .aligned => switch (pass) {
   2522             .nav => |nav| try w.print("nav__{d}_{d}", .{
   2523                 @intFromEnum(nav), @intFromEnum(ctype.index),
   2524             }),
   2525             .uav => |uav| try w.print("uav__{d}_{d}", .{
   2526                 @intFromEnum(uav), @intFromEnum(ctype.index),
   2527             }),
   2528             .flush => try renderAlignedTypeName(w, ctype),
   2529         },
   2530 
   2531         .array, .vector => |sequence_info| {
   2532             const child_trailing = try renderTypePrefix(
   2533                 pass,
   2534                 ctype_pool,
   2535                 zcu,
   2536                 w,
   2537                 sequence_info.elem_ctype,
   2538                 .suffix,
   2539                 qualifiers,
   2540             );
   2541             switch (parent_fix) {
   2542                 .prefix => {
   2543                     try w.print("{}(", .{child_trailing});
   2544                     return .no_space;
   2545                 },
   2546                 .suffix => return child_trailing,
   2547             }
   2548         },
   2549 
   2550         .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) {
   2551             .anon => switch (pass) {
   2552                 .nav => |nav| try w.print("nav__{d}_{d}", .{
   2553                     @intFromEnum(nav), @intFromEnum(ctype.index),
   2554                 }),
   2555                 .uav => |uav| try w.print("uav__{d}_{d}", .{
   2556                     @intFromEnum(uav), @intFromEnum(ctype.index),
   2557                 }),
   2558                 .flush => try renderFwdDeclTypeName(zcu, w, ctype, fwd_decl_info, ""),
   2559             },
   2560             .index => try renderFwdDeclTypeName(zcu, w, ctype, fwd_decl_info, ""),
   2561         },
   2562 
   2563         .aggregate => |aggregate_info| switch (aggregate_info.name) {
   2564             .anon => {
   2565                 try w.print("{s} {s}", .{
   2566                     @tagName(aggregate_info.tag),
   2567                     if (aggregate_info.@"packed") "zig_packed(" else "",
   2568                 });
   2569                 try renderFields(zcu, w, ctype_pool, aggregate_info, 1);
   2570                 if (aggregate_info.@"packed") try w.writeByte(')');
   2571             },
   2572             .fwd_decl => |fwd_decl| return renderTypePrefix(
   2573                 pass,
   2574                 ctype_pool,
   2575                 zcu,
   2576                 w,
   2577                 fwd_decl,
   2578                 parent_fix,
   2579                 qualifiers,
   2580             ),
   2581         },
   2582 
   2583         .function => |function_info| {
   2584             const child_trailing = try renderTypePrefix(
   2585                 pass,
   2586                 ctype_pool,
   2587                 zcu,
   2588                 w,
   2589                 function_info.return_ctype,
   2590                 .suffix,
   2591                 .{},
   2592             );
   2593             switch (parent_fix) {
   2594                 .prefix => {
   2595                     try w.print("{}(", .{child_trailing});
   2596                     return .no_space;
   2597                 },
   2598                 .suffix => return child_trailing,
   2599             }
   2600         },
   2601     }
   2602     var qualifier_it = qualifiers.iterator();
   2603     while (qualifier_it.next()) |qualifier| {
   2604         try w.print("{}{s}", .{ trailing, @tagName(qualifier) });
   2605         trailing = .maybe_space;
   2606     }
   2607     return trailing;
   2608 }
   2609 fn renderTypeSuffix(
   2610     pass: DeclGen.Pass,
   2611     ctype_pool: *const CType.Pool,
   2612     zcu: *Zcu,
   2613     w: anytype,
   2614     ctype: CType,
   2615     parent_fix: CTypeFix,
   2616     qualifiers: CQualifiers,
   2617 ) @TypeOf(w).Error!void {
   2618     switch (ctype.info(ctype_pool)) {
   2619         .basic, .aligned, .fwd_decl, .aggregate => {},
   2620         .pointer => |pointer_info| try renderTypeSuffix(
   2621             pass,
   2622             ctype_pool,
   2623             zcu,
   2624             w,
   2625             pointer_info.elem_ctype,
   2626             .prefix,
   2627             .{},
   2628         ),
   2629         .array, .vector => |sequence_info| {
   2630             switch (parent_fix) {
   2631                 .prefix => try w.writeByte(')'),
   2632                 .suffix => {},
   2633             }
   2634 
   2635             try w.print("[{}]", .{sequence_info.len});
   2636             try renderTypeSuffix(pass, ctype_pool, zcu, w, sequence_info.elem_ctype, .suffix, .{});
   2637         },
   2638         .function => |function_info| {
   2639             switch (parent_fix) {
   2640                 .prefix => try w.writeByte(')'),
   2641                 .suffix => {},
   2642             }
   2643 
   2644             try w.writeByte('(');
   2645             var need_comma = false;
   2646             for (0..function_info.param_ctypes.len) |param_index| {
   2647                 const param_type = function_info.param_ctypes.at(param_index, ctype_pool);
   2648                 if (need_comma) try w.writeAll(", ");
   2649                 need_comma = true;
   2650                 const trailing =
   2651                     try renderTypePrefix(pass, ctype_pool, zcu, w, param_type, .suffix, qualifiers);
   2652                 if (qualifiers.contains(.@"const")) try w.print("{}a{d}", .{ trailing, param_index });
   2653                 try renderTypeSuffix(pass, ctype_pool, zcu, w, param_type, .suffix, .{});
   2654             }
   2655             if (function_info.varargs) {
   2656                 if (need_comma) try w.writeAll(", ");
   2657                 need_comma = true;
   2658                 try w.writeAll("...");
   2659             }
   2660             if (!need_comma) try w.writeAll("void");
   2661             try w.writeByte(')');
   2662 
   2663             try renderTypeSuffix(pass, ctype_pool, zcu, w, function_info.return_ctype, .suffix, .{});
   2664         },
   2665     }
   2666 }
   2667 fn renderFields(
   2668     zcu: *Zcu,
   2669     writer: anytype,
   2670     ctype_pool: *const CType.Pool,
   2671     aggregate_info: CType.Info.Aggregate,
   2672     indent: usize,
   2673 ) !void {
   2674     try writer.writeAll("{\n");
   2675     for (0..aggregate_info.fields.len) |field_index| {
   2676         const field_info = aggregate_info.fields.at(field_index, ctype_pool);
   2677         try writer.writeByteNTimes(' ', indent + 1);
   2678         switch (field_info.alignas.abiOrder()) {
   2679             .lt => {
   2680                 std.debug.assert(aggregate_info.@"packed");
   2681                 if (field_info.alignas.@"align" != .@"1") try writer.print("zig_under_align({}) ", .{
   2682                     field_info.alignas.toByteUnits(),
   2683                 });
   2684             },
   2685             .eq => if (aggregate_info.@"packed" and field_info.alignas.@"align" != .@"1")
   2686                 try writer.print("zig_align({}) ", .{field_info.alignas.toByteUnits()}),
   2687             .gt => {
   2688                 std.debug.assert(field_info.alignas.@"align" != .@"1");
   2689                 try writer.print("zig_align({}) ", .{field_info.alignas.toByteUnits()});
   2690             },
   2691         }
   2692         const trailing = try renderTypePrefix(
   2693             .flush,
   2694             ctype_pool,
   2695             zcu,
   2696             writer,
   2697             field_info.ctype,
   2698             .suffix,
   2699             .{},
   2700         );
   2701         try writer.print("{}{f}", .{ trailing, fmtCTypePoolString(field_info.name, ctype_pool, true) });
   2702         try renderTypeSuffix(.flush, ctype_pool, zcu, writer, field_info.ctype, .suffix, .{});
   2703         try writer.writeAll(";\n");
   2704     }
   2705     try writer.writeByteNTimes(' ', indent);
   2706     try writer.writeByte('}');
   2707 }
   2708 
   2709 pub fn genTypeDecl(
   2710     zcu: *Zcu,
   2711     writer: anytype,
   2712     global_ctype_pool: *const CType.Pool,
   2713     global_ctype: CType,
   2714     pass: DeclGen.Pass,
   2715     decl_ctype_pool: *const CType.Pool,
   2716     decl_ctype: CType,
   2717     found_existing: bool,
   2718 ) !void {
   2719     switch (global_ctype.info(global_ctype_pool)) {
   2720         .basic, .pointer, .array, .vector, .function => {},
   2721         .aligned => |aligned_info| {
   2722             if (!found_existing) {
   2723                 std.debug.assert(aligned_info.alignas.abiOrder().compare(.lt));
   2724                 try writer.print("typedef zig_under_align({d}) ", .{aligned_info.alignas.toByteUnits()});
   2725                 try writer.print("{}", .{try renderTypePrefix(
   2726                     .flush,
   2727                     global_ctype_pool,
   2728                     zcu,
   2729                     writer,
   2730                     aligned_info.ctype,
   2731                     .suffix,
   2732                     .{},
   2733                 )});
   2734                 try renderAlignedTypeName(writer, global_ctype);
   2735                 try renderTypeSuffix(.flush, global_ctype_pool, zcu, writer, aligned_info.ctype, .suffix, .{});
   2736                 try writer.writeAll(";\n");
   2737             }
   2738             switch (pass) {
   2739                 .nav, .uav => {
   2740                     try writer.writeAll("typedef ");
   2741                     _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{});
   2742                     try writer.writeByte(' ');
   2743                     _ = try renderTypePrefix(pass, decl_ctype_pool, zcu, writer, decl_ctype, .suffix, .{});
   2744                     try writer.writeAll(";\n");
   2745                 },
   2746                 .flush => {},
   2747             }
   2748         },
   2749         .fwd_decl => |fwd_decl_info| switch (fwd_decl_info.name) {
   2750             .anon => switch (pass) {
   2751                 .nav, .uav => {
   2752                     try writer.writeAll("typedef ");
   2753                     _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{});
   2754                     try writer.writeByte(' ');
   2755                     _ = try renderTypePrefix(pass, decl_ctype_pool, zcu, writer, decl_ctype, .suffix, .{});
   2756                     try writer.writeAll(";\n");
   2757                 },
   2758                 .flush => {},
   2759             },
   2760             .index => |index| if (!found_existing) {
   2761                 const ip = &zcu.intern_pool;
   2762                 const ty: Type = .fromInterned(index);
   2763                 _ = try renderTypePrefix(.flush, global_ctype_pool, zcu, writer, global_ctype, .suffix, .{});
   2764                 try writer.writeByte(';');
   2765                 const file_scope = ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip);
   2766                 if (!zcu.fileByIndex(file_scope).mod.?.strip) try writer.print(" /* {} */", .{
   2767                     ty.containerTypeName(ip).fmt(ip),
   2768                 });
   2769                 try writer.writeByte('\n');
   2770             },
   2771         },
   2772         .aggregate => |aggregate_info| switch (aggregate_info.name) {
   2773             .anon => {},
   2774             .fwd_decl => |fwd_decl| if (!found_existing) {
   2775                 try renderFwdDeclTypeName(
   2776                     zcu,
   2777                     writer,
   2778                     fwd_decl,
   2779                     fwd_decl.info(global_ctype_pool).fwd_decl,
   2780                     if (aggregate_info.@"packed") "zig_packed(" else "",
   2781                 );
   2782                 try writer.writeByte(' ');
   2783                 try renderFields(zcu, writer, global_ctype_pool, aggregate_info, 0);
   2784                 if (aggregate_info.@"packed") try writer.writeByte(')');
   2785                 try writer.writeAll(";\n");
   2786             },
   2787         },
   2788     }
   2789 }
   2790 
   2791 pub fn genGlobalAsm(zcu: *Zcu, writer: anytype) !void {
   2792     for (zcu.global_assembly.values()) |asm_source| {
   2793         try writer.print("__asm({f});\n", .{fmtStringLiteral(asm_source, null)});
   2794     }
   2795 }
   2796 
   2797 pub fn genErrDecls(o: *Object) !void {
   2798     const pt = o.dg.pt;
   2799     const zcu = pt.zcu;
   2800     const ip = &zcu.intern_pool;
   2801     const writer = o.writer();
   2802 
   2803     var max_name_len: usize = 0;
   2804     // do not generate an invalid empty enum when the global error set is empty
   2805     const names = ip.global_error_set.getNamesFromMainThread();
   2806     if (names.len > 0) {
   2807         try writer.writeAll("enum {\n");
   2808         o.indent_writer.pushIndent();
   2809         for (names, 1..) |name_nts, value| {
   2810             const name = name_nts.toSlice(ip);
   2811             max_name_len = @max(name.len, max_name_len);
   2812             const err_val = try pt.intern(.{ .err = .{
   2813                 .ty = .anyerror_type,
   2814                 .name = name_nts,
   2815             } });
   2816             try o.dg.renderValue(writer, Value.fromInterned(err_val), .Other);
   2817             try writer.print(" = {d}u,\n", .{value});
   2818         }
   2819         o.indent_writer.popIndent();
   2820         try writer.writeAll("};\n");
   2821     }
   2822     const array_identifier = "zig_errorName";
   2823     const name_prefix = array_identifier ++ "_";
   2824     const name_buf = try o.dg.gpa.alloc(u8, name_prefix.len + max_name_len);
   2825     defer o.dg.gpa.free(name_buf);
   2826 
   2827     @memcpy(name_buf[0..name_prefix.len], name_prefix);
   2828     for (names) |name| {
   2829         const name_slice = name.toSlice(ip);
   2830         @memcpy(name_buf[name_prefix.len..][0..name_slice.len], name_slice);
   2831         const identifier = name_buf[0 .. name_prefix.len + name_slice.len];
   2832 
   2833         const name_ty = try pt.arrayType(.{
   2834             .len = name_slice.len,
   2835             .child = .u8_type,
   2836             .sentinel = .zero_u8,
   2837         });
   2838         const name_val = try pt.intern(.{ .aggregate = .{
   2839             .ty = name_ty.toIntern(),
   2840             .storage = .{ .bytes = name.toString() },
   2841         } });
   2842 
   2843         try writer.writeAll("static ");
   2844         try o.dg.renderTypeAndName(
   2845             writer,
   2846             name_ty,
   2847             .{ .identifier = identifier },
   2848             Const,
   2849             .none,
   2850             .complete,
   2851         );
   2852         try writer.writeAll(" = ");
   2853         try o.dg.renderValue(writer, Value.fromInterned(name_val), .StaticInitializer);
   2854         try writer.writeAll(";\n");
   2855     }
   2856 
   2857     const name_array_ty = try pt.arrayType(.{
   2858         .len = 1 + names.len,
   2859         .child = .slice_const_u8_sentinel_0_type,
   2860     });
   2861 
   2862     try writer.writeAll("static ");
   2863     try o.dg.renderTypeAndName(
   2864         writer,
   2865         name_array_ty,
   2866         .{ .identifier = array_identifier },
   2867         Const,
   2868         .none,
   2869         .complete,
   2870     );
   2871     try writer.writeAll(" = {");
   2872     for (names, 1..) |name_nts, val| {
   2873         const name = name_nts.toSlice(ip);
   2874         if (val > 1) try writer.writeAll(", ");
   2875         try writer.print("{{" ++ name_prefix ++ "{f}, {f}}}", .{
   2876             fmtIdentUnsolo(name),
   2877             try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, name.len), .StaticInitializer),
   2878         });
   2879     }
   2880     try writer.writeAll("};\n");
   2881 }
   2882 
   2883 pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFnMap.Entry) !void {
   2884     const pt = o.dg.pt;
   2885     const zcu = pt.zcu;
   2886     const ip = &zcu.intern_pool;
   2887     const ctype_pool = &o.dg.ctype_pool;
   2888     const w = o.writer();
   2889     const key = lazy_fn.key_ptr.*;
   2890     const val = lazy_fn.value_ptr;
   2891     switch (key) {
   2892         .tag_name => |enum_ty_ip| {
   2893             const enum_ty: Type = .fromInterned(enum_ty_ip);
   2894             const name_slice_ty: Type = .slice_const_u8_sentinel_0;
   2895 
   2896             try w.writeAll("static ");
   2897             try o.dg.renderType(w, name_slice_ty);
   2898             try w.print(" {}(", .{val.fn_name.fmt(lazy_ctype_pool)});
   2899             try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, .none, .complete);
   2900             try w.writeAll(") {\n switch (tag) {\n");
   2901             const tag_names = enum_ty.enumFields(zcu);
   2902             for (0..tag_names.len) |tag_index| {
   2903                 const tag_name = tag_names.get(ip)[tag_index];
   2904                 const tag_name_len = tag_name.length(ip);
   2905                 const tag_val = try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index));
   2906 
   2907                 const name_ty = try pt.arrayType(.{
   2908                     .len = tag_name_len,
   2909                     .child = .u8_type,
   2910                     .sentinel = .zero_u8,
   2911                 });
   2912                 const name_val = try pt.intern(.{ .aggregate = .{
   2913                     .ty = name_ty.toIntern(),
   2914                     .storage = .{ .bytes = tag_name.toString() },
   2915                 } });
   2916 
   2917                 try w.print("  case {f}: {{\n   static ", .{
   2918                     try o.dg.fmtIntLiteralDec(try tag_val.intFromEnum(enum_ty, pt), .Other),
   2919                 });
   2920                 try o.dg.renderTypeAndName(w, name_ty, .{ .identifier = "name" }, Const, .none, .complete);
   2921                 try w.writeAll(" = ");
   2922                 try o.dg.renderValue(w, Value.fromInterned(name_val), .StaticInitializer);
   2923                 try w.writeAll(";\n   return (");
   2924                 try o.dg.renderType(w, name_slice_ty);
   2925                 try w.print("){{{f}, {f}}};\n", .{
   2926                     fmtIdentUnsolo("name"),
   2927                     try o.dg.fmtIntLiteralDec(try pt.intValue(.usize, tag_name_len), .Other),
   2928                 });
   2929 
   2930                 try w.writeAll("  }\n");
   2931             }
   2932             try w.writeAll(" }\n while (");
   2933             try o.dg.renderValue(w, Value.true, .Other);
   2934             try w.writeAll(") ");
   2935             _ = try airBreakpoint(w);
   2936             try w.writeAll("}\n");
   2937         },
   2938         .never_tail, .never_inline => |fn_nav_index| {
   2939             const fn_val = zcu.navValue(fn_nav_index);
   2940             const fn_ctype = try o.dg.ctypeFromType(fn_val.typeOf(zcu), .complete);
   2941             const fn_info = fn_ctype.info(ctype_pool).function;
   2942             const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool, true);
   2943 
   2944             const fwd = o.dg.fwdDeclWriter();
   2945             try fwd.print("static zig_{s} ", .{@tagName(key)});
   2946             try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{
   2947                 .fmt_ctype_pool_string = fn_name,
   2948             });
   2949             try fwd.writeAll(";\n");
   2950 
   2951             try w.print("zig_{s} ", .{@tagName(key)});
   2952             try o.dg.renderFunctionSignature(w, fn_val, .none, .complete, .{
   2953                 .fmt_ctype_pool_string = fn_name,
   2954             });
   2955             try w.writeAll(" {\n return ");
   2956             try o.dg.renderNavName(w, fn_nav_index);
   2957             try w.writeByte('(');
   2958             for (0..fn_info.param_ctypes.len) |arg| {
   2959                 if (arg > 0) try w.writeAll(", ");
   2960                 try w.print("a{d}", .{arg});
   2961             }
   2962             try w.writeAll(");\n}\n");
   2963         },
   2964     }
   2965 }
   2966 
   2967 pub fn generate(
   2968     lf: *link.File,
   2969     pt: Zcu.PerThread,
   2970     src_loc: Zcu.LazySrcLoc,
   2971     func_index: InternPool.Index,
   2972     air: *const Air,
   2973     liveness: *const Air.Liveness,
   2974 ) @import("../codegen.zig").CodeGenError!Mir {
   2975     const zcu = pt.zcu;
   2976     const gpa = zcu.gpa;
   2977 
   2978     _ = src_loc;
   2979     assert(lf.tag == .c);
   2980 
   2981     const func = zcu.funcInfo(func_index);
   2982 
   2983     var function: Function = .{
   2984         .value_map = .init(gpa),
   2985         .air = air.*,
   2986         .liveness = liveness.*,
   2987         .func_index = func_index,
   2988         .object = .{
   2989             .dg = .{
   2990                 .gpa = gpa,
   2991                 .pt = pt,
   2992                 .mod = zcu.navFileScope(func.owner_nav).mod.?,
   2993                 .error_msg = null,
   2994                 .pass = .{ .nav = func.owner_nav },
   2995                 .is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
   2996                 .expected_block = null,
   2997                 .fwd_decl = .init(gpa),
   2998                 .ctype_pool = .empty,
   2999                 .scratch = .empty,
   3000                 .uavs = .empty,
   3001             },
   3002             .code = .init(gpa),
   3003             .indent_writer = undefined, // set later so we can get a pointer to object.code
   3004         },
   3005         .lazy_fns = .empty,
   3006     };
   3007     defer {
   3008         function.object.code.deinit();
   3009         function.object.dg.fwd_decl.deinit();
   3010         function.object.dg.ctype_pool.deinit(gpa);
   3011         function.object.dg.scratch.deinit(gpa);
   3012         function.object.dg.uavs.deinit(gpa);
   3013         function.deinit();
   3014     }
   3015     try function.object.dg.ctype_pool.init(gpa);
   3016     function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() };
   3017 
   3018     genFunc(&function) catch |err| switch (err) {
   3019         error.AnalysisFail => return zcu.codegenFailMsg(func.owner_nav, function.object.dg.error_msg.?),
   3020         error.OutOfMemory => |e| return e,
   3021     };
   3022 
   3023     var mir: Mir = .{
   3024         .uavs = .empty,
   3025         .code = &.{},
   3026         .fwd_decl = &.{},
   3027         .ctype_pool = .empty,
   3028         .lazy_fns = .empty,
   3029     };
   3030     errdefer mir.deinit(gpa);
   3031     mir.uavs = function.object.dg.uavs.move();
   3032     mir.code = try function.object.code.toOwnedSlice();
   3033     mir.fwd_decl = try function.object.dg.fwd_decl.toOwnedSlice();
   3034     mir.ctype_pool = function.object.dg.ctype_pool.move();
   3035     mir.lazy_fns = function.lazy_fns.move();
   3036     return mir;
   3037 }
   3038 
   3039 fn genFunc(f: *Function) !void {
   3040     const tracy = trace(@src());
   3041     defer tracy.end();
   3042 
   3043     const o = &f.object;
   3044     const zcu = o.dg.pt.zcu;
   3045     const ip = &zcu.intern_pool;
   3046     const gpa = o.dg.gpa;
   3047     const nav_index = o.dg.pass.nav;
   3048     const nav_val = zcu.navValue(nav_index);
   3049     const nav = ip.getNav(nav_index);
   3050 
   3051     o.code_header = std.ArrayList(u8).init(gpa);
   3052     defer o.code_header.deinit();
   3053 
   3054     const fwd = o.dg.fwdDeclWriter();
   3055     try fwd.writeAll("static ");
   3056     try o.dg.renderFunctionSignature(
   3057         fwd,
   3058         nav_val,
   3059         nav.status.fully_resolved.alignment,
   3060         .forward,
   3061         .{ .nav = nav_index },
   3062     );
   3063     try fwd.writeAll(";\n");
   3064 
   3065     if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|
   3066         try o.writer().print("zig_linksection_fn({f}) ", .{fmtStringLiteral(s, null)});
   3067     try o.dg.renderFunctionSignature(
   3068         o.writer(),
   3069         nav_val,
   3070         .none,
   3071         .complete,
   3072         .{ .nav = nav_index },
   3073     );
   3074     try o.writer().writeByte(' ');
   3075 
   3076     // In case we need to use the header, populate it with a copy of the function
   3077     // signature here. We anticipate a brace, newline, and space.
   3078     try o.code_header.ensureUnusedCapacity(o.code.items.len + 3);
   3079     o.code_header.appendSliceAssumeCapacity(o.code.items);
   3080     o.code_header.appendSliceAssumeCapacity("{\n ");
   3081     const empty_header_len = o.code_header.items.len;
   3082 
   3083     f.free_locals_map.clearRetainingCapacity();
   3084 
   3085     const main_body = f.air.getMainBody();
   3086     try genBodyResolveState(f, undefined, &.{}, main_body, false);
   3087     try o.indent_writer.insertNewline();
   3088     if (o.dg.expected_block) |_|
   3089         return f.fail("runtime code not allowed in naked function", .{});
   3090 
   3091     // Take advantage of the free_locals map to bucket locals per type. All
   3092     // locals corresponding to AIR instructions should be in there due to
   3093     // Liveness analysis, however, locals from alloc instructions will be
   3094     // missing. These are added now to complete the map. Then we can sort by
   3095     // alignment, descending.
   3096     const free_locals = &f.free_locals_map;
   3097     assert(f.value_map.count() == 0); // there must not be any unfreed locals
   3098     for (f.allocs.keys(), f.allocs.values()) |local_index, should_emit| {
   3099         if (!should_emit) continue;
   3100         const local = f.locals.items[local_index];
   3101         log.debug("inserting local {d} into free_locals", .{local_index});
   3102         const gop = try free_locals.getOrPut(gpa, local.getType());
   3103         if (!gop.found_existing) gop.value_ptr.* = .{};
   3104         try gop.value_ptr.putNoClobber(gpa, local_index, {});
   3105     }
   3106 
   3107     const SortContext = struct {
   3108         keys: []const LocalType,
   3109 
   3110         pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
   3111             const lhs_ty = ctx.keys[lhs_index];
   3112             const rhs_ty = ctx.keys[rhs_index];
   3113             return lhs_ty.alignas.order(rhs_ty.alignas).compare(.gt);
   3114         }
   3115     };
   3116     free_locals.sort(SortContext{ .keys = free_locals.keys() });
   3117 
   3118     const w = o.codeHeaderWriter();
   3119     for (free_locals.values()) |list| {
   3120         for (list.keys()) |local_index| {
   3121             const local = f.locals.items[local_index];
   3122             try o.dg.renderCTypeAndName(w, local.ctype, .{ .local = local_index }, .{}, local.flags.alignas);
   3123             try w.writeAll(";\n ");
   3124         }
   3125     }
   3126 
   3127     // If we have a header to insert, append the body to the header
   3128     // and then return the result, freeing the body.
   3129     if (o.code_header.items.len > empty_header_len) {
   3130         try o.code_header.appendSlice(o.code.items[empty_header_len..]);
   3131         mem.swap(std.ArrayList(u8), &o.code, &o.code_header);
   3132     }
   3133 }
   3134 
   3135 pub fn genDecl(o: *Object) !void {
   3136     const tracy = trace(@src());
   3137     defer tracy.end();
   3138 
   3139     const pt = o.dg.pt;
   3140     const zcu = pt.zcu;
   3141     const ip = &zcu.intern_pool;
   3142     const nav = ip.getNav(o.dg.pass.nav);
   3143     const nav_ty: Type = .fromInterned(nav.typeOf(ip));
   3144 
   3145     if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return;
   3146     switch (ip.indexToKey(nav.status.fully_resolved.val)) {
   3147         .@"extern" => |@"extern"| {
   3148             if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{
   3149                 .is_const = @"extern".is_const,
   3150                 .is_threadlocal = @"extern".is_threadlocal,
   3151                 .linkage = @"extern".linkage,
   3152                 .visibility = @"extern".visibility,
   3153             });
   3154 
   3155             const fwd = o.dg.fwdDeclWriter();
   3156             try fwd.writeAll("zig_extern ");
   3157             try o.dg.renderFunctionSignature(
   3158                 fwd,
   3159                 Value.fromInterned(nav.status.fully_resolved.val),
   3160                 nav.status.fully_resolved.alignment,
   3161                 .forward,
   3162                 .{ .@"export" = .{
   3163                     .main_name = nav.name,
   3164                     .extern_name = nav.name,
   3165                 } },
   3166             );
   3167             try fwd.writeAll(";\n");
   3168         },
   3169         .variable => |variable| {
   3170             try o.dg.renderFwdDecl(o.dg.pass.nav, .{
   3171                 .is_const = false,
   3172                 .is_threadlocal = variable.is_threadlocal,
   3173                 .linkage = .internal,
   3174                 .visibility = .default,
   3175             });
   3176             const w = o.writer();
   3177             if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
   3178             if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
   3179                 try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});
   3180             try o.dg.renderTypeAndName(
   3181                 w,
   3182                 nav_ty,
   3183                 .{ .nav = o.dg.pass.nav },
   3184                 .{},
   3185                 nav.status.fully_resolved.alignment,
   3186                 .complete,
   3187             );
   3188             try w.writeAll(" = ");
   3189             try o.dg.renderValue(w, Value.fromInterned(variable.init), .StaticInitializer);
   3190             try w.writeByte(';');
   3191             try o.indent_writer.insertNewline();
   3192         },
   3193         else => try genDeclValue(
   3194             o,
   3195             Value.fromInterned(nav.status.fully_resolved.val),
   3196             .{ .nav = o.dg.pass.nav },
   3197             nav.status.fully_resolved.alignment,
   3198             nav.status.fully_resolved.@"linksection",
   3199         ),
   3200     }
   3201 }
   3202 
   3203 pub fn genDeclValue(
   3204     o: *Object,
   3205     val: Value,
   3206     decl_c_value: CValue,
   3207     alignment: Alignment,
   3208     @"linksection": InternPool.OptionalNullTerminatedString,
   3209 ) !void {
   3210     const zcu = o.dg.pt.zcu;
   3211     const ty = val.typeOf(zcu);
   3212 
   3213     const fwd = o.dg.fwdDeclWriter();
   3214     try fwd.writeAll("static ");
   3215     try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete);
   3216     try fwd.writeAll(";\n");
   3217 
   3218     const w = o.writer();
   3219     if (@"linksection".toSlice(&zcu.intern_pool)) |s|
   3220         try w.print("zig_linksection({f}) ", .{fmtStringLiteral(s, null)});
   3221     try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete);
   3222     try w.writeAll(" = ");
   3223     try o.dg.renderValue(w, val, .StaticInitializer);
   3224     try w.writeAll(";\n");
   3225 }
   3226 
   3227 pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const Zcu.Export.Index) !void {
   3228     const zcu = dg.pt.zcu;
   3229     const ip = &zcu.intern_pool;
   3230     const fwd = dg.fwdDeclWriter();
   3231 
   3232     const main_name = export_indices[0].ptr(zcu).opts.name;
   3233     try fwd.writeAll("#define ");
   3234     switch (exported) {
   3235         .nav => |nav| try dg.renderNavName(fwd, nav),
   3236         .uav => |uav| try DeclGen.renderUavName(fwd, Value.fromInterned(uav)),
   3237     }
   3238     try fwd.writeByte(' ');
   3239     try fwd.print("{f}", .{fmtIdentSolo(main_name.toSlice(ip))});
   3240     try fwd.writeByte('\n');
   3241 
   3242     const exported_val = exported.getValue(zcu);
   3243     if (ip.isFunctionType(exported_val.typeOf(zcu).toIntern())) return for (export_indices) |export_index| {
   3244         const @"export" = export_index.ptr(zcu);
   3245         try fwd.writeAll("zig_extern ");
   3246         if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn ");
   3247         try dg.renderFunctionSignature(
   3248             fwd,
   3249             exported.getValue(zcu),
   3250             exported.getAlign(zcu),
   3251             .forward,
   3252             .{ .@"export" = .{
   3253                 .main_name = main_name,
   3254                 .extern_name = @"export".opts.name,
   3255             } },
   3256         );
   3257         try fwd.writeAll(";\n");
   3258     };
   3259     const is_const = switch (ip.indexToKey(exported_val.toIntern())) {
   3260         .func => unreachable,
   3261         .@"extern" => |@"extern"| @"extern".is_const,
   3262         .variable => false,
   3263         else => true,
   3264     };
   3265     for (export_indices) |export_index| {
   3266         const @"export" = export_index.ptr(zcu);
   3267         try fwd.writeAll("zig_extern ");
   3268         if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage ");
   3269         if (@"export".opts.section.toSlice(ip)) |s| try fwd.print("zig_linksection({f}) ", .{
   3270             fmtStringLiteral(s, null),
   3271         });
   3272         const extern_name = @"export".opts.name.toSlice(ip);
   3273         const is_mangled = isMangledIdent(extern_name, true);
   3274         const is_export = @"export".opts.name != main_name;
   3275         try dg.renderTypeAndName(
   3276             fwd,
   3277             exported.getValue(zcu).typeOf(zcu),
   3278             .{ .identifier = extern_name },
   3279             CQualifiers.init(.{ .@"const" = is_const }),
   3280             exported.getAlign(zcu),
   3281             .complete,
   3282         );
   3283         if (is_mangled and is_export) {
   3284             try fwd.print(" zig_mangled_export({f}, {f}, {f})", .{
   3285                 fmtIdentSolo(extern_name),
   3286                 fmtStringLiteral(extern_name, null),
   3287                 fmtStringLiteral(main_name.toSlice(ip), null),
   3288             });
   3289         } else if (is_mangled) {
   3290             try fwd.print(" zig_mangled({f}, {f})", .{
   3291                 fmtIdentSolo(extern_name), fmtStringLiteral(extern_name, null),
   3292             });
   3293         } else if (is_export) {
   3294             try fwd.print(" zig_export({f}, {f})", .{
   3295                 fmtStringLiteral(main_name.toSlice(ip), null),
   3296                 fmtStringLiteral(extern_name, null),
   3297             });
   3298         }
   3299         try fwd.writeAll(";\n");
   3300     }
   3301 }
   3302 
   3303 /// Generate code for an entire body which ends with a `noreturn` instruction. The states of
   3304 /// `value_map` and `free_locals_map` are undefined after the generation, and new locals may not
   3305 /// have been added to `free_locals_map`. For a version of this function that restores this state,
   3306 /// see `genBodyResolveState`.
   3307 fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
   3308     const writer = f.object.writer();
   3309     if (body.len == 0) {
   3310         try writer.writeAll("{}");
   3311     } else {
   3312         try writer.writeAll("{\n");
   3313         f.object.indent_writer.pushIndent();
   3314         try genBodyInner(f, body);
   3315         f.object.indent_writer.popIndent();
   3316         try writer.writeByte('}');
   3317     }
   3318 }
   3319 
   3320 /// Generate code for an entire body which ends with a `noreturn` instruction. The states of
   3321 /// `value_map` and `free_locals_map` are restored to their original values, and any non-allocated
   3322 /// locals introduced within the body are correctly added to `free_locals_map`. Operands in
   3323 /// `leading_deaths` have their deaths processed before the body is generated.
   3324 /// A scope is introduced (using braces) only if `inner` is `false`.
   3325 /// If `leading_deaths` is empty, `inst` may be `undefined`.
   3326 fn genBodyResolveState(f: *Function, inst: Air.Inst.Index, leading_deaths: []const Air.Inst.Index, body: []const Air.Inst.Index, inner: bool) error{ AnalysisFail, OutOfMemory }!void {
   3327     if (body.len == 0) {
   3328         // Don't go to the expense of cloning everything!
   3329         if (!inner) try f.object.writer().writeAll("{}");
   3330         return;
   3331     }
   3332 
   3333     // TODO: we can probably avoid the copies in some other common cases too.
   3334 
   3335     const gpa = f.object.dg.gpa;
   3336 
   3337     // Save the original value_map and free_locals_map so that we can restore them after the body.
   3338     var old_value_map = try f.value_map.clone();
   3339     defer old_value_map.deinit();
   3340     var old_free_locals = try cloneFreeLocalsMap(gpa, &f.free_locals_map);
   3341     defer deinitFreeLocalsMap(gpa, &old_free_locals);
   3342 
   3343     // Remember how many locals there were before entering the body so that we can free any that
   3344     // were newly introduced. Any new locals must necessarily be logically free after the then
   3345     // branch is complete.
   3346     const pre_locals_len: LocalIndex = @intCast(f.locals.items.len);
   3347 
   3348     for (leading_deaths) |death| {
   3349         try die(f, inst, death.toRef());
   3350     }
   3351 
   3352     if (inner) {
   3353         try genBodyInner(f, body);
   3354     } else {
   3355         try genBody(f, body);
   3356     }
   3357 
   3358     f.value_map.deinit();
   3359     f.value_map = old_value_map.move();
   3360     deinitFreeLocalsMap(gpa, &f.free_locals_map);
   3361     f.free_locals_map = old_free_locals.move();
   3362 
   3363     // Now, use the lengths we stored earlier to detect any locals the body generated, and free
   3364     // them, unless they were used to store allocs.
   3365 
   3366     for (pre_locals_len..f.locals.items.len) |local_i| {
   3367         const local_index: LocalIndex = @intCast(local_i);
   3368         if (f.allocs.contains(local_index)) {
   3369             continue;
   3370         }
   3371         try freeLocal(f, inst, local_index, null);
   3372     }
   3373 }
   3374 
   3375 fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfMemory }!void {
   3376     const zcu = f.object.dg.pt.zcu;
   3377     const ip = &zcu.intern_pool;
   3378     const air_tags = f.air.instructions.items(.tag);
   3379     const air_datas = f.air.instructions.items(.data);
   3380 
   3381     for (body) |inst| {
   3382         if (f.object.dg.expected_block) |_|
   3383             return f.fail("runtime code not allowed in naked function", .{});
   3384         if (f.liveness.isUnused(inst) and !f.air.mustLower(inst, ip))
   3385             continue;
   3386 
   3387         const result_value = switch (air_tags[@intFromEnum(inst)]) {
   3388             // zig fmt: off
   3389             .inferred_alloc, .inferred_alloc_comptime => unreachable,
   3390 
   3391             .arg      => try airArg(f, inst),
   3392 
   3393             .breakpoint => try airBreakpoint(f.object.writer()),
   3394             .ret_addr   => try airRetAddr(f, inst),
   3395             .frame_addr => try airFrameAddress(f, inst),
   3396 
   3397             .ptr_add => try airPtrAddSub(f, inst, '+'),
   3398             .ptr_sub => try airPtrAddSub(f, inst, '-'),
   3399 
   3400             // TODO use a different strategy for add, sub, mul, div
   3401             // that communicates to the optimizer that wrapping is UB.
   3402             .add => try airBinOp(f, inst, "+", "add", .none),
   3403             .sub => try airBinOp(f, inst, "-", "sub", .none),
   3404             .mul => try airBinOp(f, inst, "*", "mul", .none),
   3405 
   3406             .neg => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "neg", .none),
   3407             .div_float => try airBinBuiltinCall(f, inst, "div", .none),
   3408 
   3409             .div_trunc, .div_exact => try airBinOp(f, inst, "/", "div_trunc", .none),
   3410             .rem => blk: {
   3411                 const bin_op = air_datas[@intFromEnum(inst)].bin_op;
   3412                 const lhs_scalar_ty = f.typeOf(bin_op.lhs).scalarType(zcu);
   3413                 // For binary operations @TypeOf(lhs)==@TypeOf(rhs),
   3414                 // so we only check one.
   3415                 break :blk if (lhs_scalar_ty.isInt(zcu))
   3416                     try airBinOp(f, inst, "%", "rem", .none)
   3417                 else
   3418                     try airBinBuiltinCall(f, inst, "fmod", .none);
   3419             },
   3420             .div_floor => try airBinBuiltinCall(f, inst, "div_floor", .none),
   3421             .mod       => try airBinBuiltinCall(f, inst, "mod", .none),
   3422             .abs       => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "abs", .none),
   3423 
   3424             .add_wrap => try airBinBuiltinCall(f, inst, "addw", .bits),
   3425             .sub_wrap => try airBinBuiltinCall(f, inst, "subw", .bits),
   3426             .mul_wrap => try airBinBuiltinCall(f, inst, "mulw", .bits),
   3427 
   3428             .add_sat => try airBinBuiltinCall(f, inst, "adds", .bits),
   3429             .sub_sat => try airBinBuiltinCall(f, inst, "subs", .bits),
   3430             .mul_sat => try airBinBuiltinCall(f, inst, "muls", .bits),
   3431             .shl_sat => try airBinBuiltinCall(f, inst, "shls", .bits),
   3432 
   3433             .sqrt        => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "sqrt", .none),
   3434             .sin         => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "sin", .none),
   3435             .cos         => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "cos", .none),
   3436             .tan         => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "tan", .none),
   3437             .exp         => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "exp", .none),
   3438             .exp2        => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "exp2", .none),
   3439             .log         => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "log", .none),
   3440             .log2        => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "log2", .none),
   3441             .log10       => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "log10", .none),
   3442             .floor       => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "floor", .none),
   3443             .ceil        => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "ceil", .none),
   3444             .round       => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "round", .none),
   3445             .trunc_float => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].un_op, "trunc", .none),
   3446 
   3447             .mul_add => try airMulAdd(f, inst),
   3448 
   3449             .add_with_overflow => try airOverflow(f, inst, "add", .bits),
   3450             .sub_with_overflow => try airOverflow(f, inst, "sub", .bits),
   3451             .mul_with_overflow => try airOverflow(f, inst, "mul", .bits),
   3452             .shl_with_overflow => try airOverflow(f, inst, "shl", .bits),
   3453 
   3454             .min => try airMinMax(f, inst, '<', "min"),
   3455             .max => try airMinMax(f, inst, '>', "max"),
   3456 
   3457             .slice => try airSlice(f, inst),
   3458 
   3459             .cmp_gt  => try airCmpOp(f, inst, air_datas[@intFromEnum(inst)].bin_op, .gt),
   3460             .cmp_gte => try airCmpOp(f, inst, air_datas[@intFromEnum(inst)].bin_op, .gte),
   3461             .cmp_lt  => try airCmpOp(f, inst, air_datas[@intFromEnum(inst)].bin_op, .lt),
   3462             .cmp_lte => try airCmpOp(f, inst, air_datas[@intFromEnum(inst)].bin_op, .lte),
   3463 
   3464             .cmp_eq  => try airEquality(f, inst, .eq),
   3465             .cmp_neq => try airEquality(f, inst, .neq),
   3466 
   3467             .cmp_vector => blk: {
   3468                 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl;
   3469                 const extra = f.air.extraData(Air.VectorCmp, ty_pl.payload).data;
   3470                 break :blk try airCmpOp(f, inst, extra, extra.compareOperator());
   3471             },
   3472             .cmp_lt_errors_len => try airCmpLtErrorsLen(f, inst),
   3473 
   3474             // bool_and and bool_or are non-short-circuit operations
   3475             .bool_and, .bit_and => try airBinOp(f, inst, "&",  "and", .none),
   3476             .bool_or,  .bit_or  => try airBinOp(f, inst, "|",  "or",  .none),
   3477             .xor                => try airBinOp(f, inst, "^",  "xor", .none),
   3478             .shr, .shr_exact    => try airBinBuiltinCall(f, inst, "shr", .none),
   3479             .shl,               => try airBinBuiltinCall(f, inst, "shlw", .bits),
   3480             .shl_exact          => try airBinOp(f, inst, "<<", "shl", .none),
   3481             .not                => try airNot  (f, inst),
   3482 
   3483             .optional_payload         => try airOptionalPayload(f, inst, false),
   3484             .optional_payload_ptr     => try airOptionalPayload(f, inst, true),
   3485             .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst),
   3486             .wrap_optional            => try airWrapOptional(f, inst),
   3487 
   3488             .is_err          => try airIsErr(f, inst, false, "!="),
   3489             .is_non_err      => try airIsErr(f, inst, false, "=="),
   3490             .is_err_ptr      => try airIsErr(f, inst, true, "!="),
   3491             .is_non_err_ptr  => try airIsErr(f, inst, true, "=="),
   3492 
   3493             .is_null         => try airIsNull(f, inst, .eq, false),
   3494             .is_non_null     => try airIsNull(f, inst, .neq, false),
   3495             .is_null_ptr     => try airIsNull(f, inst, .eq, true),
   3496             .is_non_null_ptr => try airIsNull(f, inst, .neq, true),
   3497 
   3498             .alloc            => try airAlloc(f, inst),
   3499             .ret_ptr          => try airRetPtr(f, inst),
   3500             .assembly         => try airAsm(f, inst),
   3501             .bitcast          => try airBitcast(f, inst),
   3502             .intcast          => try airIntCast(f, inst),
   3503             .trunc            => try airTrunc(f, inst),
   3504             .load             => try airLoad(f, inst),
   3505             .store            => try airStore(f, inst, false),
   3506             .store_safe       => try airStore(f, inst, true),
   3507             .struct_field_ptr => try airStructFieldPtr(f, inst),
   3508             .array_to_slice   => try airArrayToSlice(f, inst),
   3509             .cmpxchg_weak     => try airCmpxchg(f, inst, "weak"),
   3510             .cmpxchg_strong   => try airCmpxchg(f, inst, "strong"),
   3511             .atomic_rmw       => try airAtomicRmw(f, inst),
   3512             .atomic_load      => try airAtomicLoad(f, inst),
   3513             .memset           => try airMemset(f, inst, false),
   3514             .memset_safe      => try airMemset(f, inst, true),
   3515             .memcpy           => try airMemcpy(f, inst, "memcpy("),
   3516             .memmove          => try airMemcpy(f, inst, "memmove("),
   3517             .set_union_tag    => try airSetUnionTag(f, inst),
   3518             .get_union_tag    => try airGetUnionTag(f, inst),
   3519             .clz              => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "clz", .bits),
   3520             .ctz              => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "ctz", .bits),
   3521             .popcount         => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "popcount", .bits),
   3522             .byte_swap        => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "byte_swap", .bits),
   3523             .bit_reverse      => try airUnBuiltinCall(f, inst, air_datas[@intFromEnum(inst)].ty_op.operand, "bit_reverse", .bits),
   3524             .tag_name         => try airTagName(f, inst),
   3525             .error_name       => try airErrorName(f, inst),
   3526             .splat            => try airSplat(f, inst),
   3527             .select           => try airSelect(f, inst),
   3528             .shuffle_one      => try airShuffleOne(f, inst),
   3529             .shuffle_two      => try airShuffleTwo(f, inst),
   3530             .reduce           => try airReduce(f, inst),
   3531             .aggregate_init   => try airAggregateInit(f, inst),
   3532             .union_init       => try airUnionInit(f, inst),
   3533             .prefetch         => try airPrefetch(f, inst),
   3534             .addrspace_cast   => return f.fail("TODO: C backend: implement addrspace_cast", .{}),
   3535 
   3536             .@"try"       => try airTry(f, inst),
   3537             .try_cold     => try airTry(f, inst),
   3538             .try_ptr      => try airTryPtr(f, inst),
   3539             .try_ptr_cold => try airTryPtr(f, inst),
   3540 
   3541             .dbg_stmt => try airDbgStmt(f, inst),
   3542             .dbg_empty_stmt => try airDbgEmptyStmt(f, inst),
   3543             .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => try airDbgVar(f, inst),
   3544 
   3545             .float_from_int,
   3546             .int_from_float,
   3547             .fptrunc,
   3548             .fpext,
   3549             => try airFloatCast(f, inst),
   3550 
   3551             .atomic_store_unordered => try airAtomicStore(f, inst, toMemoryOrder(.unordered)),
   3552             .atomic_store_monotonic => try airAtomicStore(f, inst, toMemoryOrder(.monotonic)),
   3553             .atomic_store_release   => try airAtomicStore(f, inst, toMemoryOrder(.release)),
   3554             .atomic_store_seq_cst   => try airAtomicStore(f, inst, toMemoryOrder(.seq_cst)),
   3555 
   3556             .struct_field_ptr_index_0 => try airStructFieldPtrIndex(f, inst, 0),
   3557             .struct_field_ptr_index_1 => try airStructFieldPtrIndex(f, inst, 1),
   3558             .struct_field_ptr_index_2 => try airStructFieldPtrIndex(f, inst, 2),
   3559             .struct_field_ptr_index_3 => try airStructFieldPtrIndex(f, inst, 3),
   3560 
   3561             .field_parent_ptr => try airFieldParentPtr(f, inst),
   3562 
   3563             .struct_field_val => try airStructFieldVal(f, inst),
   3564             .slice_ptr        => try airSliceField(f, inst, false, "ptr"),
   3565             .slice_len        => try airSliceField(f, inst, false, "len"),
   3566 
   3567             .ptr_slice_ptr_ptr => try airSliceField(f, inst, true, "ptr"),
   3568             .ptr_slice_len_ptr => try airSliceField(f, inst, true, "len"),
   3569 
   3570             .ptr_elem_val       => try airPtrElemVal(f, inst),
   3571             .ptr_elem_ptr       => try airPtrElemPtr(f, inst),
   3572             .slice_elem_val     => try airSliceElemVal(f, inst),
   3573             .slice_elem_ptr     => try airSliceElemPtr(f, inst),
   3574             .array_elem_val     => try airArrayElemVal(f, inst),
   3575 
   3576             .unwrap_errunion_payload     => try airUnwrapErrUnionPay(f, inst, false),
   3577             .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, true),
   3578             .unwrap_errunion_err         => try airUnwrapErrUnionErr(f, inst),
   3579             .unwrap_errunion_err_ptr     => try airUnwrapErrUnionErr(f, inst),
   3580             .wrap_errunion_payload       => try airWrapErrUnionPay(f, inst),
   3581             .wrap_errunion_err           => try airWrapErrUnionErr(f, inst),
   3582             .errunion_payload_ptr_set    => try airErrUnionPayloadPtrSet(f, inst),
   3583             .err_return_trace            => try airErrReturnTrace(f, inst),
   3584             .set_err_return_trace        => try airSetErrReturnTrace(f, inst),
   3585             .save_err_return_trace_index => try airSaveErrReturnTraceIndex(f, inst),
   3586 
   3587             .wasm_memory_size => try airWasmMemorySize(f, inst),
   3588             .wasm_memory_grow => try airWasmMemoryGrow(f, inst),
   3589 
   3590             .add_optimized,
   3591             .sub_optimized,
   3592             .mul_optimized,
   3593             .div_float_optimized,
   3594             .div_trunc_optimized,
   3595             .div_floor_optimized,
   3596             .div_exact_optimized,
   3597             .rem_optimized,
   3598             .mod_optimized,
   3599             .neg_optimized,
   3600             .cmp_lt_optimized,
   3601             .cmp_lte_optimized,
   3602             .cmp_eq_optimized,
   3603             .cmp_gte_optimized,
   3604             .cmp_gt_optimized,
   3605             .cmp_neq_optimized,
   3606             .cmp_vector_optimized,
   3607             .reduce_optimized,
   3608             .int_from_float_optimized,
   3609             => return f.fail("TODO implement optimized float mode", .{}),
   3610 
   3611             .add_safe,
   3612             .sub_safe,
   3613             .mul_safe,
   3614             .intcast_safe,
   3615             .int_from_float_safe,
   3616             .int_from_float_optimized_safe,
   3617             => return f.fail("TODO implement safety_checked_instructions", .{}),
   3618 
   3619             .is_named_enum_value => return f.fail("TODO: C backend: implement is_named_enum_value", .{}),
   3620             .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}),
   3621             .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}),
   3622 
   3623             .runtime_nav_ptr => try airRuntimeNavPtr(f, inst),
   3624 
   3625             .c_va_start => try airCVaStart(f, inst),
   3626             .c_va_arg => try airCVaArg(f, inst),
   3627             .c_va_end => try airCVaEnd(f, inst),
   3628             .c_va_copy => try airCVaCopy(f, inst),
   3629 
   3630             .work_item_id,
   3631             .work_group_size,
   3632             .work_group_id,
   3633             => unreachable,
   3634 
   3635             // Instructions that are known to always be `noreturn` based on their tag.
   3636             .br              => return airBr(f, inst),
   3637             .repeat          => return airRepeat(f, inst),
   3638             .switch_dispatch => return airSwitchDispatch(f, inst),
   3639             .cond_br         => return airCondBr(f, inst),
   3640             .switch_br       => return airSwitchBr(f, inst, false),
   3641             .loop_switch_br  => return airSwitchBr(f, inst, true),
   3642             .loop            => return airLoop(f, inst),
   3643             .ret             => return airRet(f, inst, false),
   3644             .ret_safe        => return airRet(f, inst, false), // TODO
   3645             .ret_load        => return airRet(f, inst, true),
   3646             .trap            => return airTrap(f, f.object.writer()),
   3647             .unreach         => return airUnreach(f),
   3648 
   3649             // Instructions which may be `noreturn`.
   3650             .block => res: {
   3651                 const res = try airBlock(f, inst);
   3652                 if (f.typeOfIndex(inst).isNoReturn(zcu)) return;
   3653                 break :res res;
   3654             },
   3655             .dbg_inline_block => res: {
   3656                 const res = try airDbgInlineBlock(f, inst);
   3657                 if (f.typeOfIndex(inst).isNoReturn(zcu)) return;
   3658                 break :res res;
   3659             },
   3660             // TODO: calls should be in this category! The AIR we emit for them is a bit weird.
   3661             // The instruction has type `noreturn`, but there are instructions (and maybe a safety
   3662             // check) following nonetheless. The `unreachable` or safety check should be emitted by
   3663             // backends instead.
   3664             .call              => try airCall(f, inst, .auto),
   3665             .call_always_tail  => .none,
   3666             .call_never_tail   => try airCall(f, inst, .never_tail),
   3667             .call_never_inline => try airCall(f, inst, .never_inline),
   3668 
   3669             // zig fmt: on
   3670         };
   3671         if (result_value == .new_local) {
   3672             log.debug("map %{d} to t{d}", .{ inst, result_value.new_local });
   3673         }
   3674         try f.value_map.putNoClobber(inst.toRef(), switch (result_value) {
   3675             .none => continue,
   3676             .new_local => |local_index| .{ .local = local_index },
   3677             else => result_value,
   3678         });
   3679     }
   3680     unreachable;
   3681 }
   3682 
   3683 fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: []const u8) !CValue {
   3684     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   3685 
   3686     const inst_ty = f.typeOfIndex(inst);
   3687     const operand = try f.resolveInst(ty_op.operand);
   3688     try reap(f, inst, &.{ty_op.operand});
   3689 
   3690     const writer = f.object.writer();
   3691     const local = try f.allocLocal(inst, inst_ty);
   3692     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   3693     try f.writeCValue(writer, local, .Other);
   3694     try a.assign(f, writer);
   3695     if (is_ptr) {
   3696         try writer.writeByte('&');
   3697         try f.writeCValueDerefMember(writer, operand, .{ .identifier = field_name });
   3698     } else try f.writeCValueMember(writer, operand, .{ .identifier = field_name });
   3699     try a.end(f, writer);
   3700     return local;
   3701 }
   3702 
   3703 fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
   3704     const zcu = f.object.dg.pt.zcu;
   3705     const inst_ty = f.typeOfIndex(inst);
   3706     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   3707     if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   3708         try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   3709         return .none;
   3710     }
   3711 
   3712     const ptr = try f.resolveInst(bin_op.lhs);
   3713     const index = try f.resolveInst(bin_op.rhs);
   3714     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   3715 
   3716     const writer = f.object.writer();
   3717     const local = try f.allocLocal(inst, inst_ty);
   3718     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   3719     try f.writeCValue(writer, local, .Other);
   3720     try a.assign(f, writer);
   3721     try f.writeCValue(writer, ptr, .Other);
   3722     try writer.writeByte('[');
   3723     try f.writeCValue(writer, index, .Other);
   3724     try writer.writeByte(']');
   3725     try a.end(f, writer);
   3726     return local;
   3727 }
   3728 
   3729 fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
   3730     const pt = f.object.dg.pt;
   3731     const zcu = pt.zcu;
   3732     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3733     const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
   3734 
   3735     const inst_ty = f.typeOfIndex(inst);
   3736     const ptr_ty = f.typeOf(bin_op.lhs);
   3737     const elem_has_bits = ptr_ty.elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu);
   3738 
   3739     const ptr = try f.resolveInst(bin_op.lhs);
   3740     const index = try f.resolveInst(bin_op.rhs);
   3741     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   3742 
   3743     const writer = f.object.writer();
   3744     const local = try f.allocLocal(inst, inst_ty);
   3745     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   3746     try f.writeCValue(writer, local, .Other);
   3747     try a.assign(f, writer);
   3748     try writer.writeByte('(');
   3749     try f.renderType(writer, inst_ty);
   3750     try writer.writeByte(')');
   3751     if (elem_has_bits) try writer.writeByte('&');
   3752     if (elem_has_bits and ptr_ty.ptrSize(zcu) == .one) {
   3753         // It's a pointer to an array, so we need to de-reference.
   3754         try f.writeCValueDeref(writer, ptr);
   3755     } else try f.writeCValue(writer, ptr, .Other);
   3756     if (elem_has_bits) {
   3757         try writer.writeByte('[');
   3758         try f.writeCValue(writer, index, .Other);
   3759         try writer.writeByte(']');
   3760     }
   3761     try a.end(f, writer);
   3762     return local;
   3763 }
   3764 
   3765 fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
   3766     const zcu = f.object.dg.pt.zcu;
   3767     const inst_ty = f.typeOfIndex(inst);
   3768     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   3769     if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   3770         try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   3771         return .none;
   3772     }
   3773 
   3774     const slice = try f.resolveInst(bin_op.lhs);
   3775     const index = try f.resolveInst(bin_op.rhs);
   3776     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   3777 
   3778     const writer = f.object.writer();
   3779     const local = try f.allocLocal(inst, inst_ty);
   3780     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   3781     try f.writeCValue(writer, local, .Other);
   3782     try a.assign(f, writer);
   3783     try f.writeCValueMember(writer, slice, .{ .identifier = "ptr" });
   3784     try writer.writeByte('[');
   3785     try f.writeCValue(writer, index, .Other);
   3786     try writer.writeByte(']');
   3787     try a.end(f, writer);
   3788     return local;
   3789 }
   3790 
   3791 fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue {
   3792     const pt = f.object.dg.pt;
   3793     const zcu = pt.zcu;
   3794     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3795     const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
   3796 
   3797     const inst_ty = f.typeOfIndex(inst);
   3798     const slice_ty = f.typeOf(bin_op.lhs);
   3799     const elem_ty = slice_ty.elemType2(zcu);
   3800     const elem_has_bits = elem_ty.hasRuntimeBitsIgnoreComptime(zcu);
   3801 
   3802     const slice = try f.resolveInst(bin_op.lhs);
   3803     const index = try f.resolveInst(bin_op.rhs);
   3804     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   3805 
   3806     const writer = f.object.writer();
   3807     const local = try f.allocLocal(inst, inst_ty);
   3808     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   3809     try f.writeCValue(writer, local, .Other);
   3810     try a.assign(f, writer);
   3811     if (elem_has_bits) try writer.writeByte('&');
   3812     try f.writeCValueMember(writer, slice, .{ .identifier = "ptr" });
   3813     if (elem_has_bits) {
   3814         try writer.writeByte('[');
   3815         try f.writeCValue(writer, index, .Other);
   3816         try writer.writeByte(']');
   3817     }
   3818     try a.end(f, writer);
   3819     return local;
   3820 }
   3821 
   3822 fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue {
   3823     const zcu = f.object.dg.pt.zcu;
   3824     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   3825     const inst_ty = f.typeOfIndex(inst);
   3826     if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   3827         try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   3828         return .none;
   3829     }
   3830 
   3831     const array = try f.resolveInst(bin_op.lhs);
   3832     const index = try f.resolveInst(bin_op.rhs);
   3833     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   3834 
   3835     const writer = f.object.writer();
   3836     const local = try f.allocLocal(inst, inst_ty);
   3837     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   3838     try f.writeCValue(writer, local, .Other);
   3839     try a.assign(f, writer);
   3840     try f.writeCValue(writer, array, .Other);
   3841     try writer.writeByte('[');
   3842     try f.writeCValue(writer, index, .Other);
   3843     try writer.writeByte(']');
   3844     try a.end(f, writer);
   3845     return local;
   3846 }
   3847 
   3848 fn airAlloc(f: *Function, inst: Air.Inst.Index) !CValue {
   3849     const pt = f.object.dg.pt;
   3850     const zcu = pt.zcu;
   3851     const inst_ty = f.typeOfIndex(inst);
   3852     const elem_ty = inst_ty.childType(zcu);
   3853     if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return .{ .undef = inst_ty };
   3854 
   3855     const local = try f.allocLocalValue(.{
   3856         .ctype = try f.ctypeFromType(elem_ty, .complete),
   3857         .alignas = CType.AlignAs.fromAlignment(.{
   3858             .@"align" = inst_ty.ptrInfo(zcu).flags.alignment,
   3859             .abi = elem_ty.abiAlignment(zcu),
   3860         }),
   3861     });
   3862     log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
   3863     try f.allocs.put(zcu.gpa, local.new_local, true);
   3864     return .{ .local_ref = local.new_local };
   3865 }
   3866 
   3867 fn airRetPtr(f: *Function, inst: Air.Inst.Index) !CValue {
   3868     const pt = f.object.dg.pt;
   3869     const zcu = pt.zcu;
   3870     const inst_ty = f.typeOfIndex(inst);
   3871     const elem_ty = inst_ty.childType(zcu);
   3872     if (!elem_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return .{ .undef = inst_ty };
   3873 
   3874     const local = try f.allocLocalValue(.{
   3875         .ctype = try f.ctypeFromType(elem_ty, .complete),
   3876         .alignas = CType.AlignAs.fromAlignment(.{
   3877             .@"align" = inst_ty.ptrInfo(zcu).flags.alignment,
   3878             .abi = elem_ty.abiAlignment(zcu),
   3879         }),
   3880     });
   3881     log.debug("%{d}: allocated unfreeable t{d}", .{ inst, local.new_local });
   3882     try f.allocs.put(zcu.gpa, local.new_local, true);
   3883     return .{ .local_ref = local.new_local };
   3884 }
   3885 
   3886 fn airArg(f: *Function, inst: Air.Inst.Index) !CValue {
   3887     const inst_ty = f.typeOfIndex(inst);
   3888     const inst_ctype = try f.ctypeFromType(inst_ty, .parameter);
   3889 
   3890     const i = f.next_arg_index;
   3891     f.next_arg_index += 1;
   3892     const result: CValue = if (inst_ctype.eql(try f.ctypeFromType(inst_ty, .complete)))
   3893         .{ .arg = i }
   3894     else
   3895         .{ .arg_array = i };
   3896 
   3897     if (f.liveness.isUnused(inst)) {
   3898         const writer = f.object.writer();
   3899         try writer.writeByte('(');
   3900         try f.renderType(writer, .void);
   3901         try writer.writeByte(')');
   3902         try f.writeCValue(writer, result, .Other);
   3903         try writer.writeAll(";\n");
   3904         return .none;
   3905     }
   3906 
   3907     return result;
   3908 }
   3909 
   3910 fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
   3911     const pt = f.object.dg.pt;
   3912     const zcu = pt.zcu;
   3913     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   3914 
   3915     const ptr_ty = f.typeOf(ty_op.operand);
   3916     const ptr_scalar_ty = ptr_ty.scalarType(zcu);
   3917     const ptr_info = ptr_scalar_ty.ptrInfo(zcu);
   3918     const src_ty: Type = .fromInterned(ptr_info.child);
   3919 
   3920     if (!src_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   3921         try reap(f, inst, &.{ty_op.operand});
   3922         return .none;
   3923     }
   3924 
   3925     const operand = try f.resolveInst(ty_op.operand);
   3926 
   3927     try reap(f, inst, &.{ty_op.operand});
   3928 
   3929     const is_aligned = if (ptr_info.flags.alignment != .none)
   3930         ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)
   3931     else
   3932         true;
   3933     const is_array = lowersToArray(src_ty, pt);
   3934     const need_memcpy = !is_aligned or is_array;
   3935 
   3936     const writer = f.object.writer();
   3937     const local = try f.allocLocal(inst, src_ty);
   3938     const v = try Vectorize.start(f, inst, writer, ptr_ty);
   3939 
   3940     if (need_memcpy) {
   3941         try writer.writeAll("memcpy(");
   3942         if (!is_array) try writer.writeByte('&');
   3943         try f.writeCValue(writer, local, .Other);
   3944         try v.elem(f, writer);
   3945         try writer.writeAll(", (const char *)");
   3946         try f.writeCValue(writer, operand, .Other);
   3947         try v.elem(f, writer);
   3948         try writer.writeAll(", sizeof(");
   3949         try f.renderType(writer, src_ty);
   3950         try writer.writeAll("))");
   3951     } else if (ptr_info.packed_offset.host_size > 0 and ptr_info.flags.vector_index == .none) {
   3952         const host_bits: u16 = ptr_info.packed_offset.host_size * 8;
   3953         const host_ty = try pt.intType(.unsigned, host_bits);
   3954 
   3955         const bit_offset_ty = try pt.intType(.unsigned, Type.smallestUnsignedBits(host_bits - 1));
   3956         const bit_offset_val = try pt.intValue(bit_offset_ty, ptr_info.packed_offset.bit_offset);
   3957 
   3958         const field_ty = try pt.intType(.unsigned, @as(u16, @intCast(src_ty.bitSize(zcu))));
   3959 
   3960         try f.writeCValue(writer, local, .Other);
   3961         try v.elem(f, writer);
   3962         try writer.writeAll(" = (");
   3963         try f.renderType(writer, src_ty);
   3964         try writer.writeAll(")zig_wrap_");
   3965         try f.object.dg.renderTypeForBuiltinFnName(writer, field_ty);
   3966         try writer.writeAll("((");
   3967         try f.renderType(writer, field_ty);
   3968         try writer.writeByte(')');
   3969         const cant_cast = host_ty.isInt(zcu) and host_ty.bitSize(zcu) > 64;
   3970         if (cant_cast) {
   3971             if (field_ty.bitSize(zcu) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{});
   3972             try writer.writeAll("zig_lo_");
   3973             try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
   3974             try writer.writeByte('(');
   3975         }
   3976         try writer.writeAll("zig_shr_");
   3977         try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
   3978         try writer.writeByte('(');
   3979         try f.writeCValueDeref(writer, operand);
   3980         try v.elem(f, writer);
   3981         try writer.print(", {f})", .{try f.fmtIntLiteralDec(bit_offset_val)});
   3982         if (cant_cast) try writer.writeByte(')');
   3983         try f.object.dg.renderBuiltinInfo(writer, field_ty, .bits);
   3984         try writer.writeByte(')');
   3985     } else {
   3986         try f.writeCValue(writer, local, .Other);
   3987         try v.elem(f, writer);
   3988         try writer.writeAll(" = ");
   3989         try f.writeCValueDeref(writer, operand);
   3990         try v.elem(f, writer);
   3991     }
   3992     try writer.writeAll(";\n");
   3993     try v.end(f, inst, writer);
   3994 
   3995     return local;
   3996 }
   3997 
   3998 fn airRet(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !void {
   3999     const pt = f.object.dg.pt;
   4000     const zcu = pt.zcu;
   4001     const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4002     const writer = f.object.writer();
   4003     const op_inst = un_op.toIndex();
   4004     const op_ty = f.typeOf(un_op);
   4005     const ret_ty = if (is_ptr) op_ty.childType(zcu) else op_ty;
   4006     const ret_ctype = try f.ctypeFromType(ret_ty, .parameter);
   4007 
   4008     if (op_inst != null and f.air.instructions.items(.tag)[@intFromEnum(op_inst.?)] == .call_always_tail) {
   4009         try reap(f, inst, &.{un_op});
   4010         _ = try airCall(f, op_inst.?, .always_tail);
   4011     } else if (ret_ctype.index != .void) {
   4012         const operand = try f.resolveInst(un_op);
   4013         try reap(f, inst, &.{un_op});
   4014         var deref = is_ptr;
   4015         const is_array = lowersToArray(ret_ty, pt);
   4016         const ret_val = if (is_array) ret_val: {
   4017             const array_local = try f.allocAlignedLocal(inst, .{
   4018                 .ctype = ret_ctype,
   4019                 .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(zcu)),
   4020             });
   4021             try writer.writeAll("memcpy(");
   4022             try f.writeCValueMember(writer, array_local, .{ .identifier = "array" });
   4023             try writer.writeAll(", ");
   4024             if (deref)
   4025                 try f.writeCValueDeref(writer, operand)
   4026             else
   4027                 try f.writeCValue(writer, operand, .FunctionArgument);
   4028             deref = false;
   4029             try writer.writeAll(", sizeof(");
   4030             try f.renderType(writer, ret_ty);
   4031             try writer.writeAll("));\n");
   4032             break :ret_val array_local;
   4033         } else operand;
   4034 
   4035         try writer.writeAll("return ");
   4036         if (deref)
   4037             try f.writeCValueDeref(writer, ret_val)
   4038         else
   4039             try f.writeCValue(writer, ret_val, .Other);
   4040         try writer.writeAll(";\n");
   4041         if (is_array) {
   4042             try freeLocal(f, inst, ret_val.new_local, null);
   4043         }
   4044     } else {
   4045         try reap(f, inst, &.{un_op});
   4046         // Not even allowed to return void in a naked function.
   4047         if (!f.object.dg.is_naked_fn) try writer.writeAll("return;\n");
   4048     }
   4049 }
   4050 
   4051 fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue {
   4052     const pt = f.object.dg.pt;
   4053     const zcu = pt.zcu;
   4054     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4055 
   4056     const operand = try f.resolveInst(ty_op.operand);
   4057     try reap(f, inst, &.{ty_op.operand});
   4058 
   4059     const inst_ty = f.typeOfIndex(inst);
   4060     const inst_scalar_ty = inst_ty.scalarType(zcu);
   4061     const operand_ty = f.typeOf(ty_op.operand);
   4062     const scalar_ty = operand_ty.scalarType(zcu);
   4063 
   4064     if (f.object.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand);
   4065 
   4066     const writer = f.object.writer();
   4067     const local = try f.allocLocal(inst, inst_ty);
   4068     const v = try Vectorize.start(f, inst, writer, operand_ty);
   4069     const a = try Assignment.start(f, writer, try f.ctypeFromType(scalar_ty, .complete));
   4070     try f.writeCValue(writer, local, .Other);
   4071     try v.elem(f, writer);
   4072     try a.assign(f, writer);
   4073     try f.renderIntCast(writer, inst_scalar_ty, operand, v, scalar_ty, .Other);
   4074     try a.end(f, writer);
   4075     try v.end(f, inst, writer);
   4076     return local;
   4077 }
   4078 
   4079 fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue {
   4080     const pt = f.object.dg.pt;
   4081     const zcu = pt.zcu;
   4082     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4083 
   4084     const operand = try f.resolveInst(ty_op.operand);
   4085     try reap(f, inst, &.{ty_op.operand});
   4086 
   4087     const inst_ty = f.typeOfIndex(inst);
   4088     const inst_scalar_ty = inst_ty.scalarType(zcu);
   4089     const dest_int_info = inst_scalar_ty.intInfo(zcu);
   4090     const dest_bits = dest_int_info.bits;
   4091     const dest_c_bits = toCIntBits(dest_bits) orelse
   4092         return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
   4093     const operand_ty = f.typeOf(ty_op.operand);
   4094     const scalar_ty = operand_ty.scalarType(zcu);
   4095     const scalar_int_info = scalar_ty.intInfo(zcu);
   4096 
   4097     const need_cast = dest_c_bits < 64;
   4098     const need_lo = scalar_int_info.bits > 64 and dest_bits <= 64;
   4099     const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits);
   4100     if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand);
   4101 
   4102     const writer = f.object.writer();
   4103     const local = try f.allocLocal(inst, inst_ty);
   4104     const v = try Vectorize.start(f, inst, writer, operand_ty);
   4105     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_scalar_ty, .complete));
   4106     try f.writeCValue(writer, local, .Other);
   4107     try v.elem(f, writer);
   4108     try a.assign(f, writer);
   4109     if (need_cast) {
   4110         try writer.writeByte('(');
   4111         try f.renderType(writer, inst_scalar_ty);
   4112         try writer.writeByte(')');
   4113     }
   4114     if (need_lo) {
   4115         try writer.writeAll("zig_lo_");
   4116         try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
   4117         try writer.writeByte('(');
   4118     }
   4119     if (!need_mask) {
   4120         try f.writeCValue(writer, operand, .Other);
   4121         try v.elem(f, writer);
   4122     } else switch (dest_int_info.signedness) {
   4123         .unsigned => {
   4124             try writer.writeAll("zig_and_");
   4125             try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
   4126             try writer.writeByte('(');
   4127             try f.writeCValue(writer, operand, .FunctionArgument);
   4128             try v.elem(f, writer);
   4129             try writer.print(", {f})", .{
   4130                 try f.fmtIntLiteralHex(try inst_scalar_ty.maxIntScalar(pt, scalar_ty)),
   4131             });
   4132         },
   4133         .signed => {
   4134             const c_bits = toCIntBits(scalar_int_info.bits) orelse
   4135                 return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
   4136             const shift_val = try pt.intValue(.u8, c_bits - dest_bits);
   4137 
   4138             try writer.writeAll("zig_shr_");
   4139             try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
   4140             if (c_bits == 128) {
   4141                 try writer.print("(zig_bitCast_i{d}(", .{c_bits});
   4142             } else {
   4143                 try writer.print("((int{d}_t)", .{c_bits});
   4144             }
   4145             try writer.print("zig_shl_u{d}(", .{c_bits});
   4146             if (c_bits == 128) {
   4147                 try writer.print("zig_bitCast_u{d}(", .{c_bits});
   4148             } else {
   4149                 try writer.print("(uint{d}_t)", .{c_bits});
   4150             }
   4151             try f.writeCValue(writer, operand, .FunctionArgument);
   4152             try v.elem(f, writer);
   4153             if (c_bits == 128) try writer.writeByte(')');
   4154             try writer.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)});
   4155             if (c_bits == 128) try writer.writeByte(')');
   4156             try writer.print(", {f})", .{try f.fmtIntLiteralDec(shift_val)});
   4157         },
   4158     }
   4159     if (need_lo) try writer.writeByte(')');
   4160     try a.end(f, writer);
   4161     try v.end(f, inst, writer);
   4162     return local;
   4163 }
   4164 
   4165 fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
   4166     const pt = f.object.dg.pt;
   4167     const zcu = pt.zcu;
   4168     // *a = b;
   4169     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4170 
   4171     const ptr_ty = f.typeOf(bin_op.lhs);
   4172     const ptr_scalar_ty = ptr_ty.scalarType(zcu);
   4173     const ptr_info = ptr_scalar_ty.ptrInfo(zcu);
   4174 
   4175     const ptr_val = try f.resolveInst(bin_op.lhs);
   4176     const src_ty = f.typeOf(bin_op.rhs);
   4177 
   4178     const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |v| v.isUndefDeep(zcu) else false;
   4179 
   4180     if (val_is_undef) {
   4181         try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   4182         if (safety and ptr_info.packed_offset.host_size == 0) {
   4183             const writer = f.object.writer();
   4184             try writer.writeAll("memset(");
   4185             try f.writeCValue(writer, ptr_val, .FunctionArgument);
   4186             try writer.writeAll(", 0xaa, sizeof(");
   4187             try f.renderType(writer, .fromInterned(ptr_info.child));
   4188             try writer.writeAll("));\n");
   4189         }
   4190         return .none;
   4191     }
   4192 
   4193     const is_aligned = if (ptr_info.flags.alignment != .none)
   4194         ptr_info.flags.alignment.order(src_ty.abiAlignment(zcu)).compare(.gte)
   4195     else
   4196         true;
   4197     const is_array = lowersToArray(.fromInterned(ptr_info.child), pt);
   4198     const need_memcpy = !is_aligned or is_array;
   4199 
   4200     const src_val = try f.resolveInst(bin_op.rhs);
   4201     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   4202 
   4203     const src_scalar_ctype = try f.ctypeFromType(src_ty.scalarType(zcu), .complete);
   4204     const writer = f.object.writer();
   4205     if (need_memcpy) {
   4206         // For this memcpy to safely work we need the rhs to have the same
   4207         // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
   4208         assert(src_ty.eql(.fromInterned(ptr_info.child), zcu));
   4209 
   4210         // If the source is a constant, writeCValue will emit a brace initialization
   4211         // so work around this by initializing into new local.
   4212         // TODO this should be done by manually initializing elements of the dest array
   4213         const array_src = if (src_val == .constant) blk: {
   4214             const new_local = try f.allocLocal(inst, src_ty);
   4215             try f.writeCValue(writer, new_local, .Other);
   4216             try writer.writeAll(" = ");
   4217             try f.writeCValue(writer, src_val, .Other);
   4218             try writer.writeAll(";\n");
   4219 
   4220             break :blk new_local;
   4221         } else src_val;
   4222 
   4223         const v = try Vectorize.start(f, inst, writer, ptr_ty);
   4224         try writer.writeAll("memcpy((char *)");
   4225         try f.writeCValue(writer, ptr_val, .FunctionArgument);
   4226         try v.elem(f, writer);
   4227         try writer.writeAll(", ");
   4228         if (!is_array) try writer.writeByte('&');
   4229         try f.writeCValue(writer, array_src, .FunctionArgument);
   4230         try v.elem(f, writer);
   4231         try writer.writeAll(", sizeof(");
   4232         try f.renderType(writer, src_ty);
   4233         try writer.writeAll("))");
   4234         try f.freeCValue(inst, array_src);
   4235         try writer.writeAll(";\n");
   4236         try v.end(f, inst, writer);
   4237     } else if (ptr_info.packed_offset.host_size > 0 and ptr_info.flags.vector_index == .none) {
   4238         const host_bits = ptr_info.packed_offset.host_size * 8;
   4239         const host_ty = try pt.intType(.unsigned, host_bits);
   4240 
   4241         const bit_offset_ty = try pt.intType(.unsigned, Type.smallestUnsignedBits(host_bits - 1));
   4242         const bit_offset_val = try pt.intValue(bit_offset_ty, ptr_info.packed_offset.bit_offset);
   4243 
   4244         const src_bits = src_ty.bitSize(zcu);
   4245 
   4246         const ExpectedContents = [BigInt.Managed.default_capacity]BigIntLimb;
   4247         var stack align(@alignOf(ExpectedContents)) =
   4248             std.heap.stackFallback(@sizeOf(ExpectedContents), f.object.dg.gpa);
   4249 
   4250         var mask = try BigInt.Managed.initCapacity(stack.get(), BigInt.calcTwosCompLimbCount(host_bits));
   4251         defer mask.deinit();
   4252 
   4253         try mask.setTwosCompIntLimit(.max, .unsigned, @as(usize, @intCast(src_bits)));
   4254         try mask.shiftLeft(&mask, ptr_info.packed_offset.bit_offset);
   4255         try mask.bitNotWrap(&mask, .unsigned, host_bits);
   4256 
   4257         const mask_val = try pt.intValue_big(host_ty, mask.toConst());
   4258 
   4259         const v = try Vectorize.start(f, inst, writer, ptr_ty);
   4260         const a = try Assignment.start(f, writer, src_scalar_ctype);
   4261         try f.writeCValueDeref(writer, ptr_val);
   4262         try v.elem(f, writer);
   4263         try a.assign(f, writer);
   4264         try writer.writeAll("zig_or_");
   4265         try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
   4266         try writer.writeAll("(zig_and_");
   4267         try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
   4268         try writer.writeByte('(');
   4269         try f.writeCValueDeref(writer, ptr_val);
   4270         try v.elem(f, writer);
   4271         try writer.print(", {f}), zig_shl_", .{try f.fmtIntLiteralHex(mask_val)});
   4272         try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
   4273         try writer.writeByte('(');
   4274         const cant_cast = host_ty.isInt(zcu) and host_ty.bitSize(zcu) > 64;
   4275         if (cant_cast) {
   4276             if (src_ty.bitSize(zcu) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{});
   4277             try writer.writeAll("zig_make_");
   4278             try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty);
   4279             try writer.writeAll("(0, ");
   4280         } else {
   4281             try writer.writeByte('(');
   4282             try f.renderType(writer, host_ty);
   4283             try writer.writeByte(')');
   4284         }
   4285 
   4286         if (src_ty.isPtrAtRuntime(zcu)) {
   4287             try writer.writeByte('(');
   4288             try f.renderType(writer, .usize);
   4289             try writer.writeByte(')');
   4290         }
   4291         try f.writeCValue(writer, src_val, .Other);
   4292         try v.elem(f, writer);
   4293         if (cant_cast) try writer.writeByte(')');
   4294         try writer.print(", {f}))", .{try f.fmtIntLiteralDec(bit_offset_val)});
   4295         try a.end(f, writer);
   4296         try v.end(f, inst, writer);
   4297     } else {
   4298         switch (ptr_val) {
   4299             .local_ref => |ptr_local_index| switch (src_val) {
   4300                 .new_local, .local => |src_local_index| if (ptr_local_index == src_local_index)
   4301                     return .none,
   4302                 else => {},
   4303             },
   4304             else => {},
   4305         }
   4306         const v = try Vectorize.start(f, inst, writer, ptr_ty);
   4307         const a = try Assignment.start(f, writer, src_scalar_ctype);
   4308         try f.writeCValueDeref(writer, ptr_val);
   4309         try v.elem(f, writer);
   4310         try a.assign(f, writer);
   4311         try f.writeCValue(writer, src_val, .Other);
   4312         try v.elem(f, writer);
   4313         try a.end(f, writer);
   4314         try v.end(f, inst, writer);
   4315     }
   4316     return .none;
   4317 }
   4318 
   4319 fn airOverflow(f: *Function, inst: Air.Inst.Index, operation: []const u8, info: BuiltinInfo) !CValue {
   4320     const pt = f.object.dg.pt;
   4321     const zcu = pt.zcu;
   4322     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4323     const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
   4324 
   4325     const lhs = try f.resolveInst(bin_op.lhs);
   4326     const rhs = try f.resolveInst(bin_op.rhs);
   4327     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   4328 
   4329     const inst_ty = f.typeOfIndex(inst);
   4330     const operand_ty = f.typeOf(bin_op.lhs);
   4331     const scalar_ty = operand_ty.scalarType(zcu);
   4332 
   4333     const w = f.object.writer();
   4334     const local = try f.allocLocal(inst, inst_ty);
   4335     const v = try Vectorize.start(f, inst, w, operand_ty);
   4336     try f.writeCValueMember(w, local, .{ .field = 1 });
   4337     try v.elem(f, w);
   4338     try w.writeAll(" = zig_");
   4339     try w.writeAll(operation);
   4340     try w.writeAll("o_");
   4341     try f.object.dg.renderTypeForBuiltinFnName(w, scalar_ty);
   4342     try w.writeAll("(&");
   4343     try f.writeCValueMember(w, local, .{ .field = 0 });
   4344     try v.elem(f, w);
   4345     try w.writeAll(", ");
   4346     try f.writeCValue(w, lhs, .FunctionArgument);
   4347     try v.elem(f, w);
   4348     try w.writeAll(", ");
   4349     try f.writeCValue(w, rhs, .FunctionArgument);
   4350     if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, w);
   4351     try f.object.dg.renderBuiltinInfo(w, scalar_ty, info);
   4352     try w.writeAll(");\n");
   4353     try v.end(f, inst, w);
   4354 
   4355     return local;
   4356 }
   4357 
   4358 fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
   4359     const pt = f.object.dg.pt;
   4360     const zcu = pt.zcu;
   4361     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4362     const operand_ty = f.typeOf(ty_op.operand);
   4363     const scalar_ty = operand_ty.scalarType(zcu);
   4364     if (scalar_ty.toIntern() != .bool_type) return try airUnBuiltinCall(f, inst, ty_op.operand, "not", .bits);
   4365 
   4366     const op = try f.resolveInst(ty_op.operand);
   4367     try reap(f, inst, &.{ty_op.operand});
   4368 
   4369     const inst_ty = f.typeOfIndex(inst);
   4370 
   4371     const writer = f.object.writer();
   4372     const local = try f.allocLocal(inst, inst_ty);
   4373     const v = try Vectorize.start(f, inst, writer, operand_ty);
   4374     try f.writeCValue(writer, local, .Other);
   4375     try v.elem(f, writer);
   4376     try writer.writeAll(" = ");
   4377     try writer.writeByte('!');
   4378     try f.writeCValue(writer, op, .Other);
   4379     try v.elem(f, writer);
   4380     try writer.writeAll(";\n");
   4381     try v.end(f, inst, writer);
   4382 
   4383     return local;
   4384 }
   4385 
   4386 fn airBinOp(
   4387     f: *Function,
   4388     inst: Air.Inst.Index,
   4389     operator: []const u8,
   4390     operation: []const u8,
   4391     info: BuiltinInfo,
   4392 ) !CValue {
   4393     const pt = f.object.dg.pt;
   4394     const zcu = pt.zcu;
   4395     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4396     const operand_ty = f.typeOf(bin_op.lhs);
   4397     const scalar_ty = operand_ty.scalarType(zcu);
   4398     if ((scalar_ty.isInt(zcu) and scalar_ty.bitSize(zcu) > 64) or scalar_ty.isRuntimeFloat())
   4399         return try airBinBuiltinCall(f, inst, operation, info);
   4400 
   4401     const lhs = try f.resolveInst(bin_op.lhs);
   4402     const rhs = try f.resolveInst(bin_op.rhs);
   4403     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   4404 
   4405     const inst_ty = f.typeOfIndex(inst);
   4406 
   4407     const writer = f.object.writer();
   4408     const local = try f.allocLocal(inst, inst_ty);
   4409     const v = try Vectorize.start(f, inst, writer, operand_ty);
   4410     try f.writeCValue(writer, local, .Other);
   4411     try v.elem(f, writer);
   4412     try writer.writeAll(" = ");
   4413     try f.writeCValue(writer, lhs, .Other);
   4414     try v.elem(f, writer);
   4415     try writer.writeByte(' ');
   4416     try writer.writeAll(operator);
   4417     try writer.writeByte(' ');
   4418     try f.writeCValue(writer, rhs, .Other);
   4419     try v.elem(f, writer);
   4420     try writer.writeAll(";\n");
   4421     try v.end(f, inst, writer);
   4422 
   4423     return local;
   4424 }
   4425 
   4426 fn airCmpOp(
   4427     f: *Function,
   4428     inst: Air.Inst.Index,
   4429     data: anytype,
   4430     operator: std.math.CompareOperator,
   4431 ) !CValue {
   4432     const pt = f.object.dg.pt;
   4433     const zcu = pt.zcu;
   4434     const lhs_ty = f.typeOf(data.lhs);
   4435     const scalar_ty = lhs_ty.scalarType(zcu);
   4436 
   4437     const scalar_bits = scalar_ty.bitSize(zcu);
   4438     if (scalar_ty.isInt(zcu) and scalar_bits > 64)
   4439         return airCmpBuiltinCall(
   4440             f,
   4441             inst,
   4442             data,
   4443             operator,
   4444             .cmp,
   4445             if (scalar_bits > 128) .bits else .none,
   4446         );
   4447     if (scalar_ty.isRuntimeFloat())
   4448         return airCmpBuiltinCall(f, inst, data, operator, .operator, .none);
   4449 
   4450     const inst_ty = f.typeOfIndex(inst);
   4451     const lhs = try f.resolveInst(data.lhs);
   4452     const rhs = try f.resolveInst(data.rhs);
   4453     try reap(f, inst, &.{ data.lhs, data.rhs });
   4454 
   4455     const rhs_ty = f.typeOf(data.rhs);
   4456     const need_cast = lhs_ty.isSinglePointer(zcu) or rhs_ty.isSinglePointer(zcu);
   4457     const writer = f.object.writer();
   4458     const local = try f.allocLocal(inst, inst_ty);
   4459     const v = try Vectorize.start(f, inst, writer, lhs_ty);
   4460     const a = try Assignment.start(f, writer, try f.ctypeFromType(scalar_ty, .complete));
   4461     try f.writeCValue(writer, local, .Other);
   4462     try v.elem(f, writer);
   4463     try a.assign(f, writer);
   4464     if (lhs != .undef and lhs.eql(rhs)) try writer.writeAll(switch (operator) {
   4465         .lt, .neq, .gt => "false",
   4466         .lte, .eq, .gte => "true",
   4467     }) else {
   4468         if (need_cast) try writer.writeAll("(void*)");
   4469         try f.writeCValue(writer, lhs, .Other);
   4470         try v.elem(f, writer);
   4471         try writer.writeAll(compareOperatorC(operator));
   4472         if (need_cast) try writer.writeAll("(void*)");
   4473         try f.writeCValue(writer, rhs, .Other);
   4474         try v.elem(f, writer);
   4475     }
   4476     try a.end(f, writer);
   4477     try v.end(f, inst, writer);
   4478 
   4479     return local;
   4480 }
   4481 
   4482 fn airEquality(
   4483     f: *Function,
   4484     inst: Air.Inst.Index,
   4485     operator: std.math.CompareOperator,
   4486 ) !CValue {
   4487     const pt = f.object.dg.pt;
   4488     const zcu = pt.zcu;
   4489     const ctype_pool = &f.object.dg.ctype_pool;
   4490     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4491 
   4492     const operand_ty = f.typeOf(bin_op.lhs);
   4493     const operand_bits = operand_ty.bitSize(zcu);
   4494     if (operand_ty.isAbiInt(zcu) and operand_bits > 64)
   4495         return airCmpBuiltinCall(
   4496             f,
   4497             inst,
   4498             bin_op,
   4499             operator,
   4500             .cmp,
   4501             if (operand_bits > 128) .bits else .none,
   4502         );
   4503     if (operand_ty.isRuntimeFloat())
   4504         return airCmpBuiltinCall(f, inst, bin_op, operator, .operator, .none);
   4505 
   4506     const lhs = try f.resolveInst(bin_op.lhs);
   4507     const rhs = try f.resolveInst(bin_op.rhs);
   4508     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   4509 
   4510     const writer = f.object.writer();
   4511     const local = try f.allocLocal(inst, .bool);
   4512     const a = try Assignment.start(f, writer, .bool);
   4513     try f.writeCValue(writer, local, .Other);
   4514     try a.assign(f, writer);
   4515 
   4516     const operand_ctype = try f.ctypeFromType(operand_ty, .complete);
   4517     if (lhs != .undef and lhs.eql(rhs)) try writer.writeAll(switch (operator) {
   4518         .lt, .lte, .gte, .gt => unreachable,
   4519         .neq => "false",
   4520         .eq => "true",
   4521     }) else switch (operand_ctype.info(ctype_pool)) {
   4522         .basic, .pointer => {
   4523             try f.writeCValue(writer, lhs, .Other);
   4524             try writer.writeAll(compareOperatorC(operator));
   4525             try f.writeCValue(writer, rhs, .Other);
   4526         },
   4527         .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   4528         .aggregate => |aggregate| if (aggregate.fields.len == 2 and
   4529             (aggregate.fields.at(0, ctype_pool).name.index == .is_null or
   4530                 aggregate.fields.at(1, ctype_pool).name.index == .is_null))
   4531         {
   4532             try f.writeCValueMember(writer, lhs, .{ .identifier = "is_null" });
   4533             try writer.writeAll(" || ");
   4534             try f.writeCValueMember(writer, rhs, .{ .identifier = "is_null" });
   4535             try writer.writeAll(" ? ");
   4536             try f.writeCValueMember(writer, lhs, .{ .identifier = "is_null" });
   4537             try writer.writeAll(compareOperatorC(operator));
   4538             try f.writeCValueMember(writer, rhs, .{ .identifier = "is_null" });
   4539             try writer.writeAll(" : ");
   4540             try f.writeCValueMember(writer, lhs, .{ .identifier = "payload" });
   4541             try writer.writeAll(compareOperatorC(operator));
   4542             try f.writeCValueMember(writer, rhs, .{ .identifier = "payload" });
   4543         } else for (0..aggregate.fields.len) |field_index| {
   4544             if (field_index > 0) try writer.writeAll(switch (operator) {
   4545                 .lt, .lte, .gte, .gt => unreachable,
   4546                 .eq => " && ",
   4547                 .neq => " || ",
   4548             });
   4549             const field_name: CValue = .{
   4550                 .ctype_pool_string = aggregate.fields.at(field_index, ctype_pool).name,
   4551             };
   4552             try f.writeCValueMember(writer, lhs, field_name);
   4553             try writer.writeAll(compareOperatorC(operator));
   4554             try f.writeCValueMember(writer, rhs, field_name);
   4555         },
   4556     }
   4557     try a.end(f, writer);
   4558 
   4559     return local;
   4560 }
   4561 
   4562 fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue {
   4563     const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   4564 
   4565     const operand = try f.resolveInst(un_op);
   4566     try reap(f, inst, &.{un_op});
   4567 
   4568     const writer = f.object.writer();
   4569     const local = try f.allocLocal(inst, .bool);
   4570     try f.writeCValue(writer, local, .Other);
   4571     try writer.writeAll(" = ");
   4572     try f.writeCValue(writer, operand, .Other);
   4573     try writer.print(" < sizeof({f}) / sizeof(*{0f});\n", .{fmtIdentSolo("zig_errorName")});
   4574     return local;
   4575 }
   4576 
   4577 fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue {
   4578     const pt = f.object.dg.pt;
   4579     const zcu = pt.zcu;
   4580     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4581     const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
   4582 
   4583     const lhs = try f.resolveInst(bin_op.lhs);
   4584     const rhs = try f.resolveInst(bin_op.rhs);
   4585     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   4586 
   4587     const inst_ty = f.typeOfIndex(inst);
   4588     const inst_scalar_ty = inst_ty.scalarType(zcu);
   4589     const elem_ty = inst_scalar_ty.elemType2(zcu);
   4590     if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return f.moveCValue(inst, inst_ty, lhs);
   4591     const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
   4592 
   4593     const local = try f.allocLocal(inst, inst_ty);
   4594     const writer = f.object.writer();
   4595     const v = try Vectorize.start(f, inst, writer, inst_ty);
   4596     const a = try Assignment.start(f, writer, inst_scalar_ctype);
   4597     try f.writeCValue(writer, local, .Other);
   4598     try v.elem(f, writer);
   4599     try a.assign(f, writer);
   4600     // We must convert to and from integer types to prevent UB if the operation
   4601     // results in a NULL pointer, or if LHS is NULL. The operation is only UB
   4602     // if the result is NULL and then dereferenced.
   4603     try writer.writeByte('(');
   4604     try f.renderCType(writer, inst_scalar_ctype);
   4605     try writer.writeAll(")(((uintptr_t)");
   4606     try f.writeCValue(writer, lhs, .Other);
   4607     try v.elem(f, writer);
   4608     try writer.writeAll(") ");
   4609     try writer.writeByte(operator);
   4610     try writer.writeAll(" (");
   4611     try f.writeCValue(writer, rhs, .Other);
   4612     try v.elem(f, writer);
   4613     try writer.writeAll("*sizeof(");
   4614     try f.renderType(writer, elem_ty);
   4615     try writer.writeAll(")))");
   4616     try a.end(f, writer);
   4617     try v.end(f, inst, writer);
   4618     return local;
   4619 }
   4620 
   4621 fn airMinMax(f: *Function, inst: Air.Inst.Index, operator: u8, operation: []const u8) !CValue {
   4622     const pt = f.object.dg.pt;
   4623     const zcu = pt.zcu;
   4624     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4625 
   4626     const inst_ty = f.typeOfIndex(inst);
   4627     const inst_scalar_ty = inst_ty.scalarType(zcu);
   4628 
   4629     if ((inst_scalar_ty.isInt(zcu) and inst_scalar_ty.bitSize(zcu) > 64) or inst_scalar_ty.isRuntimeFloat())
   4630         return try airBinBuiltinCall(f, inst, operation, .none);
   4631 
   4632     const lhs = try f.resolveInst(bin_op.lhs);
   4633     const rhs = try f.resolveInst(bin_op.rhs);
   4634     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   4635 
   4636     const writer = f.object.writer();
   4637     const local = try f.allocLocal(inst, inst_ty);
   4638     const v = try Vectorize.start(f, inst, writer, inst_ty);
   4639     try f.writeCValue(writer, local, .Other);
   4640     try v.elem(f, writer);
   4641     // (lhs <> rhs) ? lhs : rhs
   4642     try writer.writeAll(" = (");
   4643     try f.writeCValue(writer, lhs, .Other);
   4644     try v.elem(f, writer);
   4645     try writer.writeByte(' ');
   4646     try writer.writeByte(operator);
   4647     try writer.writeByte(' ');
   4648     try f.writeCValue(writer, rhs, .Other);
   4649     try v.elem(f, writer);
   4650     try writer.writeAll(") ? ");
   4651     try f.writeCValue(writer, lhs, .Other);
   4652     try v.elem(f, writer);
   4653     try writer.writeAll(" : ");
   4654     try f.writeCValue(writer, rhs, .Other);
   4655     try v.elem(f, writer);
   4656     try writer.writeAll(";\n");
   4657     try v.end(f, inst, writer);
   4658 
   4659     return local;
   4660 }
   4661 
   4662 fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue {
   4663     const pt = f.object.dg.pt;
   4664     const zcu = pt.zcu;
   4665     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4666     const bin_op = f.air.extraData(Air.Bin, ty_pl.payload).data;
   4667 
   4668     const ptr = try f.resolveInst(bin_op.lhs);
   4669     const len = try f.resolveInst(bin_op.rhs);
   4670     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   4671 
   4672     const inst_ty = f.typeOfIndex(inst);
   4673     const ptr_ty = inst_ty.slicePtrFieldType(zcu);
   4674 
   4675     const writer = f.object.writer();
   4676     const local = try f.allocLocal(inst, inst_ty);
   4677     {
   4678         const a = try Assignment.start(f, writer, try f.ctypeFromType(ptr_ty, .complete));
   4679         try f.writeCValueMember(writer, local, .{ .identifier = "ptr" });
   4680         try a.assign(f, writer);
   4681         try f.writeCValue(writer, ptr, .Other);
   4682         try a.end(f, writer);
   4683     }
   4684     {
   4685         const a = try Assignment.start(f, writer, .usize);
   4686         try f.writeCValueMember(writer, local, .{ .identifier = "len" });
   4687         try a.assign(f, writer);
   4688         try f.writeCValue(writer, len, .Other);
   4689         try a.end(f, writer);
   4690     }
   4691     return local;
   4692 }
   4693 
   4694 fn airCall(
   4695     f: *Function,
   4696     inst: Air.Inst.Index,
   4697     modifier: std.builtin.CallModifier,
   4698 ) !CValue {
   4699     const pt = f.object.dg.pt;
   4700     const zcu = pt.zcu;
   4701     const ip = &zcu.intern_pool;
   4702     // Not even allowed to call panic in a naked function.
   4703     if (f.object.dg.is_naked_fn) return .none;
   4704 
   4705     const gpa = f.object.dg.gpa;
   4706     const writer = f.object.writer();
   4707 
   4708     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   4709     const extra = f.air.extraData(Air.Call, pl_op.payload);
   4710     const args: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.args_len]);
   4711 
   4712     const resolved_args = try gpa.alloc(CValue, args.len);
   4713     defer gpa.free(resolved_args);
   4714     for (resolved_args, args) |*resolved_arg, arg| {
   4715         const arg_ty = f.typeOf(arg);
   4716         const arg_ctype = try f.ctypeFromType(arg_ty, .parameter);
   4717         if (arg_ctype.index == .void) {
   4718             resolved_arg.* = .none;
   4719             continue;
   4720         }
   4721         resolved_arg.* = try f.resolveInst(arg);
   4722         if (!arg_ctype.eql(try f.ctypeFromType(arg_ty, .complete))) {
   4723             const array_local = try f.allocAlignedLocal(inst, .{
   4724                 .ctype = arg_ctype,
   4725                 .alignas = CType.AlignAs.fromAbiAlignment(arg_ty.abiAlignment(zcu)),
   4726             });
   4727             try writer.writeAll("memcpy(");
   4728             try f.writeCValueMember(writer, array_local, .{ .identifier = "array" });
   4729             try writer.writeAll(", ");
   4730             try f.writeCValue(writer, resolved_arg.*, .FunctionArgument);
   4731             try writer.writeAll(", sizeof(");
   4732             try f.renderCType(writer, arg_ctype);
   4733             try writer.writeAll("));\n");
   4734             resolved_arg.* = array_local;
   4735         }
   4736     }
   4737 
   4738     const callee = try f.resolveInst(pl_op.operand);
   4739 
   4740     {
   4741         var bt = iterateBigTomb(f, inst);
   4742         try bt.feed(pl_op.operand);
   4743         for (args) |arg| try bt.feed(arg);
   4744     }
   4745 
   4746     const callee_ty = f.typeOf(pl_op.operand);
   4747     const callee_is_ptr = switch (callee_ty.zigTypeTag(zcu)) {
   4748         .@"fn" => false,
   4749         .pointer => true,
   4750         else => unreachable,
   4751     };
   4752     const fn_info = zcu.typeToFunc(if (callee_is_ptr) callee_ty.childType(zcu) else callee_ty).?;
   4753     const ret_ty: Type = .fromInterned(fn_info.return_type);
   4754     const ret_ctype: CType = if (ret_ty.isNoReturn(zcu))
   4755         .void
   4756     else
   4757         try f.ctypeFromType(ret_ty, .parameter);
   4758 
   4759     const result_local = result: {
   4760         if (modifier == .always_tail) {
   4761             try writer.writeAll("zig_always_tail return ");
   4762             break :result .none;
   4763         } else if (ret_ctype.index == .void) {
   4764             break :result .none;
   4765         } else if (f.liveness.isUnused(inst)) {
   4766             try writer.writeByte('(');
   4767             try f.renderCType(writer, .void);
   4768             try writer.writeByte(')');
   4769             break :result .none;
   4770         } else {
   4771             const local = try f.allocAlignedLocal(inst, .{
   4772                 .ctype = ret_ctype,
   4773                 .alignas = CType.AlignAs.fromAbiAlignment(ret_ty.abiAlignment(zcu)),
   4774             });
   4775             try f.writeCValue(writer, local, .Other);
   4776             try writer.writeAll(" = ");
   4777             break :result local;
   4778         }
   4779     };
   4780 
   4781     callee: {
   4782         known: {
   4783             const callee_val = (try f.air.value(pl_op.operand, pt)) orelse break :known;
   4784             const fn_nav, const need_cast = switch (ip.indexToKey(callee_val.toIntern())) {
   4785                 .@"extern" => |@"extern"| .{ @"extern".owner_nav, false },
   4786                 .func => |func| .{ func.owner_nav, Type.fromInterned(func.ty).fnCallingConvention(zcu) != .naked and
   4787                     Type.fromInterned(func.uncoerced_ty).fnCallingConvention(zcu) == .naked },
   4788                 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
   4789                     .nav => |nav| .{ nav, Type.fromInterned(ptr.ty).childType(zcu).fnCallingConvention(zcu) != .naked and
   4790                         zcu.navValue(nav).typeOf(zcu).fnCallingConvention(zcu) == .naked },
   4791                     else => break :known,
   4792                 } else break :known,
   4793                 else => break :known,
   4794             };
   4795             if (need_cast) {
   4796                 try writer.writeAll("((");
   4797                 try f.renderType(writer, if (callee_is_ptr) callee_ty else try pt.singleConstPtrType(callee_ty));
   4798                 try writer.writeByte(')');
   4799                 if (!callee_is_ptr) try writer.writeByte('&');
   4800             }
   4801             switch (modifier) {
   4802                 .auto, .always_tail => try f.object.dg.renderNavName(writer, fn_nav),
   4803                 inline .never_tail, .never_inline => |m| try writer.writeAll(try f.getLazyFnName(@unionInit(LazyFnKey, @tagName(m), fn_nav))),
   4804                 else => unreachable,
   4805             }
   4806             if (need_cast) try writer.writeByte(')');
   4807             break :callee;
   4808         }
   4809         switch (modifier) {
   4810             .auto, .always_tail => {},
   4811             .never_tail => return f.fail("CBE: runtime callee with never_tail attribute unsupported", .{}),
   4812             .never_inline => return f.fail("CBE: runtime callee with never_inline attribute unsupported", .{}),
   4813             else => unreachable,
   4814         }
   4815         // Fall back to function pointer call.
   4816         try f.writeCValue(writer, callee, .Other);
   4817     }
   4818 
   4819     try writer.writeByte('(');
   4820     var need_comma = false;
   4821     for (resolved_args) |resolved_arg| {
   4822         if (resolved_arg == .none) continue;
   4823         if (need_comma) try writer.writeAll(", ");
   4824         need_comma = true;
   4825         try f.writeCValue(writer, resolved_arg, .FunctionArgument);
   4826         try f.freeCValue(inst, resolved_arg);
   4827     }
   4828     try writer.writeAll(");\n");
   4829 
   4830     const result = result: {
   4831         if (result_local == .none or !lowersToArray(ret_ty, pt))
   4832             break :result result_local;
   4833 
   4834         const array_local = try f.allocLocal(inst, ret_ty);
   4835         try writer.writeAll("memcpy(");
   4836         try f.writeCValue(writer, array_local, .FunctionArgument);
   4837         try writer.writeAll(", ");
   4838         try f.writeCValueMember(writer, result_local, .{ .identifier = "array" });
   4839         try writer.writeAll(", sizeof(");
   4840         try f.renderType(writer, ret_ty);
   4841         try writer.writeAll("));\n");
   4842         try freeLocal(f, inst, result_local.new_local, null);
   4843         break :result array_local;
   4844     };
   4845 
   4846     return result;
   4847 }
   4848 
   4849 fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
   4850     const dbg_stmt = f.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
   4851     const writer = f.object.writer();
   4852     // TODO re-evaluate whether to emit these or not. If we naively emit
   4853     // these directives, the output file will report bogus line numbers because
   4854     // every newline after the #line directive adds one to the line.
   4855     // We also don't print the filename yet, so the output is strictly unhelpful.
   4856     // If we wanted to go this route, we would need to go all the way and not output
   4857     // newlines until the next dbg_stmt occurs.
   4858     // Perhaps an additional compilation option is in order?
   4859     //try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
   4860     try writer.print("/* file:{d}:{d} */\n", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
   4861     return .none;
   4862 }
   4863 
   4864 fn airDbgEmptyStmt(f: *Function, _: Air.Inst.Index) !CValue {
   4865     try f.object.writer().writeAll("(void)0;\n");
   4866     return .none;
   4867 }
   4868 
   4869 fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
   4870     const pt = f.object.dg.pt;
   4871     const zcu = pt.zcu;
   4872     const ip = &zcu.intern_pool;
   4873     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4874     const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
   4875     const owner_nav = ip.getNav(zcu.funcInfo(extra.data.func).owner_nav);
   4876     const writer = f.object.writer();
   4877     try writer.print("/* inline:{} */\n", .{owner_nav.fqn.fmt(&zcu.intern_pool)});
   4878     return lowerBlock(f, inst, @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len]));
   4879 }
   4880 
   4881 fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
   4882     const pt = f.object.dg.pt;
   4883     const zcu = pt.zcu;
   4884     const tag = f.air.instructions.items(.tag)[@intFromEnum(inst)];
   4885     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   4886     const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
   4887     const operand_is_undef = if (try f.air.value(pl_op.operand, pt)) |v| v.isUndefDeep(zcu) else false;
   4888     if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);
   4889 
   4890     try reap(f, inst, &.{pl_op.operand});
   4891     const writer = f.object.writer();
   4892     try writer.print("/* {s}:{s} */\n", .{ @tagName(tag), name.toSlice(f.air) });
   4893     return .none;
   4894 }
   4895 
   4896 fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
   4897     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4898     const extra = f.air.extraData(Air.Block, ty_pl.payload);
   4899     return lowerBlock(f, inst, @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len]));
   4900 }
   4901 
   4902 fn lowerBlock(f: *Function, inst: Air.Inst.Index, body: []const Air.Inst.Index) !CValue {
   4903     const pt = f.object.dg.pt;
   4904     const zcu = pt.zcu;
   4905     const liveness_block = f.liveness.getBlock(inst);
   4906 
   4907     const block_id = f.next_block_index;
   4908     f.next_block_index += 1;
   4909     const writer = f.object.writer();
   4910 
   4911     const inst_ty = f.typeOfIndex(inst);
   4912     const result = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu) and !f.liveness.isUnused(inst))
   4913         try f.allocLocal(inst, inst_ty)
   4914     else
   4915         .none;
   4916 
   4917     try f.blocks.putNoClobber(f.object.dg.gpa, inst, .{
   4918         .block_id = block_id,
   4919         .result = result,
   4920     });
   4921 
   4922     try genBodyResolveState(f, inst, &.{}, body, true);
   4923 
   4924     assert(f.blocks.remove(inst));
   4925 
   4926     // The body might result in some values we had beforehand being killed
   4927     for (liveness_block.deaths) |death| {
   4928         try die(f, inst, death.toRef());
   4929     }
   4930 
   4931     try f.object.indent_writer.insertNewline();
   4932 
   4933     // noreturn blocks have no `br` instructions reaching them, so we don't want a label
   4934     if (f.object.dg.is_naked_fn) {
   4935         if (f.object.dg.expected_block) |expected_block| {
   4936             if (block_id != expected_block)
   4937                 return f.fail("runtime code not allowed in naked function", .{});
   4938             f.object.dg.expected_block = null;
   4939         }
   4940     } else if (!f.typeOfIndex(inst).isNoReturn(zcu)) {
   4941         // label must be followed by an expression, include an empty one.
   4942         try writer.print("zig_block_{d}:;\n", .{block_id});
   4943     }
   4944 
   4945     return result;
   4946 }
   4947 
   4948 fn airTry(f: *Function, inst: Air.Inst.Index) !CValue {
   4949     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   4950     const extra = f.air.extraData(Air.Try, pl_op.payload);
   4951     const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len]);
   4952     const err_union_ty = f.typeOf(pl_op.operand);
   4953     return lowerTry(f, inst, pl_op.operand, body, err_union_ty, false);
   4954 }
   4955 
   4956 fn airTryPtr(f: *Function, inst: Air.Inst.Index) !CValue {
   4957     const pt = f.object.dg.pt;
   4958     const zcu = pt.zcu;
   4959     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4960     const extra = f.air.extraData(Air.TryPtr, ty_pl.payload);
   4961     const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.body_len]);
   4962     const err_union_ty = f.typeOf(extra.data.ptr).childType(zcu);
   4963     return lowerTry(f, inst, extra.data.ptr, body, err_union_ty, true);
   4964 }
   4965 
   4966 fn lowerTry(
   4967     f: *Function,
   4968     inst: Air.Inst.Index,
   4969     operand: Air.Inst.Ref,
   4970     body: []const Air.Inst.Index,
   4971     err_union_ty: Type,
   4972     is_ptr: bool,
   4973 ) !CValue {
   4974     const pt = f.object.dg.pt;
   4975     const zcu = pt.zcu;
   4976     const err_union = try f.resolveInst(operand);
   4977     const inst_ty = f.typeOfIndex(inst);
   4978     const liveness_condbr = f.liveness.getCondBr(inst);
   4979     const writer = f.object.writer();
   4980     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   4981     const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu);
   4982 
   4983     if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
   4984         try writer.writeAll("if (");
   4985         if (!payload_has_bits) {
   4986             if (is_ptr)
   4987                 try f.writeCValueDeref(writer, err_union)
   4988             else
   4989                 try f.writeCValue(writer, err_union, .Other);
   4990         } else {
   4991             // Reap the operand so that it can be reused inside genBody.
   4992             // Remember we must avoid calling reap() twice for the same operand
   4993             // in this function.
   4994             try reap(f, inst, &.{operand});
   4995             if (is_ptr)
   4996                 try f.writeCValueDerefMember(writer, err_union, .{ .identifier = "error" })
   4997             else
   4998                 try f.writeCValueMember(writer, err_union, .{ .identifier = "error" });
   4999         }
   5000         try writer.writeAll(") ");
   5001 
   5002         try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);
   5003         try f.object.indent_writer.insertNewline();
   5004         if (f.object.dg.expected_block) |_|
   5005             return f.fail("runtime code not allowed in naked function", .{});
   5006     }
   5007 
   5008     // Now we have the "then branch" (in terms of the liveness data); process any deaths.
   5009     for (liveness_condbr.then_deaths) |death| {
   5010         try die(f, inst, death.toRef());
   5011     }
   5012 
   5013     if (!payload_has_bits) {
   5014         if (!is_ptr) {
   5015             return .none;
   5016         } else {
   5017             return err_union;
   5018         }
   5019     }
   5020 
   5021     try reap(f, inst, &.{operand});
   5022 
   5023     if (f.liveness.isUnused(inst)) return .none;
   5024 
   5025     const local = try f.allocLocal(inst, inst_ty);
   5026     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   5027     try f.writeCValue(writer, local, .Other);
   5028     try a.assign(f, writer);
   5029     if (is_ptr) {
   5030         try writer.writeByte('&');
   5031         try f.writeCValueDerefMember(writer, err_union, .{ .identifier = "payload" });
   5032     } else try f.writeCValueMember(writer, err_union, .{ .identifier = "payload" });
   5033     try a.end(f, writer);
   5034     return local;
   5035 }
   5036 
   5037 fn airBr(f: *Function, inst: Air.Inst.Index) !void {
   5038     const branch = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
   5039     const block = f.blocks.get(branch.block_inst).?;
   5040     const result = block.result;
   5041     const writer = f.object.writer();
   5042 
   5043     if (f.object.dg.is_naked_fn) {
   5044         if (result != .none) return f.fail("runtime code not allowed in naked function", .{});
   5045         f.object.dg.expected_block = block.block_id;
   5046         return;
   5047     }
   5048 
   5049     // If result is .none then the value of the block is unused.
   5050     if (result != .none) {
   5051         const operand_ty = f.typeOf(branch.operand);
   5052         const operand = try f.resolveInst(branch.operand);
   5053         try reap(f, inst, &.{branch.operand});
   5054 
   5055         const a = try Assignment.start(f, writer, try f.ctypeFromType(operand_ty, .complete));
   5056         try f.writeCValue(writer, result, .Other);
   5057         try a.assign(f, writer);
   5058         try f.writeCValue(writer, operand, .Other);
   5059         try a.end(f, writer);
   5060     }
   5061 
   5062     try writer.print("goto zig_block_{d};\n", .{block.block_id});
   5063 }
   5064 
   5065 fn airRepeat(f: *Function, inst: Air.Inst.Index) !void {
   5066     const repeat = f.air.instructions.items(.data)[@intFromEnum(inst)].repeat;
   5067     const writer = f.object.writer();
   5068     try writer.print("goto zig_loop_{d};\n", .{@intFromEnum(repeat.loop_inst)});
   5069 }
   5070 
   5071 fn airSwitchDispatch(f: *Function, inst: Air.Inst.Index) !void {
   5072     const pt = f.object.dg.pt;
   5073     const zcu = pt.zcu;
   5074     const br = f.air.instructions.items(.data)[@intFromEnum(inst)].br;
   5075     const writer = f.object.writer();
   5076 
   5077     if (try f.air.value(br.operand, pt)) |cond_val| {
   5078         // Comptime-known dispatch. Iterate the cases to find the correct
   5079         // one, and branch directly to the corresponding case.
   5080         const switch_br = f.air.unwrapSwitch(br.block_inst);
   5081         var it = switch_br.iterateCases();
   5082         const target_case_idx: u32 = target: while (it.next()) |case| {
   5083             for (case.items) |item| {
   5084                 const val = Value.fromInterned(item.toInterned().?);
   5085                 if (cond_val.compareHetero(.eq, val, zcu)) break :target case.idx;
   5086             }
   5087             for (case.ranges) |range| {
   5088                 const low = Value.fromInterned(range[0].toInterned().?);
   5089                 const high = Value.fromInterned(range[1].toInterned().?);
   5090                 if (cond_val.compareHetero(.gte, low, zcu) and
   5091                     cond_val.compareHetero(.lte, high, zcu))
   5092                 {
   5093                     break :target case.idx;
   5094                 }
   5095             }
   5096         } else switch_br.cases_len;
   5097         try writer.print("goto zig_switch_{d}_dispatch_{d};\n", .{ @intFromEnum(br.block_inst), target_case_idx });
   5098         return;
   5099     }
   5100 
   5101     // Runtime-known dispatch. Set the switch condition, and branch back.
   5102     const cond = try f.resolveInst(br.operand);
   5103     const cond_local = f.loop_switch_conds.get(br.block_inst).?;
   5104     try f.writeCValue(writer, .{ .local = cond_local }, .Other);
   5105     try writer.writeAll(" = ");
   5106     try f.writeCValue(writer, cond, .Other);
   5107     try writer.writeAll(";\n");
   5108     try writer.print("goto zig_switch_{d}_loop;", .{@intFromEnum(br.block_inst)});
   5109 }
   5110 
   5111 fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
   5112     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5113     const inst_ty = f.typeOfIndex(inst);
   5114 
   5115     const operand = try f.resolveInst(ty_op.operand);
   5116     const operand_ty = f.typeOf(ty_op.operand);
   5117 
   5118     const bitcasted = try bitcast(f, inst_ty, operand, operand_ty);
   5119     try reap(f, inst, &.{ty_op.operand});
   5120     return f.moveCValue(inst, inst_ty, bitcasted);
   5121 }
   5122 
   5123 fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CValue {
   5124     const pt = f.object.dg.pt;
   5125     const zcu = pt.zcu;
   5126     const target = &f.object.dg.mod.resolved_target.result;
   5127     const ctype_pool = &f.object.dg.ctype_pool;
   5128     const writer = f.object.writer();
   5129 
   5130     if (operand_ty.isAbiInt(zcu) and dest_ty.isAbiInt(zcu)) {
   5131         const src_info = dest_ty.intInfo(zcu);
   5132         const dest_info = operand_ty.intInfo(zcu);
   5133         if (src_info.signedness == dest_info.signedness and
   5134             src_info.bits == dest_info.bits) return operand;
   5135     }
   5136 
   5137     if (dest_ty.isPtrAtRuntime(zcu) or operand_ty.isPtrAtRuntime(zcu)) {
   5138         const local = try f.allocLocal(null, dest_ty);
   5139         try f.writeCValue(writer, local, .Other);
   5140         try writer.writeAll(" = (");
   5141         try f.renderType(writer, dest_ty);
   5142         try writer.writeByte(')');
   5143         try f.writeCValue(writer, operand, .Other);
   5144         try writer.writeAll(";\n");
   5145         return local;
   5146     }
   5147 
   5148     const operand_lval = if (operand == .constant) blk: {
   5149         const operand_local = try f.allocLocal(null, operand_ty);
   5150         try f.writeCValue(writer, operand_local, .Other);
   5151         try writer.writeAll(" = ");
   5152         try f.writeCValue(writer, operand, .Other);
   5153         try writer.writeAll(";\n");
   5154         break :blk operand_local;
   5155     } else operand;
   5156 
   5157     const local = try f.allocLocal(null, dest_ty);
   5158     try writer.writeAll("memcpy(&");
   5159     try f.writeCValue(writer, local, .Other);
   5160     try writer.writeAll(", &");
   5161     try f.writeCValue(writer, operand_lval, .Other);
   5162     try writer.writeAll(", sizeof(");
   5163     try f.renderType(
   5164         writer,
   5165         if (dest_ty.abiSize(zcu) <= operand_ty.abiSize(zcu)) dest_ty else operand_ty,
   5166     );
   5167     try writer.writeAll("));\n");
   5168 
   5169     // Ensure padding bits have the expected value.
   5170     if (dest_ty.isAbiInt(zcu)) {
   5171         const dest_ctype = try f.ctypeFromType(dest_ty, .complete);
   5172         const dest_info = dest_ty.intInfo(zcu);
   5173         var bits: u16 = dest_info.bits;
   5174         var wrap_ctype: ?CType = null;
   5175         var need_bitcasts = false;
   5176 
   5177         try f.writeCValue(writer, local, .Other);
   5178         switch (dest_ctype.info(ctype_pool)) {
   5179             else => {},
   5180             .array => |array_info| {
   5181                 try writer.print("[{d}]", .{switch (target.cpu.arch.endian()) {
   5182                     .little => array_info.len - 1,
   5183                     .big => 0,
   5184                 }});
   5185                 wrap_ctype = array_info.elem_ctype.toSignedness(dest_info.signedness);
   5186                 need_bitcasts = wrap_ctype.?.index == .zig_i128;
   5187                 bits -= 1;
   5188                 bits %= @as(u16, @intCast(f.byteSize(array_info.elem_ctype) * 8));
   5189                 bits += 1;
   5190             },
   5191         }
   5192         try writer.writeAll(" = ");
   5193         if (need_bitcasts) {
   5194             try writer.writeAll("zig_bitCast_");
   5195             try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_ctype.?.toUnsigned());
   5196             try writer.writeByte('(');
   5197         }
   5198         try writer.writeAll("zig_wrap_");
   5199         const info_ty = try pt.intType(dest_info.signedness, bits);
   5200         if (wrap_ctype) |ctype|
   5201             try f.object.dg.renderCTypeForBuiltinFnName(writer, ctype)
   5202         else
   5203             try f.object.dg.renderTypeForBuiltinFnName(writer, info_ty);
   5204         try writer.writeByte('(');
   5205         if (need_bitcasts) {
   5206             try writer.writeAll("zig_bitCast_");
   5207             try f.object.dg.renderCTypeForBuiltinFnName(writer, wrap_ctype.?);
   5208             try writer.writeByte('(');
   5209         }
   5210         try f.writeCValue(writer, local, .Other);
   5211         switch (dest_ctype.info(ctype_pool)) {
   5212             else => {},
   5213             .array => |array_info| try writer.print("[{d}]", .{
   5214                 switch (target.cpu.arch.endian()) {
   5215                     .little => array_info.len - 1,
   5216                     .big => 0,
   5217                 },
   5218             }),
   5219         }
   5220         if (need_bitcasts) try writer.writeByte(')');
   5221         try f.object.dg.renderBuiltinInfo(writer, info_ty, .bits);
   5222         if (need_bitcasts) try writer.writeByte(')');
   5223         try writer.writeAll(");\n");
   5224     }
   5225 
   5226     try f.freeCValue(null, operand_lval);
   5227     return local;
   5228 }
   5229 
   5230 fn airTrap(f: *Function, writer: anytype) !void {
   5231     // Not even allowed to call trap in a naked function.
   5232     if (f.object.dg.is_naked_fn) return;
   5233     try writer.writeAll("zig_trap();\n");
   5234 }
   5235 
   5236 fn airBreakpoint(writer: anytype) !CValue {
   5237     try writer.writeAll("zig_breakpoint();\n");
   5238     return .none;
   5239 }
   5240 
   5241 fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue {
   5242     const writer = f.object.writer();
   5243     const local = try f.allocLocal(inst, .usize);
   5244     try f.writeCValue(writer, local, .Other);
   5245     try writer.writeAll(" = (");
   5246     try f.renderType(writer, .usize);
   5247     try writer.writeAll(")zig_return_address();\n");
   5248     return local;
   5249 }
   5250 
   5251 fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue {
   5252     const writer = f.object.writer();
   5253     const local = try f.allocLocal(inst, .usize);
   5254     try f.writeCValue(writer, local, .Other);
   5255     try writer.writeAll(" = (");
   5256     try f.renderType(writer, .usize);
   5257     try writer.writeAll(")zig_frame_address();\n");
   5258     return local;
   5259 }
   5260 
   5261 fn airUnreach(f: *Function) !void {
   5262     // Not even allowed to call unreachable in a naked function.
   5263     if (f.object.dg.is_naked_fn) return;
   5264     try f.object.writer().writeAll("zig_unreachable();\n");
   5265 }
   5266 
   5267 fn airLoop(f: *Function, inst: Air.Inst.Index) !void {
   5268     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5269     const loop = f.air.extraData(Air.Block, ty_pl.payload);
   5270     const body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[loop.end..][0..loop.data.body_len]);
   5271     const writer = f.object.writer();
   5272 
   5273     // `repeat` instructions matching this loop will branch to
   5274     // this label. Since we need a label for arbitrary `repeat`
   5275     // anyway, there's actually no need to use a "real" looping
   5276     // construct at all!
   5277     try writer.print("zig_loop_{d}:\n", .{@intFromEnum(inst)});
   5278     try genBodyInner(f, body); // no need to restore state, we're noreturn
   5279 }
   5280 
   5281 fn airCondBr(f: *Function, inst: Air.Inst.Index) !void {
   5282     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   5283     const cond = try f.resolveInst(pl_op.operand);
   5284     try reap(f, inst, &.{pl_op.operand});
   5285     const extra = f.air.extraData(Air.CondBr, pl_op.payload);
   5286     const then_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end..][0..extra.data.then_body_len]);
   5287     const else_body: []const Air.Inst.Index = @ptrCast(f.air.extra.items[extra.end + then_body.len ..][0..extra.data.else_body_len]);
   5288     const liveness_condbr = f.liveness.getCondBr(inst);
   5289     const writer = f.object.writer();
   5290 
   5291     try writer.writeAll("if (");
   5292     try f.writeCValue(writer, cond, .Other);
   5293     try writer.writeAll(") ");
   5294 
   5295     try genBodyResolveState(f, inst, liveness_condbr.then_deaths, then_body, false);
   5296     try writer.writeByte('\n');
   5297     if (else_body.len > 0) if (f.object.dg.expected_block) |_|
   5298         return f.fail("runtime code not allowed in naked function", .{});
   5299 
   5300     // We don't need to use `genBodyResolveState` for the else block, because this instruction is
   5301     // noreturn so must terminate a body, therefore we don't need to leave `value_map` or
   5302     // `free_locals_map` well defined (our parent is responsible for doing that).
   5303 
   5304     for (liveness_condbr.else_deaths) |death| {
   5305         try die(f, inst, death.toRef());
   5306     }
   5307 
   5308     // We never actually need an else block, because our branches are noreturn so must (for
   5309     // instance) `br` to a block (label).
   5310 
   5311     try genBodyInner(f, else_body);
   5312 }
   5313 
   5314 fn airSwitchBr(f: *Function, inst: Air.Inst.Index, is_dispatch_loop: bool) !void {
   5315     const pt = f.object.dg.pt;
   5316     const zcu = pt.zcu;
   5317     const gpa = f.object.dg.gpa;
   5318     const switch_br = f.air.unwrapSwitch(inst);
   5319     const init_condition = try f.resolveInst(switch_br.operand);
   5320     try reap(f, inst, &.{switch_br.operand});
   5321     const condition_ty = f.typeOf(switch_br.operand);
   5322     const writer = f.object.writer();
   5323 
   5324     // For dispatches, we will create a local alloc to contain the condition value.
   5325     // This may not result in optimal codegen for switch loops, but it minimizes the
   5326     // amount of C code we generate, which is probably more desirable here (and is simpler).
   5327     const condition = if (is_dispatch_loop) cond: {
   5328         const new_local = try f.allocLocal(inst, condition_ty);
   5329         try f.copyCValue(try f.ctypeFromType(condition_ty, .complete), new_local, init_condition);
   5330         try writer.print("zig_switch_{d}_loop:\n", .{@intFromEnum(inst)});
   5331         try f.loop_switch_conds.put(gpa, inst, new_local.new_local);
   5332         break :cond new_local;
   5333     } else init_condition;
   5334 
   5335     defer if (is_dispatch_loop) {
   5336         assert(f.loop_switch_conds.remove(inst));
   5337     };
   5338 
   5339     try writer.writeAll("switch (");
   5340 
   5341     const lowered_condition_ty: Type = if (condition_ty.toIntern() == .bool_type)
   5342         .u1
   5343     else if (condition_ty.isPtrAtRuntime(zcu))
   5344         .usize
   5345     else
   5346         condition_ty;
   5347     if (condition_ty.toIntern() != lowered_condition_ty.toIntern()) {
   5348         try writer.writeByte('(');
   5349         try f.renderType(writer, lowered_condition_ty);
   5350         try writer.writeByte(')');
   5351     }
   5352     try f.writeCValue(writer, condition, .Other);
   5353     try writer.writeAll(") {");
   5354     f.object.indent_writer.pushIndent();
   5355 
   5356     const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1);
   5357     defer gpa.free(liveness.deaths);
   5358 
   5359     var any_range_cases = false;
   5360     var it = switch_br.iterateCases();
   5361     while (it.next()) |case| {
   5362         if (case.ranges.len > 0) {
   5363             any_range_cases = true;
   5364             continue;
   5365         }
   5366         for (case.items) |item| {
   5367             try f.object.indent_writer.insertNewline();
   5368             try writer.writeAll("case ");
   5369             const item_value = try f.air.value(item, pt);
   5370             // If `item_value` is a pointer with a known integer address, print the address
   5371             // with no cast to avoid a warning.
   5372             write_val: {
   5373                 if (condition_ty.isPtrAtRuntime(zcu)) {
   5374                     if (item_value.?.getUnsignedInt(zcu)) |item_int| {
   5375                         try writer.print("{f}", .{try f.fmtIntLiteralDec(try pt.intValue(lowered_condition_ty, item_int))});
   5376                         break :write_val;
   5377                     }
   5378                 }
   5379                 if (condition_ty.isPtrAtRuntime(zcu)) {
   5380                     try writer.writeByte('(');
   5381                     try f.renderType(writer, .usize);
   5382                     try writer.writeByte(')');
   5383                 }
   5384                 try f.object.dg.renderValue(writer, (try f.air.value(item, pt)).?, .Other);
   5385             }
   5386             try writer.writeByte(':');
   5387         }
   5388         try writer.writeAll(" {\n");
   5389         f.object.indent_writer.pushIndent();
   5390         if (is_dispatch_loop) {
   5391             try writer.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });
   5392         }
   5393         try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
   5394         f.object.indent_writer.popIndent();
   5395         try writer.writeByte('}');
   5396         if (f.object.dg.expected_block) |_|
   5397             return f.fail("runtime code not allowed in naked function", .{});
   5398 
   5399         // The case body must be noreturn so we don't need to insert a break.
   5400     }
   5401 
   5402     const else_body = it.elseBody();
   5403     try f.object.indent_writer.insertNewline();
   5404 
   5405     try writer.writeAll("default: ");
   5406     if (any_range_cases) {
   5407         // We will iterate the cases again to handle those with ranges, and generate
   5408         // code using conditions rather than switch cases for such cases.
   5409         it = switch_br.iterateCases();
   5410         while (it.next()) |case| {
   5411             if (case.ranges.len == 0) continue; // handled above
   5412 
   5413             try writer.writeAll("if (");
   5414             for (case.items, 0..) |item, item_i| {
   5415                 if (item_i != 0) try writer.writeAll(" || ");
   5416                 try f.writeCValue(writer, condition, .Other);
   5417                 try writer.writeAll(" == ");
   5418                 try f.object.dg.renderValue(writer, (try f.air.value(item, pt)).?, .Other);
   5419             }
   5420             for (case.ranges, 0..) |range, range_i| {
   5421                 if (case.items.len != 0 or range_i != 0) try writer.writeAll(" || ");
   5422                 // "(x >= lower && x <= upper)"
   5423                 try writer.writeByte('(');
   5424                 try f.writeCValue(writer, condition, .Other);
   5425                 try writer.writeAll(" >= ");
   5426                 try f.object.dg.renderValue(writer, (try f.air.value(range[0], pt)).?, .Other);
   5427                 try writer.writeAll(" && ");
   5428                 try f.writeCValue(writer, condition, .Other);
   5429                 try writer.writeAll(" <= ");
   5430                 try f.object.dg.renderValue(writer, (try f.air.value(range[1], pt)).?, .Other);
   5431                 try writer.writeByte(')');
   5432             }
   5433             try writer.writeAll(") {\n");
   5434             f.object.indent_writer.pushIndent();
   5435             if (is_dispatch_loop) {
   5436                 try writer.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), case.idx });
   5437             }
   5438             try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, true);
   5439             f.object.indent_writer.popIndent();
   5440             try writer.writeByte('}');
   5441             if (f.object.dg.expected_block) |_|
   5442                 return f.fail("runtime code not allowed in naked function", .{});
   5443         }
   5444     }
   5445     if (is_dispatch_loop) {
   5446         try writer.print("zig_switch_{d}_dispatch_{d}: ", .{ @intFromEnum(inst), switch_br.cases_len });
   5447     }
   5448     if (else_body.len > 0) {
   5449         // Note that this must be the last case, so we do not need to use `genBodyResolveState` since
   5450         // the parent block will do it (because the case body is noreturn).
   5451         for (liveness.deaths[liveness.deaths.len - 1]) |death| {
   5452             try die(f, inst, death.toRef());
   5453         }
   5454         try genBody(f, else_body);
   5455         if (f.object.dg.expected_block) |_|
   5456             return f.fail("runtime code not allowed in naked function", .{});
   5457     } else {
   5458         try writer.writeAll("zig_unreachable();");
   5459     }
   5460     try f.object.indent_writer.insertNewline();
   5461 
   5462     f.object.indent_writer.popIndent();
   5463     try writer.writeAll("}\n");
   5464 }
   5465 
   5466 fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool {
   5467     const dg = f.object.dg;
   5468     const target = &dg.mod.resolved_target.result;
   5469     return switch (constraint[0]) {
   5470         '{' => true,
   5471         'i', 'r' => false,
   5472         'I' => !target.cpu.arch.isArm(),
   5473         else => switch (value) {
   5474             .constant => |val| switch (dg.pt.zcu.intern_pool.indexToKey(val.toIntern())) {
   5475                 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) {
   5476                     .nav => false,
   5477                     else => true,
   5478                 } else true,
   5479                 else => true,
   5480             },
   5481             else => false,
   5482         },
   5483     };
   5484 }
   5485 
   5486 fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
   5487     const pt = f.object.dg.pt;
   5488     const zcu = pt.zcu;
   5489     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5490     const extra = f.air.extraData(Air.Asm, ty_pl.payload);
   5491     const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
   5492     const clobbers_len: u31 = @truncate(extra.data.flags);
   5493     const gpa = f.object.dg.gpa;
   5494     var extra_i: usize = extra.end;
   5495     const outputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..extra.data.outputs_len]);
   5496     extra_i += outputs.len;
   5497     const inputs: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[extra_i..][0..extra.data.inputs_len]);
   5498     extra_i += inputs.len;
   5499 
   5500     const result = result: {
   5501         const writer = f.object.writer();
   5502         const inst_ty = f.typeOfIndex(inst);
   5503         const inst_local = if (inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) local: {
   5504             const inst_local = try f.allocLocalValue(.{
   5505                 .ctype = try f.ctypeFromType(inst_ty, .complete),
   5506                 .alignas = CType.AlignAs.fromAbiAlignment(inst_ty.abiAlignment(zcu)),
   5507             });
   5508             if (f.wantSafety()) {
   5509                 try f.writeCValue(writer, inst_local, .Other);
   5510                 try writer.writeAll(" = ");
   5511                 try f.writeCValue(writer, .{ .undef = inst_ty }, .Other);
   5512                 try writer.writeAll(";\n");
   5513             }
   5514             break :local inst_local;
   5515         } else .none;
   5516 
   5517         const locals_begin: LocalIndex = @intCast(f.locals.items.len);
   5518         const constraints_extra_begin = extra_i;
   5519         for (outputs) |output| {
   5520             const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]);
   5521             const constraint = mem.sliceTo(extra_bytes, 0);
   5522             const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   5523             // This equation accounts for the fact that even if we have exactly 4 bytes
   5524             // for the string, we still use the next u32 for the null terminator.
   5525             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5526 
   5527             if (constraint.len < 2 or constraint[0] != '=' or
   5528                 (constraint[1] == '{' and constraint[constraint.len - 1] != '}'))
   5529             {
   5530                 return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
   5531             }
   5532 
   5533             const is_reg = constraint[1] == '{';
   5534             if (is_reg) {
   5535                 const output_ty = if (output == .none) inst_ty else f.typeOf(output).childType(zcu);
   5536                 try writer.writeAll("register ");
   5537                 const output_local = try f.allocLocalValue(.{
   5538                     .ctype = try f.ctypeFromType(output_ty, .complete),
   5539                     .alignas = CType.AlignAs.fromAbiAlignment(output_ty.abiAlignment(zcu)),
   5540                 });
   5541                 try f.allocs.put(gpa, output_local.new_local, false);
   5542                 try f.object.dg.renderTypeAndName(writer, output_ty, output_local, .{}, .none, .complete);
   5543                 try writer.writeAll(" __asm(\"");
   5544                 try writer.writeAll(constraint["={".len .. constraint.len - "}".len]);
   5545                 try writer.writeAll("\")");
   5546                 if (f.wantSafety()) {
   5547                     try writer.writeAll(" = ");
   5548                     try f.writeCValue(writer, .{ .undef = output_ty }, .Other);
   5549                 }
   5550                 try writer.writeAll(";\n");
   5551             }
   5552         }
   5553         for (inputs) |input| {
   5554             const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]);
   5555             const constraint = mem.sliceTo(extra_bytes, 0);
   5556             const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   5557             // This equation accounts for the fact that even if we have exactly 4 bytes
   5558             // for the string, we still use the next u32 for the null terminator.
   5559             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5560 
   5561             if (constraint.len < 1 or mem.indexOfScalar(u8, "=+&%", constraint[0]) != null or
   5562                 (constraint[0] == '{' and constraint[constraint.len - 1] != '}'))
   5563             {
   5564                 return f.fail("CBE: constraint not supported: '{s}'", .{constraint});
   5565             }
   5566 
   5567             const is_reg = constraint[0] == '{';
   5568             const input_val = try f.resolveInst(input);
   5569             if (asmInputNeedsLocal(f, constraint, input_val)) {
   5570                 const input_ty = f.typeOf(input);
   5571                 if (is_reg) try writer.writeAll("register ");
   5572                 const input_local = try f.allocLocalValue(.{
   5573                     .ctype = try f.ctypeFromType(input_ty, .complete),
   5574                     .alignas = CType.AlignAs.fromAbiAlignment(input_ty.abiAlignment(zcu)),
   5575                 });
   5576                 try f.allocs.put(gpa, input_local.new_local, false);
   5577                 try f.object.dg.renderTypeAndName(writer, input_ty, input_local, Const, .none, .complete);
   5578                 if (is_reg) {
   5579                     try writer.writeAll(" __asm(\"");
   5580                     try writer.writeAll(constraint["{".len .. constraint.len - "}".len]);
   5581                     try writer.writeAll("\")");
   5582                 }
   5583                 try writer.writeAll(" = ");
   5584                 try f.writeCValue(writer, input_val, .Other);
   5585                 try writer.writeAll(";\n");
   5586             }
   5587         }
   5588         for (0..clobbers_len) |_| {
   5589             const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra.items[extra_i..]), 0);
   5590             // This equation accounts for the fact that even if we have exactly 4 bytes
   5591             // for the string, we still use the next u32 for the null terminator.
   5592             extra_i += clobber.len / 4 + 1;
   5593         }
   5594 
   5595         {
   5596             const asm_source = mem.sliceAsBytes(f.air.extra.items[extra_i..])[0..extra.data.source_len];
   5597 
   5598             var stack = std.heap.stackFallback(256, f.object.dg.gpa);
   5599             const allocator = stack.get();
   5600             const fixed_asm_source = try allocator.alloc(u8, asm_source.len);
   5601             defer allocator.free(fixed_asm_source);
   5602 
   5603             var src_i: usize = 0;
   5604             var dst_i: usize = 0;
   5605             while (true) {
   5606                 const literal = mem.sliceTo(asm_source[src_i..], '%');
   5607                 src_i += literal.len;
   5608 
   5609                 @memcpy(fixed_asm_source[dst_i..][0..literal.len], literal);
   5610                 dst_i += literal.len;
   5611 
   5612                 if (src_i >= asm_source.len) break;
   5613 
   5614                 src_i += 1;
   5615                 if (src_i >= asm_source.len)
   5616                     return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source});
   5617 
   5618                 fixed_asm_source[dst_i] = '%';
   5619                 dst_i += 1;
   5620 
   5621                 if (asm_source[src_i] != '[') {
   5622                     // This also handles %%
   5623                     fixed_asm_source[dst_i] = asm_source[src_i];
   5624                     src_i += 1;
   5625                     dst_i += 1;
   5626                     continue;
   5627                 }
   5628 
   5629                 const desc = mem.sliceTo(asm_source[src_i..], ']');
   5630                 if (mem.indexOfScalar(u8, desc, ':')) |colon| {
   5631                     const name = desc[0..colon];
   5632                     const modifier = desc[colon + 1 ..];
   5633 
   5634                     @memcpy(fixed_asm_source[dst_i..][0..modifier.len], modifier);
   5635                     dst_i += modifier.len;
   5636                     @memcpy(fixed_asm_source[dst_i..][0..name.len], name);
   5637                     dst_i += name.len;
   5638 
   5639                     src_i += desc.len;
   5640                     if (src_i >= asm_source.len)
   5641                         return f.fail("CBE: invalid inline asm string '{s}'", .{asm_source});
   5642                 }
   5643             }
   5644 
   5645             try writer.writeAll("__asm");
   5646             if (is_volatile) try writer.writeAll(" volatile");
   5647             try writer.print("({f}", .{fmtStringLiteral(fixed_asm_source[0..dst_i], null)});
   5648         }
   5649 
   5650         extra_i = constraints_extra_begin;
   5651         var locals_index = locals_begin;
   5652         try writer.writeByte(':');
   5653         for (outputs, 0..) |output, index| {
   5654             const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]);
   5655             const constraint = mem.sliceTo(extra_bytes, 0);
   5656             const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   5657             // This equation accounts for the fact that even if we have exactly 4 bytes
   5658             // for the string, we still use the next u32 for the null terminator.
   5659             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5660 
   5661             if (index > 0) try writer.writeByte(',');
   5662             try writer.writeByte(' ');
   5663             if (!mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
   5664             const is_reg = constraint[1] == '{';
   5665             try writer.print("{f}(", .{fmtStringLiteral(if (is_reg) "=r" else constraint, null)});
   5666             if (is_reg) {
   5667                 try f.writeCValue(writer, .{ .local = locals_index }, .Other);
   5668                 locals_index += 1;
   5669             } else if (output == .none) {
   5670                 try f.writeCValue(writer, inst_local, .FunctionArgument);
   5671             } else {
   5672                 try f.writeCValueDeref(writer, try f.resolveInst(output));
   5673             }
   5674             try writer.writeByte(')');
   5675         }
   5676         try writer.writeByte(':');
   5677         for (inputs, 0..) |input, index| {
   5678             const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]);
   5679             const constraint = mem.sliceTo(extra_bytes, 0);
   5680             const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   5681             // This equation accounts for the fact that even if we have exactly 4 bytes
   5682             // for the string, we still use the next u32 for the null terminator.
   5683             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5684 
   5685             if (index > 0) try writer.writeByte(',');
   5686             try writer.writeByte(' ');
   5687             if (!mem.eql(u8, name, "_")) try writer.print("[{s}]", .{name});
   5688 
   5689             const is_reg = constraint[0] == '{';
   5690             const input_val = try f.resolveInst(input);
   5691             try writer.print("{f}(", .{fmtStringLiteral(if (is_reg) "r" else constraint, null)});
   5692             try f.writeCValue(writer, if (asmInputNeedsLocal(f, constraint, input_val)) local: {
   5693                 const input_local_idx = locals_index;
   5694                 locals_index += 1;
   5695                 break :local .{ .local = input_local_idx };
   5696             } else input_val, .Other);
   5697             try writer.writeByte(')');
   5698         }
   5699         try writer.writeByte(':');
   5700         for (0..clobbers_len) |clobber_i| {
   5701             const clobber = mem.sliceTo(mem.sliceAsBytes(f.air.extra.items[extra_i..]), 0);
   5702             // This equation accounts for the fact that even if we have exactly 4 bytes
   5703             // for the string, we still use the next u32 for the null terminator.
   5704             extra_i += clobber.len / 4 + 1;
   5705 
   5706             if (clobber.len == 0) continue;
   5707 
   5708             if (clobber_i > 0) try writer.writeByte(',');
   5709             try writer.print(" {f}", .{fmtStringLiteral(clobber, null)});
   5710         }
   5711         try writer.writeAll(");\n");
   5712 
   5713         extra_i = constraints_extra_begin;
   5714         locals_index = locals_begin;
   5715         for (outputs) |output| {
   5716             const extra_bytes = mem.sliceAsBytes(f.air.extra.items[extra_i..]);
   5717             const constraint = mem.sliceTo(extra_bytes, 0);
   5718             const name = mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   5719             // This equation accounts for the fact that even if we have exactly 4 bytes
   5720             // for the string, we still use the next u32 for the null terminator.
   5721             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5722 
   5723             const is_reg = constraint[1] == '{';
   5724             if (is_reg) {
   5725                 try f.writeCValueDeref(writer, if (output == .none)
   5726                     .{ .local_ref = inst_local.new_local }
   5727                 else
   5728                     try f.resolveInst(output));
   5729                 try writer.writeAll(" = ");
   5730                 try f.writeCValue(writer, .{ .local = locals_index }, .Other);
   5731                 locals_index += 1;
   5732                 try writer.writeAll(";\n");
   5733             }
   5734         }
   5735 
   5736         break :result if (f.liveness.isUnused(inst)) .none else inst_local;
   5737     };
   5738 
   5739     var bt = iterateBigTomb(f, inst);
   5740     for (outputs) |output| {
   5741         if (output == .none) continue;
   5742         try bt.feed(output);
   5743     }
   5744     for (inputs) |input| {
   5745         try bt.feed(input);
   5746     }
   5747 
   5748     return result;
   5749 }
   5750 
   5751 fn airIsNull(
   5752     f: *Function,
   5753     inst: Air.Inst.Index,
   5754     operator: std.math.CompareOperator,
   5755     is_ptr: bool,
   5756 ) !CValue {
   5757     const pt = f.object.dg.pt;
   5758     const zcu = pt.zcu;
   5759     const ctype_pool = &f.object.dg.ctype_pool;
   5760     const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   5761 
   5762     const writer = f.object.writer();
   5763     const operand = try f.resolveInst(un_op);
   5764     try reap(f, inst, &.{un_op});
   5765 
   5766     const local = try f.allocLocal(inst, .bool);
   5767     const a = try Assignment.start(f, writer, .bool);
   5768     try f.writeCValue(writer, local, .Other);
   5769     try a.assign(f, writer);
   5770 
   5771     const operand_ty = f.typeOf(un_op);
   5772     const optional_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
   5773     const opt_ctype = try f.ctypeFromType(optional_ty, .complete);
   5774     const rhs = switch (opt_ctype.info(ctype_pool)) {
   5775         .basic, .pointer => rhs: {
   5776             if (is_ptr)
   5777                 try f.writeCValueDeref(writer, operand)
   5778             else
   5779                 try f.writeCValue(writer, operand, .Other);
   5780             break :rhs if (opt_ctype.isBool())
   5781                 "true"
   5782             else if (opt_ctype.isInteger())
   5783                 "0"
   5784             else
   5785                 "NULL";
   5786         },
   5787         .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   5788         .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
   5789             .is_null, .payload => rhs: {
   5790                 if (is_ptr)
   5791                     try f.writeCValueDerefMember(writer, operand, .{ .identifier = "is_null" })
   5792                 else
   5793                     try f.writeCValueMember(writer, operand, .{ .identifier = "is_null" });
   5794                 break :rhs "true";
   5795             },
   5796             .ptr, .len => rhs: {
   5797                 if (is_ptr)
   5798                     try f.writeCValueDerefMember(writer, operand, .{ .identifier = "ptr" })
   5799                 else
   5800                     try f.writeCValueMember(writer, operand, .{ .identifier = "ptr" });
   5801                 break :rhs "NULL";
   5802             },
   5803             else => unreachable,
   5804         },
   5805     };
   5806     try writer.writeAll(compareOperatorC(operator));
   5807     try writer.writeAll(rhs);
   5808     try a.end(f, writer);
   5809     return local;
   5810 }
   5811 
   5812 fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
   5813     const pt = f.object.dg.pt;
   5814     const zcu = pt.zcu;
   5815     const ctype_pool = &f.object.dg.ctype_pool;
   5816     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5817 
   5818     const inst_ty = f.typeOfIndex(inst);
   5819     const operand_ty = f.typeOf(ty_op.operand);
   5820     const opt_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
   5821     const opt_ctype = try f.ctypeFromType(opt_ty, .complete);
   5822     if (opt_ctype.isBool()) return if (is_ptr) .{ .undef = inst_ty } else .none;
   5823 
   5824     const operand = try f.resolveInst(ty_op.operand);
   5825     switch (opt_ctype.info(ctype_pool)) {
   5826         .basic, .pointer => return f.moveCValue(inst, inst_ty, operand),
   5827         .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   5828         .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
   5829             .is_null, .payload => {
   5830                 const writer = f.object.writer();
   5831                 const local = try f.allocLocal(inst, inst_ty);
   5832                 const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   5833                 try f.writeCValue(writer, local, .Other);
   5834                 try a.assign(f, writer);
   5835                 if (is_ptr) {
   5836                     try writer.writeByte('&');
   5837                     try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" });
   5838                 } else try f.writeCValueMember(writer, operand, .{ .identifier = "payload" });
   5839                 try a.end(f, writer);
   5840                 return local;
   5841             },
   5842             .ptr, .len => return f.moveCValue(inst, inst_ty, operand),
   5843             else => unreachable,
   5844         },
   5845     }
   5846 }
   5847 
   5848 fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
   5849     const pt = f.object.dg.pt;
   5850     const zcu = pt.zcu;
   5851     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5852     const writer = f.object.writer();
   5853     const operand = try f.resolveInst(ty_op.operand);
   5854     try reap(f, inst, &.{ty_op.operand});
   5855     const operand_ty = f.typeOf(ty_op.operand);
   5856 
   5857     const inst_ty = f.typeOfIndex(inst);
   5858     const opt_ctype = try f.ctypeFromType(operand_ty.childType(zcu), .complete);
   5859     switch (opt_ctype.info(&f.object.dg.ctype_pool)) {
   5860         .basic => {
   5861             const a = try Assignment.start(f, writer, opt_ctype);
   5862             try f.writeCValueDeref(writer, operand);
   5863             try a.assign(f, writer);
   5864             try f.object.dg.renderValue(writer, Value.false, .Other);
   5865             try a.end(f, writer);
   5866             return .none;
   5867         },
   5868         .pointer => {
   5869             if (f.liveness.isUnused(inst)) return .none;
   5870             const local = try f.allocLocal(inst, inst_ty);
   5871             const a = try Assignment.start(f, writer, opt_ctype);
   5872             try f.writeCValue(writer, local, .Other);
   5873             try a.assign(f, writer);
   5874             try f.writeCValue(writer, operand, .Other);
   5875             try a.end(f, writer);
   5876             return local;
   5877         },
   5878         .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   5879         .aggregate => {
   5880             {
   5881                 const a = try Assignment.start(f, writer, opt_ctype);
   5882                 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "is_null" });
   5883                 try a.assign(f, writer);
   5884                 try f.object.dg.renderValue(writer, Value.false, .Other);
   5885                 try a.end(f, writer);
   5886             }
   5887             if (f.liveness.isUnused(inst)) return .none;
   5888             const local = try f.allocLocal(inst, inst_ty);
   5889             const a = try Assignment.start(f, writer, opt_ctype);
   5890             try f.writeCValue(writer, local, .Other);
   5891             try a.assign(f, writer);
   5892             try writer.writeByte('&');
   5893             try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" });
   5894             try a.end(f, writer);
   5895             return local;
   5896         },
   5897     }
   5898 }
   5899 
   5900 fn fieldLocation(
   5901     container_ptr_ty: Type,
   5902     field_ptr_ty: Type,
   5903     field_index: u32,
   5904     pt: Zcu.PerThread,
   5905 ) union(enum) {
   5906     begin: void,
   5907     field: CValue,
   5908     byte_offset: u64,
   5909 } {
   5910     const zcu = pt.zcu;
   5911     const ip = &zcu.intern_pool;
   5912     const container_ty: Type = .fromInterned(ip.indexToKey(container_ptr_ty.toIntern()).ptr_type.child);
   5913     switch (ip.indexToKey(container_ty.toIntern())) {
   5914         .struct_type => {
   5915             const loaded_struct = ip.loadStructType(container_ty.toIntern());
   5916             return switch (loaded_struct.layout) {
   5917                 .auto, .@"extern" => if (!container_ty.hasRuntimeBitsIgnoreComptime(zcu))
   5918                     .begin
   5919                 else if (!field_ptr_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu))
   5920                     .{ .byte_offset = loaded_struct.offsets.get(ip)[field_index] }
   5921                 else
   5922                     .{ .field = if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name|
   5923                         .{ .identifier = field_name.toSlice(ip) }
   5924                     else
   5925                         .{ .field = field_index } },
   5926                 .@"packed" => if (field_ptr_ty.ptrInfo(zcu).packed_offset.host_size == 0)
   5927                     .{ .byte_offset = @divExact(pt.structPackedFieldBitOffset(loaded_struct, field_index) +
   5928                         container_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset, 8) }
   5929                 else
   5930                     .begin,
   5931             };
   5932         },
   5933         .tuple_type => return if (!container_ty.hasRuntimeBitsIgnoreComptime(zcu))
   5934             .begin
   5935         else if (!field_ptr_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu))
   5936             .{ .byte_offset = container_ty.structFieldOffset(field_index, zcu) }
   5937         else
   5938             .{ .field = .{ .field = field_index } },
   5939         .union_type => {
   5940             const loaded_union = ip.loadUnionType(container_ty.toIntern());
   5941             switch (loaded_union.flagsUnordered(ip).layout) {
   5942                 .auto, .@"extern" => {
   5943                     const field_ty: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
   5944                     if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu))
   5945                         return if (loaded_union.hasTag(ip) and !container_ty.unionHasAllZeroBitFieldTypes(zcu))
   5946                             .{ .field = .{ .identifier = "payload" } }
   5947                         else
   5948                             .begin;
   5949                     const field_name = loaded_union.loadTagType(ip).names.get(ip)[field_index];
   5950                     return .{ .field = if (loaded_union.hasTag(ip))
   5951                         .{ .payload_identifier = field_name.toSlice(ip) }
   5952                     else
   5953                         .{ .identifier = field_name.toSlice(ip) } };
   5954                 },
   5955                 .@"packed" => return .begin,
   5956             }
   5957         },
   5958         .ptr_type => |ptr_info| switch (ptr_info.flags.size) {
   5959             .one, .many, .c => unreachable,
   5960             .slice => switch (field_index) {
   5961                 0 => return .{ .field = .{ .identifier = "ptr" } },
   5962                 1 => return .{ .field = .{ .identifier = "len" } },
   5963                 else => unreachable,
   5964             },
   5965         },
   5966         else => unreachable,
   5967     }
   5968 }
   5969 
   5970 fn airStructFieldPtr(f: *Function, inst: Air.Inst.Index) !CValue {
   5971     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5972     const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
   5973 
   5974     const container_ptr_val = try f.resolveInst(extra.struct_operand);
   5975     try reap(f, inst, &.{extra.struct_operand});
   5976     const container_ptr_ty = f.typeOf(extra.struct_operand);
   5977     return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, extra.field_index);
   5978 }
   5979 
   5980 fn airStructFieldPtrIndex(f: *Function, inst: Air.Inst.Index, index: u8) !CValue {
   5981     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5982 
   5983     const container_ptr_val = try f.resolveInst(ty_op.operand);
   5984     try reap(f, inst, &.{ty_op.operand});
   5985     const container_ptr_ty = f.typeOf(ty_op.operand);
   5986     return fieldPtr(f, inst, container_ptr_ty, container_ptr_val, index);
   5987 }
   5988 
   5989 fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
   5990     const pt = f.object.dg.pt;
   5991     const zcu = pt.zcu;
   5992     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5993     const extra = f.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
   5994 
   5995     const container_ptr_ty = f.typeOfIndex(inst);
   5996     const container_ty = container_ptr_ty.childType(zcu);
   5997 
   5998     const field_ptr_ty = f.typeOf(extra.field_ptr);
   5999     const field_ptr_val = try f.resolveInst(extra.field_ptr);
   6000     try reap(f, inst, &.{extra.field_ptr});
   6001 
   6002     const writer = f.object.writer();
   6003     const local = try f.allocLocal(inst, container_ptr_ty);
   6004     try f.writeCValue(writer, local, .Other);
   6005     try writer.writeAll(" = (");
   6006     try f.renderType(writer, container_ptr_ty);
   6007     try writer.writeByte(')');
   6008 
   6009     switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, pt)) {
   6010         .begin => try f.writeCValue(writer, field_ptr_val, .Other),
   6011         .field => |field| {
   6012             const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);
   6013 
   6014             try writer.writeAll("((");
   6015             try f.renderType(writer, u8_ptr_ty);
   6016             try writer.writeByte(')');
   6017             try f.writeCValue(writer, field_ptr_val, .Other);
   6018             try writer.writeAll(" - offsetof(");
   6019             try f.renderType(writer, container_ty);
   6020             try writer.writeAll(", ");
   6021             try f.writeCValue(writer, field, .Other);
   6022             try writer.writeAll("))");
   6023         },
   6024         .byte_offset => |byte_offset| {
   6025             const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);
   6026 
   6027             try writer.writeAll("((");
   6028             try f.renderType(writer, u8_ptr_ty);
   6029             try writer.writeByte(')');
   6030             try f.writeCValue(writer, field_ptr_val, .Other);
   6031             try writer.print(" - {f})", .{
   6032                 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
   6033             });
   6034         },
   6035     }
   6036 
   6037     try writer.writeAll(";\n");
   6038     return local;
   6039 }
   6040 
   6041 fn fieldPtr(
   6042     f: *Function,
   6043     inst: Air.Inst.Index,
   6044     container_ptr_ty: Type,
   6045     container_ptr_val: CValue,
   6046     field_index: u32,
   6047 ) !CValue {
   6048     const pt = f.object.dg.pt;
   6049     const zcu = pt.zcu;
   6050     const container_ty = container_ptr_ty.childType(zcu);
   6051     const field_ptr_ty = f.typeOfIndex(inst);
   6052 
   6053     // Ensure complete type definition is visible before accessing fields.
   6054     _ = try f.ctypeFromType(container_ty, .complete);
   6055 
   6056     const writer = f.object.writer();
   6057     const local = try f.allocLocal(inst, field_ptr_ty);
   6058     try f.writeCValue(writer, local, .Other);
   6059     try writer.writeAll(" = (");
   6060     try f.renderType(writer, field_ptr_ty);
   6061     try writer.writeByte(')');
   6062 
   6063     switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, pt)) {
   6064         .begin => try f.writeCValue(writer, container_ptr_val, .Other),
   6065         .field => |field| {
   6066             try writer.writeByte('&');
   6067             try f.writeCValueDerefMember(writer, container_ptr_val, field);
   6068         },
   6069         .byte_offset => |byte_offset| {
   6070             const u8_ptr_ty = try pt.adjustPtrTypeChild(field_ptr_ty, .u8);
   6071 
   6072             try writer.writeAll("((");
   6073             try f.renderType(writer, u8_ptr_ty);
   6074             try writer.writeByte(')');
   6075             try f.writeCValue(writer, container_ptr_val, .Other);
   6076             try writer.print(" + {f})", .{
   6077                 try f.fmtIntLiteralDec(try pt.intValue(.usize, byte_offset)),
   6078             });
   6079         },
   6080     }
   6081 
   6082     try writer.writeAll(";\n");
   6083     return local;
   6084 }
   6085 
   6086 fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
   6087     const pt = f.object.dg.pt;
   6088     const zcu = pt.zcu;
   6089     const ip = &zcu.intern_pool;
   6090     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   6091     const extra = f.air.extraData(Air.StructField, ty_pl.payload).data;
   6092 
   6093     const inst_ty = f.typeOfIndex(inst);
   6094     if (!inst_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   6095         try reap(f, inst, &.{extra.struct_operand});
   6096         return .none;
   6097     }
   6098 
   6099     const struct_byval = try f.resolveInst(extra.struct_operand);
   6100     try reap(f, inst, &.{extra.struct_operand});
   6101     const struct_ty = f.typeOf(extra.struct_operand);
   6102     const writer = f.object.writer();
   6103 
   6104     // Ensure complete type definition is visible before accessing fields.
   6105     _ = try f.ctypeFromType(struct_ty, .complete);
   6106 
   6107     const field_name: CValue = switch (ip.indexToKey(struct_ty.toIntern())) {
   6108         .struct_type => field_name: {
   6109             const loaded_struct = ip.loadStructType(struct_ty.toIntern());
   6110             switch (loaded_struct.layout) {
   6111                 .auto, .@"extern" => break :field_name if (loaded_struct.fieldName(ip, extra.field_index).unwrap()) |field_name|
   6112                     .{ .identifier = field_name.toSlice(ip) }
   6113                 else
   6114                     .{ .field = extra.field_index },
   6115                 .@"packed" => {
   6116                     const int_info = struct_ty.intInfo(zcu);
   6117 
   6118                     const bit_offset_ty = try pt.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1));
   6119 
   6120                     const bit_offset = pt.structPackedFieldBitOffset(loaded_struct, extra.field_index);
   6121 
   6122                     const field_int_signedness = if (inst_ty.isAbiInt(zcu))
   6123                         inst_ty.intInfo(zcu).signedness
   6124                     else
   6125                         .unsigned;
   6126                     const field_int_ty = try pt.intType(field_int_signedness, @as(u16, @intCast(inst_ty.bitSize(zcu))));
   6127 
   6128                     const temp_local = try f.allocLocal(inst, field_int_ty);
   6129                     try f.writeCValue(writer, temp_local, .Other);
   6130                     try writer.writeAll(" = zig_wrap_");
   6131                     try f.object.dg.renderTypeForBuiltinFnName(writer, field_int_ty);
   6132                     try writer.writeAll("((");
   6133                     try f.renderType(writer, field_int_ty);
   6134                     try writer.writeByte(')');
   6135                     const cant_cast = int_info.bits > 64;
   6136                     if (cant_cast) {
   6137                         if (field_int_ty.bitSize(zcu) > 64) return f.fail("TODO: C backend: implement casting between types > 64 bits", .{});
   6138                         try writer.writeAll("zig_lo_");
   6139                         try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty);
   6140                         try writer.writeByte('(');
   6141                     }
   6142                     if (bit_offset > 0) {
   6143                         try writer.writeAll("zig_shr_");
   6144                         try f.object.dg.renderTypeForBuiltinFnName(writer, struct_ty);
   6145                         try writer.writeByte('(');
   6146                     }
   6147                     try f.writeCValue(writer, struct_byval, .Other);
   6148                     if (bit_offset > 0) try writer.print(", {f})", .{
   6149                         try f.fmtIntLiteralDec(try pt.intValue(bit_offset_ty, bit_offset)),
   6150                     });
   6151                     if (cant_cast) try writer.writeByte(')');
   6152                     try f.object.dg.renderBuiltinInfo(writer, field_int_ty, .bits);
   6153                     try writer.writeAll(");\n");
   6154                     if (inst_ty.eql(field_int_ty, zcu)) return temp_local;
   6155 
   6156                     const local = try f.allocLocal(inst, inst_ty);
   6157                     if (local.new_local != temp_local.new_local) {
   6158                         try writer.writeAll("memcpy(");
   6159                         try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument);
   6160                         try writer.writeAll(", ");
   6161                         try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument);
   6162                         try writer.writeAll(", sizeof(");
   6163                         try f.renderType(writer, inst_ty);
   6164                         try writer.writeAll("));\n");
   6165                     }
   6166                     try freeLocal(f, inst, temp_local.new_local, null);
   6167                     return local;
   6168                 },
   6169             }
   6170         },
   6171         .tuple_type => .{ .field = extra.field_index },
   6172         .union_type => field_name: {
   6173             const loaded_union = ip.loadUnionType(struct_ty.toIntern());
   6174             switch (loaded_union.flagsUnordered(ip).layout) {
   6175                 .auto, .@"extern" => {
   6176                     const name = loaded_union.loadTagType(ip).names.get(ip)[extra.field_index];
   6177                     break :field_name if (loaded_union.hasTag(ip))
   6178                         .{ .payload_identifier = name.toSlice(ip) }
   6179                     else
   6180                         .{ .identifier = name.toSlice(ip) };
   6181                 },
   6182                 .@"packed" => {
   6183                     const operand_lval = if (struct_byval == .constant) blk: {
   6184                         const operand_local = try f.allocLocal(inst, struct_ty);
   6185                         try f.writeCValue(writer, operand_local, .Other);
   6186                         try writer.writeAll(" = ");
   6187                         try f.writeCValue(writer, struct_byval, .Other);
   6188                         try writer.writeAll(";\n");
   6189                         break :blk operand_local;
   6190                     } else struct_byval;
   6191                     const local = try f.allocLocal(inst, inst_ty);
   6192                     if (switch (local) {
   6193                         .new_local, .local => |local_index| switch (operand_lval) {
   6194                             .new_local, .local => |operand_local_index| local_index != operand_local_index,
   6195                             else => true,
   6196                         },
   6197                         else => true,
   6198                     }) {
   6199                         try writer.writeAll("memcpy(&");
   6200                         try f.writeCValue(writer, local, .Other);
   6201                         try writer.writeAll(", &");
   6202                         try f.writeCValue(writer, operand_lval, .Other);
   6203                         try writer.writeAll(", sizeof(");
   6204                         try f.renderType(writer, inst_ty);
   6205                         try writer.writeAll("));\n");
   6206                     }
   6207                     try f.freeCValue(inst, operand_lval);
   6208                     return local;
   6209                 },
   6210             }
   6211         },
   6212         else => unreachable,
   6213     };
   6214 
   6215     const local = try f.allocLocal(inst, inst_ty);
   6216     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   6217     try f.writeCValue(writer, local, .Other);
   6218     try a.assign(f, writer);
   6219     try f.writeCValueMember(writer, struct_byval, field_name);
   6220     try a.end(f, writer);
   6221     return local;
   6222 }
   6223 
   6224 /// *(E!T) -> E
   6225 /// Note that the result is never a pointer.
   6226 fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
   6227     const pt = f.object.dg.pt;
   6228     const zcu = pt.zcu;
   6229     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6230 
   6231     const inst_ty = f.typeOfIndex(inst);
   6232     const operand = try f.resolveInst(ty_op.operand);
   6233     const operand_ty = f.typeOf(ty_op.operand);
   6234     try reap(f, inst, &.{ty_op.operand});
   6235 
   6236     const operand_is_ptr = operand_ty.zigTypeTag(zcu) == .pointer;
   6237     const error_union_ty = if (operand_is_ptr) operand_ty.childType(zcu) else operand_ty;
   6238     const error_ty = error_union_ty.errorUnionSet(zcu);
   6239     const payload_ty = error_union_ty.errorUnionPayload(zcu);
   6240     const local = try f.allocLocal(inst, inst_ty);
   6241 
   6242     if (!payload_ty.hasRuntimeBits(zcu) and operand == .local and operand.local == local.new_local) {
   6243         // The store will be 'x = x'; elide it.
   6244         return local;
   6245     }
   6246 
   6247     const writer = f.object.writer();
   6248     try f.writeCValue(writer, local, .Other);
   6249     try writer.writeAll(" = ");
   6250 
   6251     if (!payload_ty.hasRuntimeBits(zcu))
   6252         try f.writeCValue(writer, operand, .Other)
   6253     else if (error_ty.errorSetIsEmpty(zcu))
   6254         try writer.print("{f}", .{
   6255             try f.fmtIntLiteralDec(try pt.intValue(try pt.errorIntType(), 0)),
   6256         })
   6257     else if (operand_is_ptr)
   6258         try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" })
   6259     else
   6260         try f.writeCValueMember(writer, operand, .{ .identifier = "error" });
   6261     try writer.writeAll(";\n");
   6262     return local;
   6263 }
   6264 
   6265 fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue {
   6266     const pt = f.object.dg.pt;
   6267     const zcu = pt.zcu;
   6268     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6269 
   6270     const inst_ty = f.typeOfIndex(inst);
   6271     const operand = try f.resolveInst(ty_op.operand);
   6272     try reap(f, inst, &.{ty_op.operand});
   6273     const operand_ty = f.typeOf(ty_op.operand);
   6274     const error_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
   6275 
   6276     const writer = f.object.writer();
   6277     if (!error_union_ty.errorUnionPayload(zcu).hasRuntimeBits(zcu)) {
   6278         if (!is_ptr) return .none;
   6279 
   6280         const local = try f.allocLocal(inst, inst_ty);
   6281         try f.writeCValue(writer, local, .Other);
   6282         try writer.writeAll(" = (");
   6283         try f.renderType(writer, inst_ty);
   6284         try writer.writeByte(')');
   6285         try f.writeCValue(writer, operand, .Other);
   6286         try writer.writeAll(";\n");
   6287         return local;
   6288     }
   6289 
   6290     const local = try f.allocLocal(inst, inst_ty);
   6291     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   6292     try f.writeCValue(writer, local, .Other);
   6293     try a.assign(f, writer);
   6294     if (is_ptr) {
   6295         try writer.writeByte('&');
   6296         try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" });
   6297     } else try f.writeCValueMember(writer, operand, .{ .identifier = "payload" });
   6298     try a.end(f, writer);
   6299     return local;
   6300 }
   6301 
   6302 fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue {
   6303     const ctype_pool = &f.object.dg.ctype_pool;
   6304     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6305 
   6306     const inst_ty = f.typeOfIndex(inst);
   6307     const inst_ctype = try f.ctypeFromType(inst_ty, .complete);
   6308     if (inst_ctype.isBool()) return .{ .constant = Value.true };
   6309 
   6310     const operand = try f.resolveInst(ty_op.operand);
   6311     switch (inst_ctype.info(ctype_pool)) {
   6312         .basic, .pointer => return f.moveCValue(inst, inst_ty, operand),
   6313         .aligned, .array, .vector, .fwd_decl, .function => unreachable,
   6314         .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) {
   6315             .is_null, .payload => {
   6316                 const operand_ctype = try f.ctypeFromType(f.typeOf(ty_op.operand), .complete);
   6317                 const writer = f.object.writer();
   6318                 const local = try f.allocLocal(inst, inst_ty);
   6319                 {
   6320                     const a = try Assignment.start(f, writer, .bool);
   6321                     try f.writeCValueMember(writer, local, .{ .identifier = "is_null" });
   6322                     try a.assign(f, writer);
   6323                     try writer.writeAll("false");
   6324                     try a.end(f, writer);
   6325                 }
   6326                 {
   6327                     const a = try Assignment.start(f, writer, operand_ctype);
   6328                     try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
   6329                     try a.assign(f, writer);
   6330                     try f.writeCValue(writer, operand, .Other);
   6331                     try a.end(f, writer);
   6332                 }
   6333                 return local;
   6334             },
   6335             .ptr, .len => return f.moveCValue(inst, inst_ty, operand),
   6336             else => unreachable,
   6337         },
   6338     }
   6339 }
   6340 
   6341 fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
   6342     const pt = f.object.dg.pt;
   6343     const zcu = pt.zcu;
   6344     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6345 
   6346     const inst_ty = f.typeOfIndex(inst);
   6347     const payload_ty = inst_ty.errorUnionPayload(zcu);
   6348     const repr_is_err = !payload_ty.hasRuntimeBitsIgnoreComptime(zcu);
   6349     const err_ty = inst_ty.errorUnionSet(zcu);
   6350     const err = try f.resolveInst(ty_op.operand);
   6351     try reap(f, inst, &.{ty_op.operand});
   6352 
   6353     const writer = f.object.writer();
   6354     const local = try f.allocLocal(inst, inst_ty);
   6355 
   6356     if (repr_is_err and err == .local and err.local == local.new_local) {
   6357         // The store will be 'x = x'; elide it.
   6358         return local;
   6359     }
   6360 
   6361     if (!repr_is_err) {
   6362         const a = try Assignment.start(f, writer, try f.ctypeFromType(payload_ty, .complete));
   6363         try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
   6364         try a.assign(f, writer);
   6365         try f.object.dg.renderUndefValue(writer, payload_ty, .Other);
   6366         try a.end(f, writer);
   6367     }
   6368     {
   6369         const a = try Assignment.start(f, writer, try f.ctypeFromType(err_ty, .complete));
   6370         if (repr_is_err)
   6371             try f.writeCValue(writer, local, .Other)
   6372         else
   6373             try f.writeCValueMember(writer, local, .{ .identifier = "error" });
   6374         try a.assign(f, writer);
   6375         try f.writeCValue(writer, err, .Other);
   6376         try a.end(f, writer);
   6377     }
   6378     return local;
   6379 }
   6380 
   6381 fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
   6382     const pt = f.object.dg.pt;
   6383     const zcu = pt.zcu;
   6384     const writer = f.object.writer();
   6385     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6386     const inst_ty = f.typeOfIndex(inst);
   6387     const operand = try f.resolveInst(ty_op.operand);
   6388     const operand_ty = f.typeOf(ty_op.operand);
   6389     const error_union_ty = operand_ty.childType(zcu);
   6390 
   6391     const payload_ty = error_union_ty.errorUnionPayload(zcu);
   6392     const err_int_ty = try pt.errorIntType();
   6393     const no_err = try pt.intValue(err_int_ty, 0);
   6394     try reap(f, inst, &.{ty_op.operand});
   6395 
   6396     // First, set the non-error value.
   6397     if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   6398         const a = try Assignment.start(f, writer, try f.ctypeFromType(operand_ty, .complete));
   6399         try f.writeCValueDeref(writer, operand);
   6400         try a.assign(f, writer);
   6401         try writer.print("{f}", .{try f.fmtIntLiteralDec(no_err)});
   6402         try a.end(f, writer);
   6403         return .none;
   6404     }
   6405     {
   6406         const a = try Assignment.start(f, writer, try f.ctypeFromType(err_int_ty, .complete));
   6407         try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" });
   6408         try a.assign(f, writer);
   6409         try writer.print("{f}", .{try f.fmtIntLiteralDec(no_err)});
   6410         try a.end(f, writer);
   6411     }
   6412 
   6413     // Then return the payload pointer (only if it is used)
   6414     if (f.liveness.isUnused(inst)) return .none;
   6415 
   6416     const local = try f.allocLocal(inst, inst_ty);
   6417     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   6418     try f.writeCValue(writer, local, .Other);
   6419     try a.assign(f, writer);
   6420     try writer.writeByte('&');
   6421     try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" });
   6422     try a.end(f, writer);
   6423     return local;
   6424 }
   6425 
   6426 fn airErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
   6427     _ = inst;
   6428     return f.fail("TODO: C backend: implement airErrReturnTrace", .{});
   6429 }
   6430 
   6431 fn airSetErrReturnTrace(f: *Function, inst: Air.Inst.Index) !CValue {
   6432     _ = inst;
   6433     return f.fail("TODO: C backend: implement airSetErrReturnTrace", .{});
   6434 }
   6435 
   6436 fn airSaveErrReturnTraceIndex(f: *Function, inst: Air.Inst.Index) !CValue {
   6437     _ = inst;
   6438     return f.fail("TODO: C backend: implement airSaveErrReturnTraceIndex", .{});
   6439 }
   6440 
   6441 fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
   6442     const pt = f.object.dg.pt;
   6443     const zcu = pt.zcu;
   6444     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6445 
   6446     const inst_ty = f.typeOfIndex(inst);
   6447     const payload_ty = inst_ty.errorUnionPayload(zcu);
   6448     const payload = try f.resolveInst(ty_op.operand);
   6449     const repr_is_err = !payload_ty.hasRuntimeBitsIgnoreComptime(zcu);
   6450     const err_ty = inst_ty.errorUnionSet(zcu);
   6451     try reap(f, inst, &.{ty_op.operand});
   6452 
   6453     const writer = f.object.writer();
   6454     const local = try f.allocLocal(inst, inst_ty);
   6455     if (!repr_is_err) {
   6456         const a = try Assignment.start(f, writer, try f.ctypeFromType(payload_ty, .complete));
   6457         try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
   6458         try a.assign(f, writer);
   6459         try f.writeCValue(writer, payload, .Other);
   6460         try a.end(f, writer);
   6461     }
   6462     {
   6463         const a = try Assignment.start(f, writer, try f.ctypeFromType(err_ty, .complete));
   6464         if (repr_is_err)
   6465             try f.writeCValue(writer, local, .Other)
   6466         else
   6467             try f.writeCValueMember(writer, local, .{ .identifier = "error" });
   6468         try a.assign(f, writer);
   6469         try f.object.dg.renderValue(writer, try pt.intValue(try pt.errorIntType(), 0), .Other);
   6470         try a.end(f, writer);
   6471     }
   6472     return local;
   6473 }
   6474 
   6475 fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const u8) !CValue {
   6476     const pt = f.object.dg.pt;
   6477     const zcu = pt.zcu;
   6478     const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   6479 
   6480     const writer = f.object.writer();
   6481     const operand = try f.resolveInst(un_op);
   6482     try reap(f, inst, &.{un_op});
   6483     const operand_ty = f.typeOf(un_op);
   6484     const local = try f.allocLocal(inst, .bool);
   6485     const err_union_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty;
   6486     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   6487     const error_ty = err_union_ty.errorUnionSet(zcu);
   6488 
   6489     const a = try Assignment.start(f, writer, .bool);
   6490     try f.writeCValue(writer, local, .Other);
   6491     try a.assign(f, writer);
   6492     const err_int_ty = try pt.errorIntType();
   6493     if (!error_ty.errorSetIsEmpty(zcu))
   6494         if (payload_ty.hasRuntimeBits(zcu))
   6495             if (is_ptr)
   6496                 try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" })
   6497             else
   6498                 try f.writeCValueMember(writer, operand, .{ .identifier = "error" })
   6499         else
   6500             try f.writeCValue(writer, operand, .Other)
   6501     else
   6502         try f.object.dg.renderValue(writer, try pt.intValue(err_int_ty, 0), .Other);
   6503     try writer.writeByte(' ');
   6504     try writer.writeAll(operator);
   6505     try writer.writeByte(' ');
   6506     try f.object.dg.renderValue(writer, try pt.intValue(err_int_ty, 0), .Other);
   6507     try a.end(f, writer);
   6508     return local;
   6509 }
   6510 
   6511 fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue {
   6512     const pt = f.object.dg.pt;
   6513     const zcu = pt.zcu;
   6514     const ctype_pool = &f.object.dg.ctype_pool;
   6515     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6516 
   6517     const operand = try f.resolveInst(ty_op.operand);
   6518     try reap(f, inst, &.{ty_op.operand});
   6519     const inst_ty = f.typeOfIndex(inst);
   6520     const ptr_ty = inst_ty.slicePtrFieldType(zcu);
   6521     const writer = f.object.writer();
   6522     const local = try f.allocLocal(inst, inst_ty);
   6523     const operand_ty = f.typeOf(ty_op.operand);
   6524     const array_ty = operand_ty.childType(zcu);
   6525 
   6526     {
   6527         const a = try Assignment.start(f, writer, try f.ctypeFromType(ptr_ty, .complete));
   6528         try f.writeCValueMember(writer, local, .{ .identifier = "ptr" });
   6529         try a.assign(f, writer);
   6530         if (operand == .undef) {
   6531             try f.writeCValue(writer, .{ .undef = inst_ty.slicePtrFieldType(zcu) }, .Other);
   6532         } else {
   6533             const ptr_ctype = try f.ctypeFromType(ptr_ty, .complete);
   6534             const ptr_child_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype;
   6535             const elem_ty = array_ty.childType(zcu);
   6536             const elem_ctype = try f.ctypeFromType(elem_ty, .complete);
   6537             if (!ptr_child_ctype.eql(elem_ctype)) {
   6538                 try writer.writeByte('(');
   6539                 try f.renderCType(writer, ptr_ctype);
   6540                 try writer.writeByte(')');
   6541             }
   6542             const operand_ctype = try f.ctypeFromType(operand_ty, .complete);
   6543             const operand_child_ctype = operand_ctype.info(ctype_pool).pointer.elem_ctype;
   6544             if (operand_child_ctype.info(ctype_pool) == .array) {
   6545                 try writer.writeByte('&');
   6546                 try f.writeCValueDeref(writer, operand);
   6547                 try writer.print("[{f}]", .{try f.fmtIntLiteralDec(.zero_usize)});
   6548             } else try f.writeCValue(writer, operand, .Other);
   6549         }
   6550         try a.end(f, writer);
   6551     }
   6552     {
   6553         const a = try Assignment.start(f, writer, .usize);
   6554         try f.writeCValueMember(writer, local, .{ .identifier = "len" });
   6555         try a.assign(f, writer);
   6556         try writer.print("{f}", .{
   6557             try f.fmtIntLiteralDec(try pt.intValue(.usize, array_ty.arrayLen(zcu))),
   6558         });
   6559         try a.end(f, writer);
   6560     }
   6561 
   6562     return local;
   6563 }
   6564 
   6565 fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue {
   6566     const pt = f.object.dg.pt;
   6567     const zcu = pt.zcu;
   6568     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   6569 
   6570     const inst_ty = f.typeOfIndex(inst);
   6571     const inst_scalar_ty = inst_ty.scalarType(zcu);
   6572     const operand = try f.resolveInst(ty_op.operand);
   6573     try reap(f, inst, &.{ty_op.operand});
   6574     const operand_ty = f.typeOf(ty_op.operand);
   6575     const scalar_ty = operand_ty.scalarType(zcu);
   6576     const target = &f.object.dg.mod.resolved_target.result;
   6577     const operation = if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isRuntimeFloat())
   6578         if (inst_scalar_ty.floatBits(target) < scalar_ty.floatBits(target)) "trunc" else "extend"
   6579     else if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat())
   6580         if (inst_scalar_ty.isSignedInt(zcu)) "fix" else "fixuns"
   6581     else if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isInt(zcu))
   6582         if (scalar_ty.isSignedInt(zcu)) "float" else "floatun"
   6583     else
   6584         unreachable;
   6585 
   6586     const writer = f.object.writer();
   6587     const local = try f.allocLocal(inst, inst_ty);
   6588     const v = try Vectorize.start(f, inst, writer, operand_ty);
   6589     const a = try Assignment.start(f, writer, try f.ctypeFromType(scalar_ty, .complete));
   6590     try f.writeCValue(writer, local, .Other);
   6591     try v.elem(f, writer);
   6592     try a.assign(f, writer);
   6593     if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) {
   6594         try writer.writeAll("zig_wrap_");
   6595         try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty);
   6596         try writer.writeByte('(');
   6597     }
   6598     try writer.writeAll("zig_");
   6599     try writer.writeAll(operation);
   6600     try writer.writeAll(compilerRtAbbrev(scalar_ty, zcu, target));
   6601     try writer.writeAll(compilerRtAbbrev(inst_scalar_ty, zcu, target));
   6602     try writer.writeByte('(');
   6603     try f.writeCValue(writer, operand, .FunctionArgument);
   6604     try v.elem(f, writer);
   6605     try writer.writeByte(')');
   6606     if (inst_scalar_ty.isInt(zcu) and scalar_ty.isRuntimeFloat()) {
   6607         try f.object.dg.renderBuiltinInfo(writer, inst_scalar_ty, .bits);
   6608         try writer.writeByte(')');
   6609     }
   6610     try a.end(f, writer);
   6611     try v.end(f, inst, writer);
   6612 
   6613     return local;
   6614 }
   6615 
   6616 fn airUnBuiltinCall(
   6617     f: *Function,
   6618     inst: Air.Inst.Index,
   6619     operand_ref: Air.Inst.Ref,
   6620     operation: []const u8,
   6621     info: BuiltinInfo,
   6622 ) !CValue {
   6623     const pt = f.object.dg.pt;
   6624     const zcu = pt.zcu;
   6625 
   6626     const operand = try f.resolveInst(operand_ref);
   6627     try reap(f, inst, &.{operand_ref});
   6628     const inst_ty = f.typeOfIndex(inst);
   6629     const inst_scalar_ty = inst_ty.scalarType(zcu);
   6630     const operand_ty = f.typeOf(operand_ref);
   6631     const scalar_ty = operand_ty.scalarType(zcu);
   6632 
   6633     const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
   6634     const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
   6635 
   6636     const writer = f.object.writer();
   6637     const local = try f.allocLocal(inst, inst_ty);
   6638     const v = try Vectorize.start(f, inst, writer, operand_ty);
   6639     if (!ref_ret) {
   6640         try f.writeCValue(writer, local, .Other);
   6641         try v.elem(f, writer);
   6642         try writer.writeAll(" = ");
   6643     }
   6644     try writer.print("zig_{s}_", .{operation});
   6645     try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
   6646     try writer.writeByte('(');
   6647     if (ref_ret) {
   6648         try f.writeCValue(writer, local, .FunctionArgument);
   6649         try v.elem(f, writer);
   6650         try writer.writeAll(", ");
   6651     }
   6652     try f.writeCValue(writer, operand, .FunctionArgument);
   6653     try v.elem(f, writer);
   6654     try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info);
   6655     try writer.writeAll(");\n");
   6656     try v.end(f, inst, writer);
   6657 
   6658     return local;
   6659 }
   6660 
   6661 fn airBinBuiltinCall(
   6662     f: *Function,
   6663     inst: Air.Inst.Index,
   6664     operation: []const u8,
   6665     info: BuiltinInfo,
   6666 ) !CValue {
   6667     const pt = f.object.dg.pt;
   6668     const zcu = pt.zcu;
   6669     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   6670 
   6671     const operand_ty = f.typeOf(bin_op.lhs);
   6672     const operand_ctype = try f.ctypeFromType(operand_ty, .complete);
   6673     const is_big = operand_ctype.info(&f.object.dg.ctype_pool) == .array;
   6674 
   6675     const lhs = try f.resolveInst(bin_op.lhs);
   6676     const rhs = try f.resolveInst(bin_op.rhs);
   6677     if (!is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   6678 
   6679     const inst_ty = f.typeOfIndex(inst);
   6680     const inst_scalar_ty = inst_ty.scalarType(zcu);
   6681     const scalar_ty = operand_ty.scalarType(zcu);
   6682 
   6683     const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
   6684     const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
   6685 
   6686     const writer = f.object.writer();
   6687     const local = try f.allocLocal(inst, inst_ty);
   6688     if (is_big) try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   6689     const v = try Vectorize.start(f, inst, writer, operand_ty);
   6690     if (!ref_ret) {
   6691         try f.writeCValue(writer, local, .Other);
   6692         try v.elem(f, writer);
   6693         try writer.writeAll(" = ");
   6694     }
   6695     try writer.print("zig_{s}_", .{operation});
   6696     try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
   6697     try writer.writeByte('(');
   6698     if (ref_ret) {
   6699         try f.writeCValue(writer, local, .FunctionArgument);
   6700         try v.elem(f, writer);
   6701         try writer.writeAll(", ");
   6702     }
   6703     try f.writeCValue(writer, lhs, .FunctionArgument);
   6704     try v.elem(f, writer);
   6705     try writer.writeAll(", ");
   6706     try f.writeCValue(writer, rhs, .FunctionArgument);
   6707     if (f.typeOf(bin_op.rhs).isVector(zcu)) try v.elem(f, writer);
   6708     try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info);
   6709     try writer.writeAll(");\n");
   6710     try v.end(f, inst, writer);
   6711 
   6712     return local;
   6713 }
   6714 
   6715 fn airCmpBuiltinCall(
   6716     f: *Function,
   6717     inst: Air.Inst.Index,
   6718     data: anytype,
   6719     operator: std.math.CompareOperator,
   6720     operation: enum { cmp, operator },
   6721     info: BuiltinInfo,
   6722 ) !CValue {
   6723     const pt = f.object.dg.pt;
   6724     const zcu = pt.zcu;
   6725     const lhs = try f.resolveInst(data.lhs);
   6726     const rhs = try f.resolveInst(data.rhs);
   6727     try reap(f, inst, &.{ data.lhs, data.rhs });
   6728 
   6729     const inst_ty = f.typeOfIndex(inst);
   6730     const inst_scalar_ty = inst_ty.scalarType(zcu);
   6731     const operand_ty = f.typeOf(data.lhs);
   6732     const scalar_ty = operand_ty.scalarType(zcu);
   6733 
   6734     const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete);
   6735     const ref_ret = inst_scalar_ctype.info(&f.object.dg.ctype_pool) == .array;
   6736 
   6737     const writer = f.object.writer();
   6738     const local = try f.allocLocal(inst, inst_ty);
   6739     const v = try Vectorize.start(f, inst, writer, operand_ty);
   6740     if (!ref_ret) {
   6741         try f.writeCValue(writer, local, .Other);
   6742         try v.elem(f, writer);
   6743         try writer.writeAll(" = ");
   6744     }
   6745     try writer.print("zig_{s}_", .{switch (operation) {
   6746         else => @tagName(operation),
   6747         .operator => compareOperatorAbbrev(operator),
   6748     }});
   6749     try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
   6750     try writer.writeByte('(');
   6751     if (ref_ret) {
   6752         try f.writeCValue(writer, local, .FunctionArgument);
   6753         try v.elem(f, writer);
   6754         try writer.writeAll(", ");
   6755     }
   6756     try f.writeCValue(writer, lhs, .FunctionArgument);
   6757     try v.elem(f, writer);
   6758     try writer.writeAll(", ");
   6759     try f.writeCValue(writer, rhs, .FunctionArgument);
   6760     try v.elem(f, writer);
   6761     try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info);
   6762     try writer.writeByte(')');
   6763     if (!ref_ret) try writer.print("{s}{f}", .{
   6764         compareOperatorC(operator),
   6765         try f.fmtIntLiteralDec(try pt.intValue(.i32, 0)),
   6766     });
   6767     try writer.writeAll(";\n");
   6768     try v.end(f, inst, writer);
   6769 
   6770     return local;
   6771 }
   6772 
   6773 fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
   6774     const pt = f.object.dg.pt;
   6775     const zcu = pt.zcu;
   6776     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   6777     const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
   6778     const inst_ty = f.typeOfIndex(inst);
   6779     const ptr = try f.resolveInst(extra.ptr);
   6780     const expected_value = try f.resolveInst(extra.expected_value);
   6781     const new_value = try f.resolveInst(extra.new_value);
   6782     const ptr_ty = f.typeOf(extra.ptr);
   6783     const ty = ptr_ty.childType(zcu);
   6784     const ctype = try f.ctypeFromType(ty, .complete);
   6785 
   6786     const writer = f.object.writer();
   6787     const new_value_mat = try Materialize.start(f, inst, ty, new_value);
   6788     try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value });
   6789 
   6790     const repr_ty = if (ty.isRuntimeFloat())
   6791         pt.intType(.unsigned, @as(u16, @intCast(ty.abiSize(zcu) * 8))) catch unreachable
   6792     else
   6793         ty;
   6794 
   6795     const local = try f.allocLocal(inst, inst_ty);
   6796     if (inst_ty.isPtrLikeOptional(zcu)) {
   6797         {
   6798             const a = try Assignment.start(f, writer, ctype);
   6799             try f.writeCValue(writer, local, .Other);
   6800             try a.assign(f, writer);
   6801             try f.writeCValue(writer, expected_value, .Other);
   6802             try a.end(f, writer);
   6803         }
   6804 
   6805         try writer.writeAll("if (");
   6806         try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor});
   6807         try f.renderType(writer, ty);
   6808         try writer.writeByte(')');
   6809         if (ptr_ty.isVolatilePtr(zcu)) try writer.writeAll(" volatile");
   6810         try writer.writeAll(" *)");
   6811         try f.writeCValue(writer, ptr, .Other);
   6812         try writer.writeAll(", ");
   6813         try f.writeCValue(writer, local, .FunctionArgument);
   6814         try writer.writeAll(", ");
   6815         try new_value_mat.mat(f, writer);
   6816         try writer.writeAll(", ");
   6817         try writeMemoryOrder(writer, extra.successOrder());
   6818         try writer.writeAll(", ");
   6819         try writeMemoryOrder(writer, extra.failureOrder());
   6820         try writer.writeAll(", ");
   6821         try f.object.dg.renderTypeForBuiltinFnName(writer, ty);
   6822         try writer.writeAll(", ");
   6823         try f.renderType(writer, repr_ty);
   6824         try writer.writeByte(')');
   6825         try writer.writeAll(") {\n");
   6826         f.object.indent_writer.pushIndent();
   6827         {
   6828             const a = try Assignment.start(f, writer, ctype);
   6829             try f.writeCValue(writer, local, .Other);
   6830             try a.assign(f, writer);
   6831             try writer.writeAll("NULL");
   6832             try a.end(f, writer);
   6833         }
   6834         f.object.indent_writer.popIndent();
   6835         try writer.writeAll("}\n");
   6836     } else {
   6837         {
   6838             const a = try Assignment.start(f, writer, ctype);
   6839             try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
   6840             try a.assign(f, writer);
   6841             try f.writeCValue(writer, expected_value, .Other);
   6842             try a.end(f, writer);
   6843         }
   6844         {
   6845             const a = try Assignment.start(f, writer, .bool);
   6846             try f.writeCValueMember(writer, local, .{ .identifier = "is_null" });
   6847             try a.assign(f, writer);
   6848             try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor});
   6849             try f.renderType(writer, ty);
   6850             try writer.writeByte(')');
   6851             if (ptr_ty.isVolatilePtr(zcu)) try writer.writeAll(" volatile");
   6852             try writer.writeAll(" *)");
   6853             try f.writeCValue(writer, ptr, .Other);
   6854             try writer.writeAll(", ");
   6855             try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
   6856             try writer.writeAll(", ");
   6857             try new_value_mat.mat(f, writer);
   6858             try writer.writeAll(", ");
   6859             try writeMemoryOrder(writer, extra.successOrder());
   6860             try writer.writeAll(", ");
   6861             try writeMemoryOrder(writer, extra.failureOrder());
   6862             try writer.writeAll(", ");
   6863             try f.object.dg.renderTypeForBuiltinFnName(writer, ty);
   6864             try writer.writeAll(", ");
   6865             try f.renderType(writer, repr_ty);
   6866             try writer.writeByte(')');
   6867             try a.end(f, writer);
   6868         }
   6869     }
   6870     try new_value_mat.end(f, inst);
   6871 
   6872     if (f.liveness.isUnused(inst)) {
   6873         try freeLocal(f, inst, local.new_local, null);
   6874         return .none;
   6875     }
   6876 
   6877     return local;
   6878 }
   6879 
   6880 fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
   6881     const pt = f.object.dg.pt;
   6882     const zcu = pt.zcu;
   6883     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   6884     const extra = f.air.extraData(Air.AtomicRmw, pl_op.payload).data;
   6885     const inst_ty = f.typeOfIndex(inst);
   6886     const ptr_ty = f.typeOf(pl_op.operand);
   6887     const ty = ptr_ty.childType(zcu);
   6888     const ptr = try f.resolveInst(pl_op.operand);
   6889     const operand = try f.resolveInst(extra.operand);
   6890 
   6891     const writer = f.object.writer();
   6892     const operand_mat = try Materialize.start(f, inst, ty, operand);
   6893     try reap(f, inst, &.{ pl_op.operand, extra.operand });
   6894 
   6895     const repr_bits: u16 = @intCast(ty.abiSize(zcu) * 8);
   6896     const is_float = ty.isRuntimeFloat();
   6897     const is_128 = repr_bits == 128;
   6898     const repr_ty = if (is_float) pt.intType(.unsigned, repr_bits) catch unreachable else ty;
   6899 
   6900     const local = try f.allocLocal(inst, inst_ty);
   6901     try writer.print("zig_atomicrmw_{s}", .{toAtomicRmwSuffix(extra.op())});
   6902     if (is_float) try writer.writeAll("_float") else if (is_128) try writer.writeAll("_int128");
   6903     try writer.writeByte('(');
   6904     try f.writeCValue(writer, local, .Other);
   6905     try writer.writeAll(", (");
   6906     const use_atomic = switch (extra.op()) {
   6907         else => true,
   6908         // These are missing from stdatomic.h, so no atomic types unless a fallback is used.
   6909         .Nand, .Min, .Max => is_float or is_128,
   6910     };
   6911     if (use_atomic) try writer.writeAll("zig_atomic(");
   6912     try f.renderType(writer, ty);
   6913     if (use_atomic) try writer.writeByte(')');
   6914     if (ptr_ty.isVolatilePtr(zcu)) try writer.writeAll(" volatile");
   6915     try writer.writeAll(" *)");
   6916     try f.writeCValue(writer, ptr, .Other);
   6917     try writer.writeAll(", ");
   6918     try operand_mat.mat(f, writer);
   6919     try writer.writeAll(", ");
   6920     try writeMemoryOrder(writer, extra.ordering());
   6921     try writer.writeAll(", ");
   6922     try f.object.dg.renderTypeForBuiltinFnName(writer, ty);
   6923     try writer.writeAll(", ");
   6924     try f.renderType(writer, repr_ty);
   6925     try writer.writeAll(");\n");
   6926     try operand_mat.end(f, inst);
   6927 
   6928     if (f.liveness.isUnused(inst)) {
   6929         try freeLocal(f, inst, local.new_local, null);
   6930         return .none;
   6931     }
   6932 
   6933     return local;
   6934 }
   6935 
   6936 fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
   6937     const pt = f.object.dg.pt;
   6938     const zcu = pt.zcu;
   6939     const atomic_load = f.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load;
   6940     const ptr = try f.resolveInst(atomic_load.ptr);
   6941     try reap(f, inst, &.{atomic_load.ptr});
   6942     const ptr_ty = f.typeOf(atomic_load.ptr);
   6943     const ty = ptr_ty.childType(zcu);
   6944 
   6945     const repr_ty = if (ty.isRuntimeFloat())
   6946         pt.intType(.unsigned, @as(u16, @intCast(ty.abiSize(zcu) * 8))) catch unreachable
   6947     else
   6948         ty;
   6949 
   6950     const inst_ty = f.typeOfIndex(inst);
   6951     const writer = f.object.writer();
   6952     const local = try f.allocLocal(inst, inst_ty);
   6953 
   6954     try writer.writeAll("zig_atomic_load(");
   6955     try f.writeCValue(writer, local, .Other);
   6956     try writer.writeAll(", (zig_atomic(");
   6957     try f.renderType(writer, ty);
   6958     try writer.writeByte(')');
   6959     if (ptr_ty.isVolatilePtr(zcu)) try writer.writeAll(" volatile");
   6960     try writer.writeAll(" *)");
   6961     try f.writeCValue(writer, ptr, .Other);
   6962     try writer.writeAll(", ");
   6963     try writeMemoryOrder(writer, atomic_load.order);
   6964     try writer.writeAll(", ");
   6965     try f.object.dg.renderTypeForBuiltinFnName(writer, ty);
   6966     try writer.writeAll(", ");
   6967     try f.renderType(writer, repr_ty);
   6968     try writer.writeAll(");\n");
   6969 
   6970     return local;
   6971 }
   6972 
   6973 fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CValue {
   6974     const pt = f.object.dg.pt;
   6975     const zcu = pt.zcu;
   6976     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   6977     const ptr_ty = f.typeOf(bin_op.lhs);
   6978     const ty = ptr_ty.childType(zcu);
   6979     const ptr = try f.resolveInst(bin_op.lhs);
   6980     const element = try f.resolveInst(bin_op.rhs);
   6981 
   6982     const writer = f.object.writer();
   6983     const element_mat = try Materialize.start(f, inst, ty, element);
   6984     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   6985 
   6986     const repr_ty = if (ty.isRuntimeFloat())
   6987         pt.intType(.unsigned, @as(u16, @intCast(ty.abiSize(zcu) * 8))) catch unreachable
   6988     else
   6989         ty;
   6990 
   6991     try writer.writeAll("zig_atomic_store((zig_atomic(");
   6992     try f.renderType(writer, ty);
   6993     try writer.writeByte(')');
   6994     if (ptr_ty.isVolatilePtr(zcu)) try writer.writeAll(" volatile");
   6995     try writer.writeAll(" *)");
   6996     try f.writeCValue(writer, ptr, .Other);
   6997     try writer.writeAll(", ");
   6998     try element_mat.mat(f, writer);
   6999     try writer.print(", {s}, ", .{order});
   7000     try f.object.dg.renderTypeForBuiltinFnName(writer, ty);
   7001     try writer.writeAll(", ");
   7002     try f.renderType(writer, repr_ty);
   7003     try writer.writeAll(");\n");
   7004     try element_mat.end(f, inst);
   7005 
   7006     return .none;
   7007 }
   7008 
   7009 fn writeSliceOrPtr(f: *Function, writer: anytype, ptr: CValue, ptr_ty: Type) !void {
   7010     const pt = f.object.dg.pt;
   7011     const zcu = pt.zcu;
   7012     if (ptr_ty.isSlice(zcu)) {
   7013         try f.writeCValueMember(writer, ptr, .{ .identifier = "ptr" });
   7014     } else {
   7015         try f.writeCValue(writer, ptr, .FunctionArgument);
   7016     }
   7017 }
   7018 
   7019 fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
   7020     const pt = f.object.dg.pt;
   7021     const zcu = pt.zcu;
   7022     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   7023     const dest_ty = f.typeOf(bin_op.lhs);
   7024     const dest_slice = try f.resolveInst(bin_op.lhs);
   7025     const value = try f.resolveInst(bin_op.rhs);
   7026     const elem_ty = f.typeOf(bin_op.rhs);
   7027     const elem_abi_size = elem_ty.abiSize(zcu);
   7028     const val_is_undef = if (try f.air.value(bin_op.rhs, pt)) |val| val.isUndefDeep(zcu) else false;
   7029     const writer = f.object.writer();
   7030 
   7031     if (val_is_undef) {
   7032         if (!safety) {
   7033             try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   7034             return .none;
   7035         }
   7036 
   7037         try writer.writeAll("memset(");
   7038         switch (dest_ty.ptrSize(zcu)) {
   7039             .slice => {
   7040                 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
   7041                 try writer.writeAll(", 0xaa, ");
   7042                 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
   7043                 if (elem_abi_size > 1) {
   7044                     try writer.print(" * {d});\n", .{elem_abi_size});
   7045                 } else {
   7046                     try writer.writeAll(");\n");
   7047                 }
   7048             },
   7049             .one => {
   7050                 const array_ty = dest_ty.childType(zcu);
   7051                 const len = array_ty.arrayLen(zcu) * elem_abi_size;
   7052 
   7053                 try f.writeCValue(writer, dest_slice, .FunctionArgument);
   7054                 try writer.print(", 0xaa, {d});\n", .{len});
   7055             },
   7056             .many, .c => unreachable,
   7057         }
   7058         try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   7059         return .none;
   7060     }
   7061 
   7062     if (elem_abi_size > 1 or dest_ty.isVolatilePtr(zcu)) {
   7063         // For the assignment in this loop, the array pointer needs to get
   7064         // casted to a regular pointer, otherwise an error like this occurs:
   7065         // error: array type 'uint32_t[20]' (aka 'unsigned int[20]') is not assignable
   7066         const elem_ptr_ty = try pt.ptrType(.{
   7067             .child = elem_ty.toIntern(),
   7068             .flags = .{
   7069                 .size = .c,
   7070             },
   7071         });
   7072 
   7073         const index = try f.allocLocal(inst, .usize);
   7074 
   7075         try writer.writeAll("for (");
   7076         try f.writeCValue(writer, index, .Other);
   7077         try writer.writeAll(" = ");
   7078         try f.object.dg.renderValue(writer, .zero_usize, .Other);
   7079         try writer.writeAll("; ");
   7080         try f.writeCValue(writer, index, .Other);
   7081         try writer.writeAll(" != ");
   7082         switch (dest_ty.ptrSize(zcu)) {
   7083             .slice => {
   7084                 try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
   7085             },
   7086             .one => {
   7087                 const array_ty = dest_ty.childType(zcu);
   7088                 try writer.print("{d}", .{array_ty.arrayLen(zcu)});
   7089             },
   7090             .many, .c => unreachable,
   7091         }
   7092         try writer.writeAll("; ++");
   7093         try f.writeCValue(writer, index, .Other);
   7094         try writer.writeAll(") ");
   7095 
   7096         const a = try Assignment.start(f, writer, try f.ctypeFromType(elem_ty, .complete));
   7097         try writer.writeAll("((");
   7098         try f.renderType(writer, elem_ptr_ty);
   7099         try writer.writeByte(')');
   7100         try writeSliceOrPtr(f, writer, dest_slice, dest_ty);
   7101         try writer.writeAll(")[");
   7102         try f.writeCValue(writer, index, .Other);
   7103         try writer.writeByte(']');
   7104         try a.assign(f, writer);
   7105         try f.writeCValue(writer, value, .Other);
   7106         try a.end(f, writer);
   7107 
   7108         try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   7109         try freeLocal(f, inst, index.new_local, null);
   7110 
   7111         return .none;
   7112     }
   7113 
   7114     const bitcasted = try bitcast(f, .u8, value, elem_ty);
   7115 
   7116     try writer.writeAll("memset(");
   7117     switch (dest_ty.ptrSize(zcu)) {
   7118         .slice => {
   7119             try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
   7120             try writer.writeAll(", ");
   7121             try f.writeCValue(writer, bitcasted, .FunctionArgument);
   7122             try writer.writeAll(", ");
   7123             try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
   7124             try writer.writeAll(");\n");
   7125         },
   7126         .one => {
   7127             const array_ty = dest_ty.childType(zcu);
   7128             const len = array_ty.arrayLen(zcu) * elem_abi_size;
   7129 
   7130             try f.writeCValue(writer, dest_slice, .FunctionArgument);
   7131             try writer.writeAll(", ");
   7132             try f.writeCValue(writer, bitcasted, .FunctionArgument);
   7133             try writer.print(", {d});\n", .{len});
   7134         },
   7135         .many, .c => unreachable,
   7136     }
   7137     try f.freeCValue(inst, bitcasted);
   7138     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   7139     return .none;
   7140 }
   7141 
   7142 fn airMemcpy(f: *Function, inst: Air.Inst.Index, function_paren: []const u8) !CValue {
   7143     const pt = f.object.dg.pt;
   7144     const zcu = pt.zcu;
   7145     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   7146     const dest_ptr = try f.resolveInst(bin_op.lhs);
   7147     const src_ptr = try f.resolveInst(bin_op.rhs);
   7148     const dest_ty = f.typeOf(bin_op.lhs);
   7149     const src_ty = f.typeOf(bin_op.rhs);
   7150     const writer = f.object.writer();
   7151 
   7152     if (dest_ty.ptrSize(zcu) != .one) {
   7153         try writer.writeAll("if (");
   7154         try writeArrayLen(f, writer, dest_ptr, dest_ty);
   7155         try writer.writeAll(" != 0) ");
   7156     }
   7157     try writer.writeAll(function_paren);
   7158     try writeSliceOrPtr(f, writer, dest_ptr, dest_ty);
   7159     try writer.writeAll(", ");
   7160     try writeSliceOrPtr(f, writer, src_ptr, src_ty);
   7161     try writer.writeAll(", ");
   7162     try writeArrayLen(f, writer, dest_ptr, dest_ty);
   7163     try writer.writeAll(" * sizeof(");
   7164     try f.renderType(writer, dest_ty.elemType2(zcu));
   7165     try writer.writeAll("));\n");
   7166 
   7167     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   7168     return .none;
   7169 }
   7170 
   7171 fn writeArrayLen(f: *Function, writer: ArrayListWriter, dest_ptr: CValue, dest_ty: Type) !void {
   7172     const pt = f.object.dg.pt;
   7173     const zcu = pt.zcu;
   7174     switch (dest_ty.ptrSize(zcu)) {
   7175         .one => try writer.print("{f}", .{
   7176             try f.fmtIntLiteralDec(try pt.intValue(.usize, dest_ty.childType(zcu).arrayLen(zcu))),
   7177         }),
   7178         .many, .c => unreachable,
   7179         .slice => try f.writeCValueMember(writer, dest_ptr, .{ .identifier = "len" }),
   7180     }
   7181 }
   7182 
   7183 fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
   7184     const pt = f.object.dg.pt;
   7185     const zcu = pt.zcu;
   7186     const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   7187     const union_ptr = try f.resolveInst(bin_op.lhs);
   7188     const new_tag = try f.resolveInst(bin_op.rhs);
   7189     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
   7190 
   7191     const union_ty = f.typeOf(bin_op.lhs).childType(zcu);
   7192     const layout = union_ty.unionGetLayout(zcu);
   7193     if (layout.tag_size == 0) return .none;
   7194     const tag_ty = union_ty.unionTagTypeSafety(zcu).?;
   7195 
   7196     const writer = f.object.writer();
   7197     const a = try Assignment.start(f, writer, try f.ctypeFromType(tag_ty, .complete));
   7198     try f.writeCValueDerefMember(writer, union_ptr, .{ .identifier = "tag" });
   7199     try a.assign(f, writer);
   7200     try f.writeCValue(writer, new_tag, .Other);
   7201     try a.end(f, writer);
   7202     return .none;
   7203 }
   7204 
   7205 fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
   7206     const pt = f.object.dg.pt;
   7207     const zcu = pt.zcu;
   7208     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   7209 
   7210     const operand = try f.resolveInst(ty_op.operand);
   7211     try reap(f, inst, &.{ty_op.operand});
   7212 
   7213     const union_ty = f.typeOf(ty_op.operand);
   7214     const layout = union_ty.unionGetLayout(zcu);
   7215     if (layout.tag_size == 0) return .none;
   7216 
   7217     const inst_ty = f.typeOfIndex(inst);
   7218     const writer = f.object.writer();
   7219     const local = try f.allocLocal(inst, inst_ty);
   7220     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete));
   7221     try f.writeCValue(writer, local, .Other);
   7222     try a.assign(f, writer);
   7223     try f.writeCValueMember(writer, operand, .{ .identifier = "tag" });
   7224     try a.end(f, writer);
   7225     return local;
   7226 }
   7227 
   7228 fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
   7229     const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   7230 
   7231     const inst_ty = f.typeOfIndex(inst);
   7232     const enum_ty = f.typeOf(un_op);
   7233     const operand = try f.resolveInst(un_op);
   7234     try reap(f, inst, &.{un_op});
   7235 
   7236     const writer = f.object.writer();
   7237     const local = try f.allocLocal(inst, inst_ty);
   7238     try f.writeCValue(writer, local, .Other);
   7239     try writer.print(" = {s}(", .{
   7240         try f.getLazyFnName(.{ .tag_name = enum_ty.toIntern() }),
   7241     });
   7242     try f.writeCValue(writer, operand, .Other);
   7243     try writer.writeAll(");\n");
   7244 
   7245     return local;
   7246 }
   7247 
   7248 fn airErrorName(f: *Function, inst: Air.Inst.Index) !CValue {
   7249     const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   7250 
   7251     const writer = f.object.writer();
   7252     const inst_ty = f.typeOfIndex(inst);
   7253     const operand = try f.resolveInst(un_op);
   7254     try reap(f, inst, &.{un_op});
   7255     const local = try f.allocLocal(inst, inst_ty);
   7256     try f.writeCValue(writer, local, .Other);
   7257 
   7258     try writer.writeAll(" = zig_errorName[");
   7259     try f.writeCValue(writer, operand, .Other);
   7260     try writer.writeAll(" - 1];\n");
   7261     return local;
   7262 }
   7263 
   7264 fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
   7265     const pt = f.object.dg.pt;
   7266     const zcu = pt.zcu;
   7267     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   7268 
   7269     const operand = try f.resolveInst(ty_op.operand);
   7270     try reap(f, inst, &.{ty_op.operand});
   7271 
   7272     const inst_ty = f.typeOfIndex(inst);
   7273     const inst_scalar_ty = inst_ty.scalarType(zcu);
   7274 
   7275     const writer = f.object.writer();
   7276     const local = try f.allocLocal(inst, inst_ty);
   7277     const v = try Vectorize.start(f, inst, writer, inst_ty);
   7278     const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_scalar_ty, .complete));
   7279     try f.writeCValue(writer, local, .Other);
   7280     try v.elem(f, writer);
   7281     try a.assign(f, writer);
   7282     try f.writeCValue(writer, operand, .Other);
   7283     try a.end(f, writer);
   7284     try v.end(f, inst, writer);
   7285 
   7286     return local;
   7287 }
   7288 
   7289 fn airSelect(f: *Function, inst: Air.Inst.Index) !CValue {
   7290     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   7291     const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
   7292 
   7293     const pred = try f.resolveInst(pl_op.operand);
   7294     const lhs = try f.resolveInst(extra.lhs);
   7295     const rhs = try f.resolveInst(extra.rhs);
   7296     try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
   7297 
   7298     const inst_ty = f.typeOfIndex(inst);
   7299 
   7300     const writer = f.object.writer();
   7301     const local = try f.allocLocal(inst, inst_ty);
   7302     const v = try Vectorize.start(f, inst, writer, inst_ty);
   7303     try f.writeCValue(writer, local, .Other);
   7304     try v.elem(f, writer);
   7305     try writer.writeAll(" = ");
   7306     try f.writeCValue(writer, pred, .Other);
   7307     try v.elem(f, writer);
   7308     try writer.writeAll(" ? ");
   7309     try f.writeCValue(writer, lhs, .Other);
   7310     try v.elem(f, writer);
   7311     try writer.writeAll(" : ");
   7312     try f.writeCValue(writer, rhs, .Other);
   7313     try v.elem(f, writer);
   7314     try writer.writeAll(";\n");
   7315     try v.end(f, inst, writer);
   7316 
   7317     return local;
   7318 }
   7319 
   7320 fn airShuffleOne(f: *Function, inst: Air.Inst.Index) !CValue {
   7321     const pt = f.object.dg.pt;
   7322     const zcu = pt.zcu;
   7323 
   7324     const unwrapped = f.air.unwrapShuffleOne(zcu, inst);
   7325     const mask = unwrapped.mask;
   7326     const operand = try f.resolveInst(unwrapped.operand);
   7327     const inst_ty = unwrapped.result_ty;
   7328 
   7329     const writer = f.object.writer();
   7330     const local = try f.allocLocal(inst, inst_ty);
   7331     try reap(f, inst, &.{unwrapped.operand}); // local cannot alias operand
   7332     for (mask, 0..) |mask_elem, out_idx| {
   7333         try f.writeCValue(writer, local, .Other);
   7334         try writer.writeByte('[');
   7335         try f.object.dg.renderValue(writer, try pt.intValue(.usize, out_idx), .Other);
   7336         try writer.writeAll("] = ");
   7337         switch (mask_elem.unwrap()) {
   7338             .elem => |src_idx| {
   7339                 try f.writeCValue(writer, operand, .Other);
   7340                 try writer.writeByte('[');
   7341                 try f.object.dg.renderValue(writer, try pt.intValue(.usize, src_idx), .Other);
   7342                 try writer.writeByte(']');
   7343             },
   7344             .value => |val| try f.object.dg.renderValue(writer, .fromInterned(val), .Other),
   7345         }
   7346         try writer.writeAll(";\n");
   7347     }
   7348 
   7349     return local;
   7350 }
   7351 
   7352 fn airShuffleTwo(f: *Function, inst: Air.Inst.Index) !CValue {
   7353     const pt = f.object.dg.pt;
   7354     const zcu = pt.zcu;
   7355 
   7356     const unwrapped = f.air.unwrapShuffleTwo(zcu, inst);
   7357     const mask = unwrapped.mask;
   7358     const operand_a = try f.resolveInst(unwrapped.operand_a);
   7359     const operand_b = try f.resolveInst(unwrapped.operand_b);
   7360     const inst_ty = unwrapped.result_ty;
   7361     const elem_ty = inst_ty.childType(zcu);
   7362 
   7363     const writer = f.object.writer();
   7364     const local = try f.allocLocal(inst, inst_ty);
   7365     try reap(f, inst, &.{ unwrapped.operand_a, unwrapped.operand_b }); // local cannot alias operands
   7366     for (mask, 0..) |mask_elem, out_idx| {
   7367         try f.writeCValue(writer, local, .Other);
   7368         try writer.writeByte('[');
   7369         try f.object.dg.renderValue(writer, try pt.intValue(.usize, out_idx), .Other);
   7370         try writer.writeAll("] = ");
   7371         switch (mask_elem.unwrap()) {
   7372             .a_elem => |src_idx| {
   7373                 try f.writeCValue(writer, operand_a, .Other);
   7374                 try writer.writeByte('[');
   7375                 try f.object.dg.renderValue(writer, try pt.intValue(.usize, src_idx), .Other);
   7376                 try writer.writeByte(']');
   7377             },
   7378             .b_elem => |src_idx| {
   7379                 try f.writeCValue(writer, operand_b, .Other);
   7380                 try writer.writeByte('[');
   7381                 try f.object.dg.renderValue(writer, try pt.intValue(.usize, src_idx), .Other);
   7382                 try writer.writeByte(']');
   7383             },
   7384             .undef => try f.object.dg.renderUndefValue(writer, elem_ty, .Other),
   7385         }
   7386         try writer.writeAll(";\n");
   7387     }
   7388 
   7389     return local;
   7390 }
   7391 
   7392 fn airReduce(f: *Function, inst: Air.Inst.Index) !CValue {
   7393     const pt = f.object.dg.pt;
   7394     const zcu = pt.zcu;
   7395     const reduce = f.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
   7396 
   7397     const scalar_ty = f.typeOfIndex(inst);
   7398     const operand = try f.resolveInst(reduce.operand);
   7399     try reap(f, inst, &.{reduce.operand});
   7400     const operand_ty = f.typeOf(reduce.operand);
   7401     const writer = f.object.writer();
   7402 
   7403     const use_operator = scalar_ty.bitSize(zcu) <= 64;
   7404     const op: union(enum) {
   7405         const Func = struct { operation: []const u8, info: BuiltinInfo = .none };
   7406         builtin: Func,
   7407         infix: []const u8,
   7408         ternary: []const u8,
   7409     } = switch (reduce.operation) {
   7410         .And => if (use_operator) .{ .infix = " &= " } else .{ .builtin = .{ .operation = "and" } },
   7411         .Or => if (use_operator) .{ .infix = " |= " } else .{ .builtin = .{ .operation = "or" } },
   7412         .Xor => if (use_operator) .{ .infix = " ^= " } else .{ .builtin = .{ .operation = "xor" } },
   7413         .Min => switch (scalar_ty.zigTypeTag(zcu)) {
   7414             .int => if (use_operator) .{ .ternary = " < " } else .{ .builtin = .{ .operation = "min" } },
   7415             .float => .{ .builtin = .{ .operation = "min" } },
   7416             else => unreachable,
   7417         },
   7418         .Max => switch (scalar_ty.zigTypeTag(zcu)) {
   7419             .int => if (use_operator) .{ .ternary = " > " } else .{ .builtin = .{ .operation = "max" } },
   7420             .float => .{ .builtin = .{ .operation = "max" } },
   7421             else => unreachable,
   7422         },
   7423         .Add => switch (scalar_ty.zigTypeTag(zcu)) {
   7424             .int => if (use_operator) .{ .infix = " += " } else .{ .builtin = .{ .operation = "addw", .info = .bits } },
   7425             .float => .{ .builtin = .{ .operation = "add" } },
   7426             else => unreachable,
   7427         },
   7428         .Mul => switch (scalar_ty.zigTypeTag(zcu)) {
   7429             .int => if (use_operator) .{ .infix = " *= " } else .{ .builtin = .{ .operation = "mulw", .info = .bits } },
   7430             .float => .{ .builtin = .{ .operation = "mul" } },
   7431             else => unreachable,
   7432         },
   7433     };
   7434 
   7435     // Reduce a vector by repeatedly applying a function to produce an
   7436     // accumulated result.
   7437     //
   7438     // Equivalent to:
   7439     //   reduce: {
   7440     //     var accum: T = init;
   7441     //     for (vec) |elem| {
   7442     //       accum = func(accum, elem);
   7443     //     }
   7444     //     break :reduce accum;
   7445     //   }
   7446 
   7447     const accum = try f.allocLocal(inst, scalar_ty);
   7448     try f.writeCValue(writer, accum, .Other);
   7449     try writer.writeAll(" = ");
   7450 
   7451     try f.object.dg.renderValue(writer, switch (reduce.operation) {
   7452         .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) {
   7453             .bool => Value.false,
   7454             .int => try pt.intValue(scalar_ty, 0),
   7455             else => unreachable,
   7456         },
   7457         .And => switch (scalar_ty.zigTypeTag(zcu)) {
   7458             .bool => Value.true,
   7459             .int => switch (scalar_ty.intInfo(zcu).signedness) {
   7460                 .unsigned => try scalar_ty.maxIntScalar(pt, scalar_ty),
   7461                 .signed => try pt.intValue(scalar_ty, -1),
   7462             },
   7463             else => unreachable,
   7464         },
   7465         .Add => switch (scalar_ty.zigTypeTag(zcu)) {
   7466             .int => try pt.intValue(scalar_ty, 0),
   7467             .float => try pt.floatValue(scalar_ty, 0.0),
   7468             else => unreachable,
   7469         },
   7470         .Mul => switch (scalar_ty.zigTypeTag(zcu)) {
   7471             .int => try pt.intValue(scalar_ty, 1),
   7472             .float => try pt.floatValue(scalar_ty, 1.0),
   7473             else => unreachable,
   7474         },
   7475         .Min => switch (scalar_ty.zigTypeTag(zcu)) {
   7476             .bool => Value.true,
   7477             .int => try scalar_ty.maxIntScalar(pt, scalar_ty),
   7478             .float => try pt.floatValue(scalar_ty, std.math.nan(f128)),
   7479             else => unreachable,
   7480         },
   7481         .Max => switch (scalar_ty.zigTypeTag(zcu)) {
   7482             .bool => Value.false,
   7483             .int => try scalar_ty.minIntScalar(pt, scalar_ty),
   7484             .float => try pt.floatValue(scalar_ty, std.math.nan(f128)),
   7485             else => unreachable,
   7486         },
   7487     }, .Other);
   7488     try writer.writeAll(";\n");
   7489 
   7490     const v = try Vectorize.start(f, inst, writer, operand_ty);
   7491     try f.writeCValue(writer, accum, .Other);
   7492     switch (op) {
   7493         .builtin => |func| {
   7494             try writer.print(" = zig_{s}_", .{func.operation});
   7495             try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty);
   7496             try writer.writeByte('(');
   7497             try f.writeCValue(writer, accum, .FunctionArgument);
   7498             try writer.writeAll(", ");
   7499             try f.writeCValue(writer, operand, .Other);
   7500             try v.elem(f, writer);
   7501             try f.object.dg.renderBuiltinInfo(writer, scalar_ty, func.info);
   7502             try writer.writeByte(')');
   7503         },
   7504         .infix => |ass| {
   7505             try writer.writeAll(ass);
   7506             try f.writeCValue(writer, operand, .Other);
   7507             try v.elem(f, writer);
   7508         },
   7509         .ternary => |cmp| {
   7510             try writer.writeAll(" = ");
   7511             try f.writeCValue(writer, accum, .Other);
   7512             try writer.writeAll(cmp);
   7513             try f.writeCValue(writer, operand, .Other);
   7514             try v.elem(f, writer);
   7515             try writer.writeAll(" ? ");
   7516             try f.writeCValue(writer, accum, .Other);
   7517             try writer.writeAll(" : ");
   7518             try f.writeCValue(writer, operand, .Other);
   7519             try v.elem(f, writer);
   7520         },
   7521     }
   7522     try writer.writeAll(";\n");
   7523     try v.end(f, inst, writer);
   7524 
   7525     return accum;
   7526 }
   7527 
   7528 fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
   7529     const pt = f.object.dg.pt;
   7530     const zcu = pt.zcu;
   7531     const ip = &zcu.intern_pool;
   7532     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   7533     const inst_ty = f.typeOfIndex(inst);
   7534     const len: usize = @intCast(inst_ty.arrayLen(zcu));
   7535     const elements: []const Air.Inst.Ref = @ptrCast(f.air.extra.items[ty_pl.payload..][0..len]);
   7536     const gpa = f.object.dg.gpa;
   7537     const resolved_elements = try gpa.alloc(CValue, elements.len);
   7538     defer gpa.free(resolved_elements);
   7539     for (resolved_elements, elements) |*resolved_element, element| {
   7540         resolved_element.* = try f.resolveInst(element);
   7541     }
   7542     {
   7543         var bt = iterateBigTomb(f, inst);
   7544         for (elements) |element| {
   7545             try bt.feed(element);
   7546         }
   7547     }
   7548 
   7549     const writer = f.object.writer();
   7550     const local = try f.allocLocal(inst, inst_ty);
   7551     switch (ip.indexToKey(inst_ty.toIntern())) {
   7552         inline .array_type, .vector_type => |info, tag| {
   7553             const a: Assignment = .{
   7554                 .ctype = try f.ctypeFromType(.fromInterned(info.child), .complete),
   7555             };
   7556             for (resolved_elements, 0..) |element, i| {
   7557                 try a.restart(f, writer);
   7558                 try f.writeCValue(writer, local, .Other);
   7559                 try writer.print("[{d}]", .{i});
   7560                 try a.assign(f, writer);
   7561                 try f.writeCValue(writer, element, .Other);
   7562                 try a.end(f, writer);
   7563             }
   7564             if (tag == .array_type and info.sentinel != .none) {
   7565                 try a.restart(f, writer);
   7566                 try f.writeCValue(writer, local, .Other);
   7567                 try writer.print("[{d}]", .{info.len});
   7568                 try a.assign(f, writer);
   7569                 try f.object.dg.renderValue(writer, Value.fromInterned(info.sentinel), .Other);
   7570                 try a.end(f, writer);
   7571             }
   7572         },
   7573         .struct_type => {
   7574             const loaded_struct = ip.loadStructType(inst_ty.toIntern());
   7575             switch (loaded_struct.layout) {
   7576                 .auto, .@"extern" => {
   7577                     var field_it = loaded_struct.iterateRuntimeOrder(ip);
   7578                     while (field_it.next()) |field_index| {
   7579                         const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
   7580                         if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   7581 
   7582                         const a = try Assignment.start(f, writer, try f.ctypeFromType(field_ty, .complete));
   7583                         try f.writeCValueMember(writer, local, if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name|
   7584                             .{ .identifier = field_name.toSlice(ip) }
   7585                         else
   7586                             .{ .field = field_index });
   7587                         try a.assign(f, writer);
   7588                         try f.writeCValue(writer, resolved_elements[field_index], .Other);
   7589                         try a.end(f, writer);
   7590                     }
   7591                 },
   7592                 .@"packed" => {
   7593                     try f.writeCValue(writer, local, .Other);
   7594                     try writer.writeAll(" = ");
   7595 
   7596                     const backing_int_ty: Type = .fromInterned(loaded_struct.backingIntTypeUnordered(ip));
   7597                     const int_info = backing_int_ty.intInfo(zcu);
   7598 
   7599                     const bit_offset_ty = try pt.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1));
   7600 
   7601                     var bit_offset: u64 = 0;
   7602 
   7603                     var empty = true;
   7604                     for (0..elements.len) |field_index| {
   7605                         if (inst_ty.structFieldIsComptime(field_index, zcu)) continue;
   7606                         const field_ty = inst_ty.fieldType(field_index, zcu);
   7607                         if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   7608 
   7609                         if (!empty) {
   7610                             try writer.writeAll("zig_or_");
   7611                             try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);
   7612                             try writer.writeByte('(');
   7613                         }
   7614                         empty = false;
   7615                     }
   7616                     empty = true;
   7617                     for (resolved_elements, 0..) |element, field_index| {
   7618                         if (inst_ty.structFieldIsComptime(field_index, zcu)) continue;
   7619                         const field_ty = inst_ty.fieldType(field_index, zcu);
   7620                         if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   7621 
   7622                         if (!empty) try writer.writeAll(", ");
   7623                         // TODO: Skip this entire shift if val is 0?
   7624                         try writer.writeAll("zig_shlw_");
   7625                         try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);
   7626                         try writer.writeByte('(');
   7627 
   7628                         if (field_ty.isAbiInt(zcu)) {
   7629                             try writer.writeAll("zig_and_");
   7630                             try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty);
   7631                             try writer.writeByte('(');
   7632                         }
   7633 
   7634                         if (inst_ty.isAbiInt(zcu) and (field_ty.isAbiInt(zcu) or field_ty.isPtrAtRuntime(zcu))) {
   7635                             try f.renderIntCast(writer, inst_ty, element, .{}, field_ty, .FunctionArgument);
   7636                         } else {
   7637                             try writer.writeByte('(');
   7638                             try f.renderType(writer, inst_ty);
   7639                             try writer.writeByte(')');
   7640                             if (field_ty.isPtrAtRuntime(zcu)) {
   7641                                 try writer.writeByte('(');
   7642                                 try f.renderType(writer, switch (int_info.signedness) {
   7643                                     .unsigned => .usize,
   7644                                     .signed => .isize,
   7645                                 });
   7646                                 try writer.writeByte(')');
   7647                             }
   7648                             try f.writeCValue(writer, element, .Other);
   7649                         }
   7650 
   7651                         if (field_ty.isAbiInt(zcu)) {
   7652                             try writer.writeAll(", ");
   7653                             const field_int_info = field_ty.intInfo(zcu);
   7654                             const field_mask = if (int_info.signedness == .signed and int_info.bits == field_int_info.bits)
   7655                                 try pt.intValue(backing_int_ty, -1)
   7656                             else
   7657                                 try (try pt.intType(.unsigned, field_int_info.bits)).maxIntScalar(pt, backing_int_ty);
   7658                             try f.object.dg.renderValue(writer, field_mask, .FunctionArgument);
   7659                             try writer.writeByte(')');
   7660                         }
   7661 
   7662                         try writer.print(", {f}", .{
   7663                             try f.fmtIntLiteralDec(try pt.intValue(bit_offset_ty, bit_offset)),
   7664                         });
   7665                         try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits);
   7666                         try writer.writeByte(')');
   7667                         if (!empty) try writer.writeByte(')');
   7668 
   7669                         bit_offset += field_ty.bitSize(zcu);
   7670                         empty = false;
   7671                     }
   7672                     try writer.writeAll(";\n");
   7673                 },
   7674             }
   7675         },
   7676         .tuple_type => |tuple_info| for (0..tuple_info.types.len) |field_index| {
   7677             if (tuple_info.values.get(ip)[field_index] != .none) continue;
   7678             const field_ty: Type = .fromInterned(tuple_info.types.get(ip)[field_index]);
   7679             if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   7680 
   7681             const a = try Assignment.start(f, writer, try f.ctypeFromType(field_ty, .complete));
   7682             try f.writeCValueMember(writer, local, .{ .field = field_index });
   7683             try a.assign(f, writer);
   7684             try f.writeCValue(writer, resolved_elements[field_index], .Other);
   7685             try a.end(f, writer);
   7686         },
   7687         else => unreachable,
   7688     }
   7689 
   7690     return local;
   7691 }
   7692 
   7693 fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
   7694     const pt = f.object.dg.pt;
   7695     const zcu = pt.zcu;
   7696     const ip = &zcu.intern_pool;
   7697     const ty_pl = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   7698     const extra = f.air.extraData(Air.UnionInit, ty_pl.payload).data;
   7699 
   7700     const union_ty = f.typeOfIndex(inst);
   7701     const loaded_union = ip.loadUnionType(union_ty.toIntern());
   7702     const field_name = loaded_union.loadTagType(ip).names.get(ip)[extra.field_index];
   7703     const payload_ty = f.typeOf(extra.init);
   7704     const payload = try f.resolveInst(extra.init);
   7705     try reap(f, inst, &.{extra.init});
   7706 
   7707     const writer = f.object.writer();
   7708     const local = try f.allocLocal(inst, union_ty);
   7709     if (loaded_union.flagsUnordered(ip).layout == .@"packed") return f.moveCValue(inst, union_ty, payload);
   7710 
   7711     const field: CValue = if (union_ty.unionTagTypeSafety(zcu)) |tag_ty| field: {
   7712         const layout = union_ty.unionGetLayout(zcu);
   7713         if (layout.tag_size != 0) {
   7714             const field_index = tag_ty.enumFieldIndex(field_name, zcu).?;
   7715             const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
   7716 
   7717             const a = try Assignment.start(f, writer, try f.ctypeFromType(tag_ty, .complete));
   7718             try f.writeCValueMember(writer, local, .{ .identifier = "tag" });
   7719             try a.assign(f, writer);
   7720             try writer.print("{f}", .{try f.fmtIntLiteralDec(try tag_val.intFromEnum(tag_ty, pt))});
   7721             try a.end(f, writer);
   7722         }
   7723         break :field .{ .payload_identifier = field_name.toSlice(ip) };
   7724     } else .{ .identifier = field_name.toSlice(ip) };
   7725 
   7726     const a = try Assignment.start(f, writer, try f.ctypeFromType(payload_ty, .complete));
   7727     try f.writeCValueMember(writer, local, field);
   7728     try a.assign(f, writer);
   7729     try f.writeCValue(writer, payload, .Other);
   7730     try a.end(f, writer);
   7731     return local;
   7732 }
   7733 
   7734 fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
   7735     const pt = f.object.dg.pt;
   7736     const zcu = pt.zcu;
   7737     const prefetch = f.air.instructions.items(.data)[@intFromEnum(inst)].prefetch;
   7738 
   7739     const ptr_ty = f.typeOf(prefetch.ptr);
   7740     const ptr = try f.resolveInst(prefetch.ptr);
   7741     try reap(f, inst, &.{prefetch.ptr});
   7742 
   7743     const writer = f.object.writer();
   7744     switch (prefetch.cache) {
   7745         .data => {
   7746             try writer.writeAll("zig_prefetch(");
   7747             if (ptr_ty.isSlice(zcu))
   7748                 try f.writeCValueMember(writer, ptr, .{ .identifier = "ptr" })
   7749             else
   7750                 try f.writeCValue(writer, ptr, .FunctionArgument);
   7751             try writer.print(", {d}, {d});\n", .{ @intFromEnum(prefetch.rw), prefetch.locality });
   7752         },
   7753         // The available prefetch intrinsics do not accept a cache argument; only
   7754         // address, rw, and locality.
   7755         .instruction => {},
   7756     }
   7757 
   7758     return .none;
   7759 }
   7760 
   7761 fn airWasmMemorySize(f: *Function, inst: Air.Inst.Index) !CValue {
   7762     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   7763 
   7764     const writer = f.object.writer();
   7765     const inst_ty = f.typeOfIndex(inst);
   7766     const local = try f.allocLocal(inst, inst_ty);
   7767     try f.writeCValue(writer, local, .Other);
   7768 
   7769     try writer.writeAll(" = ");
   7770     try writer.print("zig_wasm_memory_size({d});\n", .{pl_op.payload});
   7771 
   7772     return local;
   7773 }
   7774 
   7775 fn airWasmMemoryGrow(f: *Function, inst: Air.Inst.Index) !CValue {
   7776     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   7777 
   7778     const writer = f.object.writer();
   7779     const inst_ty = f.typeOfIndex(inst);
   7780     const operand = try f.resolveInst(pl_op.operand);
   7781     try reap(f, inst, &.{pl_op.operand});
   7782     const local = try f.allocLocal(inst, inst_ty);
   7783     try f.writeCValue(writer, local, .Other);
   7784 
   7785     try writer.writeAll(" = ");
   7786     try writer.print("zig_wasm_memory_grow({d}, ", .{pl_op.payload});
   7787     try f.writeCValue(writer, operand, .FunctionArgument);
   7788     try writer.writeAll(");\n");
   7789     return local;
   7790 }
   7791 
   7792 fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
   7793     const pt = f.object.dg.pt;
   7794     const zcu = pt.zcu;
   7795     const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   7796     const bin_op = f.air.extraData(Air.Bin, pl_op.payload).data;
   7797 
   7798     const mulend1 = try f.resolveInst(bin_op.lhs);
   7799     const mulend2 = try f.resolveInst(bin_op.rhs);
   7800     const addend = try f.resolveInst(pl_op.operand);
   7801     try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs, pl_op.operand });
   7802 
   7803     const inst_ty = f.typeOfIndex(inst);
   7804     const inst_scalar_ty = inst_ty.scalarType(zcu);
   7805 
   7806     const writer = f.object.writer();
   7807     const local = try f.allocLocal(inst, inst_ty);
   7808     const v = try Vectorize.start(f, inst, writer, inst_ty);
   7809     try f.writeCValue(writer, local, .Other);
   7810     try v.elem(f, writer);
   7811     try writer.writeAll(" = zig_fma_");
   7812     try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty);
   7813     try writer.writeByte('(');
   7814     try f.writeCValue(writer, mulend1, .FunctionArgument);
   7815     try v.elem(f, writer);
   7816     try writer.writeAll(", ");
   7817     try f.writeCValue(writer, mulend2, .FunctionArgument);
   7818     try v.elem(f, writer);
   7819     try writer.writeAll(", ");
   7820     try f.writeCValue(writer, addend, .FunctionArgument);
   7821     try v.elem(f, writer);
   7822     try writer.writeAll(");\n");
   7823     try v.end(f, inst, writer);
   7824 
   7825     return local;
   7826 }
   7827 
   7828 fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue {
   7829     const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
   7830     const writer = f.object.writer();
   7831     const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty));
   7832     try f.writeCValue(writer, local, .Other);
   7833     try writer.writeAll(" = ");
   7834     try f.object.dg.renderNav(writer, ty_nav.nav, .Other);
   7835     try writer.writeAll(";\n");
   7836     return local;
   7837 }
   7838 
   7839 fn airCVaStart(f: *Function, inst: Air.Inst.Index) !CValue {
   7840     const pt = f.object.dg.pt;
   7841     const zcu = pt.zcu;
   7842     const inst_ty = f.typeOfIndex(inst);
   7843     const function_ty = zcu.navValue(f.object.dg.pass.nav).typeOf(zcu);
   7844     const function_info = (try f.ctypeFromType(function_ty, .complete)).info(&f.object.dg.ctype_pool).function;
   7845     assert(function_info.varargs);
   7846 
   7847     const writer = f.object.writer();
   7848     const local = try f.allocLocal(inst, inst_ty);
   7849     try writer.writeAll("va_start(*(va_list *)&");
   7850     try f.writeCValue(writer, local, .Other);
   7851     if (function_info.param_ctypes.len > 0) {
   7852         try writer.writeAll(", ");
   7853         try f.writeCValue(writer, .{ .arg = function_info.param_ctypes.len - 1 }, .FunctionArgument);
   7854     }
   7855     try writer.writeAll(");\n");
   7856     return local;
   7857 }
   7858 
   7859 fn airCVaArg(f: *Function, inst: Air.Inst.Index) !CValue {
   7860     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   7861 
   7862     const inst_ty = f.typeOfIndex(inst);
   7863     const va_list = try f.resolveInst(ty_op.operand);
   7864     try reap(f, inst, &.{ty_op.operand});
   7865 
   7866     const writer = f.object.writer();
   7867     const local = try f.allocLocal(inst, inst_ty);
   7868     try f.writeCValue(writer, local, .Other);
   7869     try writer.writeAll(" = va_arg(*(va_list *)");
   7870     try f.writeCValue(writer, va_list, .Other);
   7871     try writer.writeAll(", ");
   7872     try f.renderType(writer, ty_op.ty.toType());
   7873     try writer.writeAll(");\n");
   7874     return local;
   7875 }
   7876 
   7877 fn airCVaEnd(f: *Function, inst: Air.Inst.Index) !CValue {
   7878     const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   7879 
   7880     const va_list = try f.resolveInst(un_op);
   7881     try reap(f, inst, &.{un_op});
   7882 
   7883     const writer = f.object.writer();
   7884     try writer.writeAll("va_end(*(va_list *)");
   7885     try f.writeCValue(writer, va_list, .Other);
   7886     try writer.writeAll(");\n");
   7887     return .none;
   7888 }
   7889 
   7890 fn airCVaCopy(f: *Function, inst: Air.Inst.Index) !CValue {
   7891     const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   7892 
   7893     const inst_ty = f.typeOfIndex(inst);
   7894     const va_list = try f.resolveInst(ty_op.operand);
   7895     try reap(f, inst, &.{ty_op.operand});
   7896 
   7897     const writer = f.object.writer();
   7898     const local = try f.allocLocal(inst, inst_ty);
   7899     try writer.writeAll("va_copy(*(va_list *)&");
   7900     try f.writeCValue(writer, local, .Other);
   7901     try writer.writeAll(", *(va_list *)");
   7902     try f.writeCValue(writer, va_list, .Other);
   7903     try writer.writeAll(");\n");
   7904     return local;
   7905 }
   7906 
   7907 fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
   7908     return switch (order) {
   7909         // Note: unordered is actually even less atomic than relaxed
   7910         .unordered, .monotonic => "zig_memory_order_relaxed",
   7911         .acquire => "zig_memory_order_acquire",
   7912         .release => "zig_memory_order_release",
   7913         .acq_rel => "zig_memory_order_acq_rel",
   7914         .seq_cst => "zig_memory_order_seq_cst",
   7915     };
   7916 }
   7917 
   7918 fn writeMemoryOrder(w: anytype, order: std.builtin.AtomicOrder) !void {
   7919     return w.writeAll(toMemoryOrder(order));
   7920 }
   7921 
   7922 fn toCallingConvention(cc: std.builtin.CallingConvention, zcu: *Zcu) ?[]const u8 {
   7923     if (zcu.getTarget().cCallingConvention()) |ccc| {
   7924         if (cc.eql(ccc)) {
   7925             return null;
   7926         }
   7927     }
   7928     return switch (cc) {
   7929         .auto, .naked => null,
   7930 
   7931         .x86_64_sysv, .x86_sysv => "sysv_abi",
   7932         .x86_64_win, .x86_win => "ms_abi",
   7933         .x86_stdcall => "stdcall",
   7934         .x86_fastcall => "fastcall",
   7935         .x86_thiscall => "thiscall",
   7936 
   7937         .x86_vectorcall,
   7938         .x86_64_vectorcall,
   7939         => "vectorcall",
   7940 
   7941         .x86_64_regcall_v3_sysv,
   7942         .x86_64_regcall_v4_win,
   7943         .x86_regcall_v3,
   7944         .x86_regcall_v4_win,
   7945         => "regcall",
   7946 
   7947         .aarch64_vfabi => "aarch64_vector_pcs",
   7948         .aarch64_vfabi_sve => "aarch64_sve_pcs",
   7949 
   7950         .arm_aapcs => "pcs(\"aapcs\")",
   7951         .arm_aapcs_vfp => "pcs(\"aapcs-vfp\")",
   7952 
   7953         .arm_interrupt => |opts| switch (opts.type) {
   7954             .generic => "interrupt",
   7955             .irq => "interrupt(\"IRQ\")",
   7956             .fiq => "interrupt(\"FIQ\")",
   7957             .swi => "interrupt(\"SWI\")",
   7958             .abort => "interrupt(\"ABORT\")",
   7959             .undef => "interrupt(\"UNDEF\")",
   7960         },
   7961 
   7962         .avr_signal => "signal",
   7963 
   7964         .mips_interrupt,
   7965         .mips64_interrupt,
   7966         => |opts| switch (opts.mode) {
   7967             inline else => |m| "interrupt(\"" ++ @tagName(m) ++ "\")",
   7968         },
   7969 
   7970         .riscv64_lp64_v, .riscv32_ilp32_v => "riscv_vector_cc",
   7971 
   7972         .riscv32_interrupt,
   7973         .riscv64_interrupt,
   7974         => |opts| switch (opts.mode) {
   7975             inline else => |m| "interrupt(\"" ++ @tagName(m) ++ "\")",
   7976         },
   7977 
   7978         .m68k_rtd => "m68k_rtd",
   7979 
   7980         .avr_interrupt,
   7981         .csky_interrupt,
   7982         .m68k_interrupt,
   7983         .x86_interrupt,
   7984         .x86_64_interrupt,
   7985         => "interrupt",
   7986 
   7987         else => unreachable, // `Zcu.callconvSupported`
   7988     };
   7989 }
   7990 
   7991 fn toAtomicRmwSuffix(order: std.builtin.AtomicRmwOp) []const u8 {
   7992     return switch (order) {
   7993         .Xchg => "xchg",
   7994         .Add => "add",
   7995         .Sub => "sub",
   7996         .And => "and",
   7997         .Nand => "nand",
   7998         .Or => "or",
   7999         .Xor => "xor",
   8000         .Max => "max",
   8001         .Min => "min",
   8002     };
   8003 }
   8004 
   8005 const ArrayListWriter = ErrorOnlyGenericWriter(std.ArrayList(u8).Writer.Error);
   8006 
   8007 fn arrayListWriter(list: *std.ArrayList(u8)) ArrayListWriter {
   8008     return .{ .context = .{
   8009         .context = list,
   8010         .writeFn = struct {
   8011             fn write(context: *const anyopaque, bytes: []const u8) anyerror!usize {
   8012                 const l: *std.ArrayList(u8) = @alignCast(@constCast(@ptrCast(context)));
   8013                 return l.writer().write(bytes);
   8014             }
   8015         }.write,
   8016     } };
   8017 }
   8018 
   8019 fn IndentWriter(comptime UnderlyingWriter: type) type {
   8020     return struct {
   8021         const Self = @This();
   8022         pub const Error = UnderlyingWriter.Error;
   8023         pub const Writer = ErrorOnlyGenericWriter(Error);
   8024 
   8025         pub const indent_delta = 1;
   8026 
   8027         underlying_writer: UnderlyingWriter,
   8028         indent_count: usize = 0,
   8029         current_line_empty: bool = true,
   8030 
   8031         pub fn writer(self: *Self) Writer {
   8032             return .{ .context = .{
   8033                 .context = self,
   8034                 .writeFn = writeAny,
   8035             } };
   8036         }
   8037 
   8038         pub fn write(self: *Self, bytes: []const u8) Error!usize {
   8039             if (bytes.len == 0) return 0;
   8040 
   8041             const current_indent = self.indent_count * Self.indent_delta;
   8042             if (self.current_line_empty and current_indent > 0) {
   8043                 try self.underlying_writer.writeByteNTimes(' ', current_indent);
   8044             }
   8045             self.current_line_empty = false;
   8046 
   8047             return self.writeNoIndent(bytes);
   8048         }
   8049 
   8050         fn writeAny(context: *const anyopaque, bytes: []const u8) anyerror!usize {
   8051             const self: *Self = @alignCast(@constCast(@ptrCast(context)));
   8052             return self.write(bytes);
   8053         }
   8054 
   8055         pub fn insertNewline(self: *Self) Error!void {
   8056             _ = try self.writeNoIndent("\n");
   8057         }
   8058 
   8059         pub fn pushIndent(self: *Self) void {
   8060             self.indent_count += 1;
   8061         }
   8062 
   8063         pub fn popIndent(self: *Self) void {
   8064             assert(self.indent_count != 0);
   8065             self.indent_count -= 1;
   8066         }
   8067 
   8068         fn writeNoIndent(self: *Self, bytes: []const u8) Error!usize {
   8069             if (bytes.len == 0) return 0;
   8070 
   8071             try self.underlying_writer.writeAll(bytes);
   8072             if (bytes[bytes.len - 1] == '\n') {
   8073                 self.current_line_empty = true;
   8074             }
   8075             return bytes.len;
   8076         }
   8077     };
   8078 }
   8079 
   8080 /// A wrapper around `std.io.AnyWriter` that maintains a generic error set while
   8081 /// erasing the rest of the implementation. This is intended to avoid duplicate
   8082 /// generic instantiations for writer types which share the same error set, while
   8083 /// maintaining ease of error handling.
   8084 fn ErrorOnlyGenericWriter(comptime Error: type) type {
   8085     return std.io.GenericWriter(std.io.AnyWriter, Error, struct {
   8086         fn write(context: std.io.AnyWriter, bytes: []const u8) Error!usize {
   8087             return @errorCast(context.write(bytes));
   8088         }
   8089     }.write);
   8090 }
   8091 
   8092 fn toCIntBits(zig_bits: u32) ?u32 {
   8093     for (&[_]u8{ 8, 16, 32, 64, 128 }) |c_bits| {
   8094         if (zig_bits <= c_bits) {
   8095             return c_bits;
   8096         }
   8097     }
   8098     return null;
   8099 }
   8100 
   8101 fn signAbbrev(signedness: std.builtin.Signedness) u8 {
   8102     return switch (signedness) {
   8103         .signed => 'i',
   8104         .unsigned => 'u',
   8105     };
   8106 }
   8107 
   8108 fn compilerRtAbbrev(ty: Type, zcu: *Zcu, target: *const std.Target) []const u8 {
   8109     return if (ty.isInt(zcu)) switch (ty.intInfo(zcu).bits) {
   8110         1...32 => "si",
   8111         33...64 => "di",
   8112         65...128 => "ti",
   8113         else => unreachable,
   8114     } else if (ty.isRuntimeFloat()) switch (ty.floatBits(target)) {
   8115         16 => "hf",
   8116         32 => "sf",
   8117         64 => "df",
   8118         80 => "xf",
   8119         128 => "tf",
   8120         else => unreachable,
   8121     } else unreachable;
   8122 }
   8123 
   8124 fn compareOperatorAbbrev(operator: std.math.CompareOperator) []const u8 {
   8125     return switch (operator) {
   8126         .lt => "lt",
   8127         .lte => "le",
   8128         .eq => "eq",
   8129         .gte => "ge",
   8130         .gt => "gt",
   8131         .neq => "ne",
   8132     };
   8133 }
   8134 
   8135 fn compareOperatorC(operator: std.math.CompareOperator) []const u8 {
   8136     return switch (operator) {
   8137         .lt => " < ",
   8138         .lte => " <= ",
   8139         .eq => " == ",
   8140         .gte => " >= ",
   8141         .gt => " > ",
   8142         .neq => " != ",
   8143     };
   8144 }
   8145 
   8146 const StringLiteral = struct {
   8147     len: usize,
   8148     cur_len: usize,
   8149     start_count: usize,
   8150     writer: *std.io.Writer,
   8151 
   8152     // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal,
   8153     // regardless of the length of the string literal initializing it. Array initializer syntax is
   8154     // used instead.
   8155     // C99 only requires 4095.
   8156     const max_string_initializer_len = @min(65535, 4095);
   8157 
   8158     // MSVC has a length limit of 16380 per string literal (before concatenation)
   8159     // C99 only requires 4095.
   8160     const max_char_len = 4;
   8161     const max_literal_len = @min(16380 - max_char_len, 4095);
   8162 
   8163     fn init(writer: *std.io.Writer, len: usize) StringLiteral {
   8164         return .{
   8165             .cur_len = 0,
   8166             .len = len,
   8167             .start_count = writer.count,
   8168             .writer = writer,
   8169         };
   8170     }
   8171 
   8172     pub fn start(sl: *StringLiteral) std.io.Writer.Error!void {
   8173         if (sl.len <= max_string_initializer_len) {
   8174             try sl.writer.writeByte('\"');
   8175         } else {
   8176             try sl.writer.writeByte('{');
   8177         }
   8178     }
   8179 
   8180     pub fn end(sl: *StringLiteral) std.io.Writer.Error!void {
   8181         if (sl.len <= max_string_initializer_len) {
   8182             try sl.writer.writeByte('\"');
   8183         } else {
   8184             try sl.writer.writeByte('}');
   8185         }
   8186     }
   8187 
   8188     fn writeStringLiteralChar(sl: *StringLiteral, c: u8) std.io.Writer.Error!void {
   8189         switch (c) {
   8190             7 => try sl.writer.writeAll("\\a"),
   8191             8 => try sl.writer.writeAll("\\b"),
   8192             '\t' => try sl.writer.writeAll("\\t"),
   8193             '\n' => try sl.writer.writeAll("\\n"),
   8194             11 => try sl.writer.writeAll("\\v"),
   8195             12 => try sl.writer.writeAll("\\f"),
   8196             '\r' => try sl.writer.writeAll("\\r"),
   8197             '"', '\'', '?', '\\' => try sl.writer.print("\\{c}", .{c}),
   8198             else => switch (c) {
   8199                 ' '...'~' => try sl.writer.writeByte(c),
   8200                 else => try sl.writer.print("\\{o:0>3}", .{c}),
   8201             },
   8202         }
   8203     }
   8204 
   8205     pub fn writeChar(sl: *StringLiteral, c: u8) std.io.Writer.Error!void {
   8206         if (sl.len <= max_string_initializer_len) {
   8207             if (sl.cur_len == 0 and sl.writer.count - sl.start_count > 1)
   8208                 try sl.writer.writeAll("\"\"");
   8209 
   8210             const count = sl.writer.count;
   8211             try sl.writeStringLiteralChar(c);
   8212             const char_len = sl.writer.count - count;
   8213             assert(char_len <= max_char_len);
   8214             sl.cur_len += char_len;
   8215 
   8216             if (sl.cur_len >= max_literal_len) sl.cur_len = 0;
   8217         } else {
   8218             if (sl.writer.count - sl.start_count > 1) try sl.writer.writeByte(',');
   8219             try sl.writer.print("'\\x{x}'", .{c});
   8220         }
   8221     }
   8222 };
   8223 
   8224 const FormatStringContext = struct {
   8225     str: []const u8,
   8226     sentinel: ?u8,
   8227 };
   8228 
   8229 fn formatStringLiteral(data: FormatStringContext, writer: *std.io.Writer) std.io.Writer.Error!void {
   8230     var literal: StringLiteral = .init(writer, data.str.len + @intFromBool(data.sentinel != null));
   8231     try literal.start();
   8232     for (data.str) |c| try literal.writeChar(c);
   8233     if (data.sentinel) |sentinel| if (sentinel != 0) try literal.writeChar(sentinel);
   8234     try literal.end();
   8235 }
   8236 
   8237 fn fmtStringLiteral(str: []const u8, sentinel: ?u8) std.fmt.Formatter(FormatStringContext, formatStringLiteral) {
   8238     return .{ .data = .{ .str = str, .sentinel = sentinel } };
   8239 }
   8240 
   8241 fn undefPattern(comptime IntType: type) IntType {
   8242     const int_info = @typeInfo(IntType).int;
   8243     const UnsignedType = std.meta.Int(.unsigned, int_info.bits);
   8244     return @bitCast(@as(UnsignedType, (1 << (int_info.bits | 1)) / 3));
   8245 }
   8246 
   8247 const FormatIntLiteralContext = struct {
   8248     dg: *DeclGen,
   8249     int_info: InternPool.Key.IntType,
   8250     kind: CType.Kind,
   8251     ctype: CType,
   8252     val: Value,
   8253     base: u8,
   8254     case: std.fmt.Case,
   8255 };
   8256 fn formatIntLiteral(data: FormatIntLiteralContext, writer: *std.io.Writer) std.io.Writer.Error!void {
   8257     const pt = data.dg.pt;
   8258     const zcu = pt.zcu;
   8259     const target = &data.dg.mod.resolved_target.result;
   8260     const ctype_pool = &data.dg.ctype_pool;
   8261 
   8262     const ExpectedContents = struct {
   8263         const base = 10;
   8264         const bits = 128;
   8265         const limbs_count = BigInt.calcTwosCompLimbCount(bits);
   8266 
   8267         undef_limbs: [limbs_count]BigIntLimb,
   8268         wrap_limbs: [limbs_count]BigIntLimb,
   8269         to_string_buf: [bits]u8,
   8270         to_string_limbs: [BigInt.calcToStringLimbsBufferLen(limbs_count, base)]BigIntLimb,
   8271     };
   8272     var stack align(@alignOf(ExpectedContents)) =
   8273         std.heap.stackFallback(@sizeOf(ExpectedContents), data.dg.gpa);
   8274     const allocator = stack.get();
   8275 
   8276     var undef_limbs: []BigIntLimb = &.{};
   8277     defer allocator.free(undef_limbs);
   8278 
   8279     var int_buf: Value.BigIntSpace = undefined;
   8280     const int = if (data.val.isUndefDeep(zcu)) blk: {
   8281         undef_limbs = try oom(allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(data.int_info.bits)));
   8282         @memset(undef_limbs, undefPattern(BigIntLimb));
   8283 
   8284         var undef_int = BigInt.Mutable{
   8285             .limbs = undef_limbs,
   8286             .len = undef_limbs.len,
   8287             .positive = true,
   8288         };
   8289         undef_int.truncate(undef_int.toConst(), data.int_info.signedness, data.int_info.bits);
   8290         break :blk undef_int.toConst();
   8291     } else data.val.toBigInt(&int_buf, zcu);
   8292     assert(int.fitsInTwosComp(data.int_info.signedness, data.int_info.bits));
   8293 
   8294     const c_bits: usize = @intCast(data.ctype.byteSize(ctype_pool, data.dg.mod) * 8);
   8295     var one_limbs: [BigInt.calcLimbLen(1)]BigIntLimb = undefined;
   8296     const one = BigInt.Mutable.init(&one_limbs, 1).toConst();
   8297 
   8298     var wrap = BigInt.Mutable{
   8299         .limbs = try oom(allocator.alloc(BigIntLimb, BigInt.calcTwosCompLimbCount(c_bits))),
   8300         .len = undefined,
   8301         .positive = undefined,
   8302     };
   8303     defer allocator.free(wrap.limbs);
   8304 
   8305     const c_limb_info: struct {
   8306         ctype: CType,
   8307         count: usize,
   8308         endian: std.builtin.Endian,
   8309         homogeneous: bool,
   8310     } = switch (data.ctype.info(ctype_pool)) {
   8311         .basic => |basic_info| switch (basic_info) {
   8312             else => .{
   8313                 .ctype = .void,
   8314                 .count = 1,
   8315                 .endian = .little,
   8316                 .homogeneous = true,
   8317             },
   8318             .zig_u128, .zig_i128 => .{
   8319                 .ctype = .u64,
   8320                 .count = 2,
   8321                 .endian = .big,
   8322                 .homogeneous = false,
   8323             },
   8324         },
   8325         .array => |array_info| .{
   8326             .ctype = array_info.elem_ctype,
   8327             .count = @intCast(array_info.len),
   8328             .endian = target.cpu.arch.endian(),
   8329             .homogeneous = true,
   8330         },
   8331         else => unreachable,
   8332     };
   8333     if (c_limb_info.count == 1) {
   8334         if (wrap.addWrap(int, one, data.int_info.signedness, c_bits) or
   8335             data.int_info.signedness == .signed and wrap.subWrap(int, one, data.int_info.signedness, c_bits))
   8336             return writer.print("{s}_{s}", .{
   8337                 data.ctype.getStandardDefineAbbrev() orelse return writer.print("zig_{s}Int_{c}{d}", .{
   8338                     if (int.positive) "max" else "min", signAbbrev(data.int_info.signedness), c_bits,
   8339                 }),
   8340                 if (int.positive) "MAX" else "MIN",
   8341             });
   8342 
   8343         if (!int.positive) try writer.writeByte('-');
   8344         try data.ctype.renderLiteralPrefix(writer, data.kind, ctype_pool);
   8345 
   8346         switch (data.base) {
   8347             2 => try writer.writeAll("0b"),
   8348             8 => try writer.writeByte('0'),
   8349             10 => {},
   8350             16 => try writer.writeAll("0x"),
   8351             else => unreachable,
   8352         }
   8353         const string = try oom(int.abs().toStringAlloc(allocator, data.base, data.case));
   8354         defer allocator.free(string);
   8355         try writer.writeAll(string);
   8356     } else {
   8357         try data.ctype.renderLiteralPrefix(writer, data.kind, ctype_pool);
   8358         wrap.truncate(int, .unsigned, c_bits);
   8359         @memset(wrap.limbs[wrap.len..], 0);
   8360         wrap.len = wrap.limbs.len;
   8361         const limbs_per_c_limb = @divExact(wrap.len, c_limb_info.count);
   8362 
   8363         var c_limb_int_info: std.builtin.Type.Int = .{
   8364             .signedness = undefined,
   8365             .bits = @intCast(@divExact(c_bits, c_limb_info.count)),
   8366         };
   8367         var c_limb_ctype: CType = undefined;
   8368 
   8369         var limb_offset: usize = 0;
   8370         const most_significant_limb_i = wrap.len - limbs_per_c_limb;
   8371         while (limb_offset < wrap.len) : (limb_offset += limbs_per_c_limb) {
   8372             const limb_i = switch (c_limb_info.endian) {
   8373                 .little => limb_offset,
   8374                 .big => most_significant_limb_i - limb_offset,
   8375             };
   8376             var c_limb_mut = BigInt.Mutable{
   8377                 .limbs = wrap.limbs[limb_i..][0..limbs_per_c_limb],
   8378                 .len = undefined,
   8379                 .positive = true,
   8380             };
   8381             c_limb_mut.normalize(limbs_per_c_limb);
   8382 
   8383             if (limb_i == most_significant_limb_i and
   8384                 !c_limb_info.homogeneous and data.int_info.signedness == .signed)
   8385             {
   8386                 // most significant limb is actually signed
   8387                 c_limb_int_info.signedness = .signed;
   8388                 c_limb_ctype = c_limb_info.ctype.toSigned();
   8389 
   8390                 c_limb_mut.truncate(
   8391                     c_limb_mut.toConst(),
   8392                     .signed,
   8393                     data.int_info.bits - limb_i * @bitSizeOf(BigIntLimb),
   8394                 );
   8395             } else {
   8396                 c_limb_int_info.signedness = .unsigned;
   8397                 c_limb_ctype = c_limb_info.ctype;
   8398             }
   8399 
   8400             if (limb_offset > 0) try writer.writeAll(", ");
   8401             try formatIntLiteral(.{
   8402                 .dg = data.dg,
   8403                 .int_info = c_limb_int_info,
   8404                 .kind = data.kind,
   8405                 .ctype = c_limb_ctype,
   8406                 .val = try oom(pt.intValue_big(.comptime_int, c_limb_mut.toConst())),
   8407                 .base = data.base,
   8408                 .case = data.case,
   8409             }, writer);
   8410         }
   8411     }
   8412     try data.ctype.renderLiteralSuffix(writer, ctype_pool);
   8413 }
   8414 
   8415 const Materialize = struct {
   8416     local: CValue,
   8417 
   8418     pub fn start(f: *Function, inst: Air.Inst.Index, ty: Type, value: CValue) !Materialize {
   8419         return .{ .local = switch (value) {
   8420             .local_ref, .constant, .nav_ref, .undef => try f.moveCValue(inst, ty, value),
   8421             .new_local => |local| .{ .local = local },
   8422             else => value,
   8423         } };
   8424     }
   8425 
   8426     pub fn mat(self: Materialize, f: *Function, writer: anytype) !void {
   8427         try f.writeCValue(writer, self.local, .Other);
   8428     }
   8429 
   8430     pub fn end(self: Materialize, f: *Function, inst: Air.Inst.Index) !void {
   8431         try f.freeCValue(inst, self.local);
   8432     }
   8433 };
   8434 
   8435 const Assignment = struct {
   8436     ctype: CType,
   8437 
   8438     pub fn start(f: *Function, writer: anytype, ctype: CType) !Assignment {
   8439         const self: Assignment = .{ .ctype = ctype };
   8440         try self.restart(f, writer);
   8441         return self;
   8442     }
   8443 
   8444     pub fn restart(self: Assignment, f: *Function, writer: anytype) !void {
   8445         switch (self.strategy(f)) {
   8446             .assign => {},
   8447             .memcpy => try writer.writeAll("memcpy("),
   8448         }
   8449     }
   8450 
   8451     pub fn assign(self: Assignment, f: *Function, writer: anytype) !void {
   8452         switch (self.strategy(f)) {
   8453             .assign => try writer.writeAll(" = "),
   8454             .memcpy => try writer.writeAll(", "),
   8455         }
   8456     }
   8457 
   8458     pub fn end(self: Assignment, f: *Function, writer: anytype) !void {
   8459         switch (self.strategy(f)) {
   8460             .assign => {},
   8461             .memcpy => {
   8462                 try writer.writeAll(", sizeof(");
   8463                 try f.renderCType(writer, self.ctype);
   8464                 try writer.writeAll("))");
   8465             },
   8466         }
   8467         try writer.writeAll(";\n");
   8468     }
   8469 
   8470     fn strategy(self: Assignment, f: *Function) enum { assign, memcpy } {
   8471         return switch (self.ctype.info(&f.object.dg.ctype_pool)) {
   8472             else => .assign,
   8473             .array, .vector => .memcpy,
   8474         };
   8475     }
   8476 };
   8477 
   8478 const Vectorize = struct {
   8479     index: CValue = .none,
   8480 
   8481     pub fn start(f: *Function, inst: Air.Inst.Index, writer: anytype, ty: Type) !Vectorize {
   8482         const pt = f.object.dg.pt;
   8483         const zcu = pt.zcu;
   8484         return if (ty.zigTypeTag(zcu) == .vector) index: {
   8485             const local = try f.allocLocal(inst, .usize);
   8486 
   8487             try writer.writeAll("for (");
   8488             try f.writeCValue(writer, local, .Other);
   8489             try writer.print(" = {f}; ", .{try f.fmtIntLiteralDec(.zero_usize)});
   8490             try f.writeCValue(writer, local, .Other);
   8491             try writer.print(" < {f}; ", .{try f.fmtIntLiteralDec(try pt.intValue(.usize, ty.vectorLen(zcu)))});
   8492             try f.writeCValue(writer, local, .Other);
   8493             try writer.print(" += {f}) {{\n", .{try f.fmtIntLiteralDec(.one_usize)});
   8494             f.object.indent_writer.pushIndent();
   8495 
   8496             break :index .{ .index = local };
   8497         } else .{};
   8498     }
   8499 
   8500     pub fn elem(self: Vectorize, f: *Function, writer: anytype) !void {
   8501         if (self.index != .none) {
   8502             try writer.writeByte('[');
   8503             try f.writeCValue(writer, self.index, .Other);
   8504             try writer.writeByte(']');
   8505         }
   8506     }
   8507 
   8508     pub fn end(self: Vectorize, f: *Function, inst: Air.Inst.Index, writer: anytype) !void {
   8509         if (self.index != .none) {
   8510             f.object.indent_writer.popIndent();
   8511             try writer.writeAll("}\n");
   8512             try freeLocal(f, inst, self.index.new_local, null);
   8513         }
   8514     }
   8515 };
   8516 
   8517 fn lowersToArray(ty: Type, pt: Zcu.PerThread) bool {
   8518     const zcu = pt.zcu;
   8519     return switch (ty.zigTypeTag(zcu)) {
   8520         .array, .vector => return true,
   8521         else => return ty.isAbiInt(zcu) and toCIntBits(@as(u32, @intCast(ty.bitSize(zcu)))) == null,
   8522     };
   8523 }
   8524 
   8525 fn reap(f: *Function, inst: Air.Inst.Index, operands: []const Air.Inst.Ref) !void {
   8526     assert(operands.len <= Air.Liveness.bpi - 1);
   8527     var tomb_bits = f.liveness.getTombBits(inst);
   8528     for (operands) |operand| {
   8529         const dies = @as(u1, @truncate(tomb_bits)) != 0;
   8530         tomb_bits >>= 1;
   8531         if (!dies) continue;
   8532         try die(f, inst, operand);
   8533     }
   8534 }
   8535 
   8536 fn die(f: *Function, inst: Air.Inst.Index, ref: Air.Inst.Ref) !void {
   8537     const ref_inst = ref.toIndex() orelse return;
   8538     const c_value = (f.value_map.fetchRemove(ref) orelse return).value;
   8539     const local_index = switch (c_value) {
   8540         .new_local, .local => |l| l,
   8541         else => return,
   8542     };
   8543     try freeLocal(f, inst, local_index, ref_inst);
   8544 }
   8545 
   8546 fn freeLocal(f: *Function, inst: ?Air.Inst.Index, local_index: LocalIndex, ref_inst: ?Air.Inst.Index) !void {
   8547     const gpa = f.object.dg.gpa;
   8548     const local = &f.locals.items[local_index];
   8549     if (inst) |i| {
   8550         if (ref_inst) |operand| {
   8551             log.debug("%{d}: freeing t{d} (operand %{d})", .{ @intFromEnum(i), local_index, operand });
   8552         } else {
   8553             log.debug("%{d}: freeing t{d}", .{ @intFromEnum(i), local_index });
   8554         }
   8555     } else {
   8556         if (ref_inst) |operand| {
   8557             log.debug("freeing t{d} (operand %{d})", .{ local_index, operand });
   8558         } else {
   8559             log.debug("freeing t{d}", .{local_index});
   8560         }
   8561     }
   8562     const gop = try f.free_locals_map.getOrPut(gpa, local.getType());
   8563     if (!gop.found_existing) gop.value_ptr.* = .{};
   8564     if (std.debug.runtime_safety) {
   8565         // If this trips, an unfreeable allocation was attempted to be freed.
   8566         assert(!f.allocs.contains(local_index));
   8567     }
   8568     // If this trips, it means a local is being inserted into the
   8569     // free_locals map while it already exists in the map, which is not
   8570     // allowed.
   8571     try gop.value_ptr.putNoClobber(gpa, local_index, {});
   8572 }
   8573 
   8574 const BigTomb = struct {
   8575     f: *Function,
   8576     inst: Air.Inst.Index,
   8577     lbt: Air.Liveness.BigTomb,
   8578 
   8579     fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) !void {
   8580         const dies = bt.lbt.feed();
   8581         if (!dies) return;
   8582         try die(bt.f, bt.inst, op_ref);
   8583     }
   8584 };
   8585 
   8586 fn iterateBigTomb(f: *Function, inst: Air.Inst.Index) BigTomb {
   8587     return .{
   8588         .f = f,
   8589         .inst = inst,
   8590         .lbt = f.liveness.iterateBigTomb(inst),
   8591     };
   8592 }
   8593 
   8594 /// A naive clone of this map would create copies of the ArrayList which is
   8595 /// stored in the values. This function additionally clones the values.
   8596 fn cloneFreeLocalsMap(gpa: Allocator, map: *LocalsMap) !LocalsMap {
   8597     var cloned = try map.clone(gpa);
   8598     const values = cloned.values();
   8599     var i: usize = 0;
   8600     errdefer {
   8601         cloned.deinit(gpa);
   8602         while (i > 0) {
   8603             i -= 1;
   8604             values[i].deinit(gpa);
   8605         }
   8606     }
   8607     while (i < values.len) : (i += 1) {
   8608         values[i] = try values[i].clone(gpa);
   8609     }
   8610     return cloned;
   8611 }
   8612 
   8613 fn deinitFreeLocalsMap(gpa: Allocator, map: *LocalsMap) void {
   8614     for (map.values()) |*value| {
   8615         value.deinit(gpa);
   8616     }
   8617     map.deinit(gpa);
   8618 }
   8619 
   8620 fn oom(x: anytype) error{WriteFailed}!@typeInfo(@TypeOf(x)).error_union.payload {
   8621     return x catch |err| switch (err) {
   8622         error.OutOfMemory => error.WriteFailed,
   8623     };
   8624 }