zig

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

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