zig

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

blob d4227ddf (249085B) - Raw


      1 const std = @import("std");
      2 const Allocator = std.mem.Allocator;
      3 const Target = std.Target;
      4 const Signedness = std.builtin.Signedness;
      5 const assert = std.debug.assert;
      6 const log = std.log.scoped(.codegen);
      7 
      8 const Zcu = @import("../../Zcu.zig");
      9 const Type = @import("../../Type.zig");
     10 const Value = @import("../../Value.zig");
     11 const Air = @import("../../Air.zig");
     12 const InternPool = @import("../../InternPool.zig");
     13 const Section = @import("Section.zig");
     14 const Assembler = @import("Assembler.zig");
     15 
     16 const spec = @import("spec.zig");
     17 const Opcode = spec.Opcode;
     18 const Word = spec.Word;
     19 const Id = spec.Id;
     20 const IdRange = spec.IdRange;
     21 const StorageClass = spec.StorageClass;
     22 
     23 const Module = @import("Module.zig");
     24 const Decl = Module.Decl;
     25 const Repr = Module.Repr;
     26 const InternMap = Module.InternMap;
     27 const PtrTypeMap = Module.PtrTypeMap;
     28 
     29 const CodeGen = @This();
     30 
     31 pub fn legalizeFeatures(_: *const std.Target) *const Air.Legalize.Features {
     32     return comptime &.initMany(&.{
     33         .expand_intcast_safe,
     34         .expand_int_from_float_safe,
     35         .expand_int_from_float_optimized_safe,
     36         .expand_add_safe,
     37         .expand_sub_safe,
     38         .expand_mul_safe,
     39     });
     40 }
     41 
     42 pub const zig_call_abi_ver = 3;
     43 
     44 const ControlFlow = union(enum) {
     45     const Structured = struct {
     46         /// This type indicates the way that a block is terminated. The
     47         /// state of a particular block is used to track how a jump from
     48         /// inside the block must reach the outside.
     49         const Block = union(enum) {
     50             const Incoming = struct {
     51                 src_label: Id,
     52                 /// Instruction that returns an u32 value of the
     53                 /// `Air.Inst.Index` that control flow should jump to.
     54                 next_block: Id,
     55             };
     56 
     57             const SelectionMerge = struct {
     58                 /// Incoming block from the `then` label.
     59                 /// Note that hte incoming block from the `else` label is
     60                 /// either given by the next element in the stack.
     61                 incoming: Incoming,
     62                 /// The label id of the cond_br's merge block.
     63                 /// For the top-most element in the stack, this
     64                 /// value is undefined.
     65                 merge_block: Id,
     66             };
     67 
     68             /// For a `selection` type block, we cannot use early exits, and we
     69             /// must generate a 'merge ladder' of OpSelection instructions. To that end,
     70             /// we keep a stack of the merges that still must be closed at the end of
     71             /// a block.
     72             ///
     73             /// This entire structure basically just resembles a tree like
     74             ///     a   x
     75             ///      \ /
     76             ///   b   o   merge
     77             ///    \ /
     78             /// c   o   merge
     79             ///  \ /
     80             ///   o   merge
     81             ///  /
     82             /// o   jump to next block
     83             selection: struct {
     84                 /// In order to know which merges we still need to do, we need to keep
     85                 /// a stack of those.
     86                 merge_stack: std.ArrayList(SelectionMerge) = .empty,
     87             },
     88             /// For a `loop` type block, we can early-exit the block by
     89             /// jumping to the loop exit node, and we don't need to generate
     90             /// an entire stack of merges.
     91             loop: struct {
     92                 /// The next block to jump to can be determined from any number
     93                 /// of conditions that jump to the loop exit.
     94                 merges: std.ArrayList(Incoming) = .empty,
     95                 /// The label id of the loop's merge block.
     96                 merge_block: Id,
     97             },
     98 
     99             fn deinit(block: *Structured.Block, gpa: Allocator) void {
    100                 switch (block.*) {
    101                     .selection => |*merge| merge.merge_stack.deinit(gpa),
    102                     .loop => |*merge| merge.merges.deinit(gpa),
    103                 }
    104                 block.* = undefined;
    105             }
    106         };
    107         /// This determines how exits from the current block must be handled.
    108         block_stack: std.ArrayList(*Structured.Block) = .empty,
    109         block_results: std.AutoHashMapUnmanaged(Air.Inst.Index, Id) = .empty,
    110     };
    111 
    112     const Unstructured = struct {
    113         const Incoming = struct {
    114             src_label: Id,
    115             break_value_id: Id,
    116         };
    117 
    118         const Block = struct {
    119             label: ?Id = null,
    120             incoming_blocks: std.ArrayList(Incoming) = .empty,
    121         };
    122 
    123         /// We need to keep track of result ids for block labels, as well as the 'incoming'
    124         /// blocks for a block.
    125         blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *Block) = .empty,
    126     };
    127 
    128     structured: Structured,
    129     unstructured: Unstructured,
    130 
    131     pub fn deinit(cg: *ControlFlow, gpa: Allocator) void {
    132         switch (cg.*) {
    133             .structured => |*cf| {
    134                 cf.block_stack.deinit(gpa);
    135                 cf.block_results.deinit(gpa);
    136             },
    137             .unstructured => |*cf| {
    138                 cf.blocks.deinit(gpa);
    139             },
    140         }
    141         cg.* = undefined;
    142     }
    143 };
    144 
    145 pt: Zcu.PerThread,
    146 air: Air,
    147 liveness: Air.Liveness,
    148 owner_nav: InternPool.Nav.Index,
    149 module: *Module,
    150 control_flow: ControlFlow,
    151 base_line: u32,
    152 block_label: Id = .none,
    153 next_arg_index: u32 = 0,
    154 args: std.ArrayList(Id) = .empty,
    155 inst_results: std.AutoHashMapUnmanaged(Air.Inst.Index, Id) = .empty,
    156 id_scratch: std.ArrayList(Id) = .empty,
    157 prologue: Section = .{},
    158 body: Section = .{},
    159 error_msg: ?*Zcu.ErrorMsg = null,
    160 
    161 pub fn deinit(cg: *CodeGen) void {
    162     const gpa = cg.module.gpa;
    163     cg.control_flow.deinit(gpa);
    164     cg.args.deinit(gpa);
    165     cg.inst_results.deinit(gpa);
    166     cg.id_scratch.deinit(gpa);
    167     cg.prologue.deinit(gpa);
    168     cg.body.deinit(gpa);
    169 }
    170 
    171 const Error = error{ CodegenFail, OutOfMemory };
    172 
    173 pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
    174     const gpa = cg.module.gpa;
    175     const zcu = cg.module.zcu;
    176     const ip = &zcu.intern_pool;
    177     const target = zcu.getTarget();
    178 
    179     const nav = ip.getNav(cg.owner_nav);
    180     const val = zcu.navValue(cg.owner_nav);
    181     const ty = val.typeOf(zcu);
    182 
    183     if (!do_codegen and !ty.hasRuntimeBits(zcu)) return;
    184 
    185     const spv_decl_index = try cg.module.resolveNav(ip, cg.owner_nav);
    186     const decl = cg.module.declPtr(spv_decl_index);
    187     const result_id = decl.result_id;
    188     decl.begin_dep = cg.module.decl_deps.items.len;
    189 
    190     switch (decl.kind) {
    191         .func => {
    192             const fn_info = zcu.typeToFunc(ty).?;
    193             const return_ty_id = try cg.resolveFnReturnType(.fromInterned(fn_info.return_type));
    194             const is_test = zcu.test_functions.contains(cg.owner_nav);
    195 
    196             const func_result_id = if (is_test) cg.module.allocId() else result_id;
    197             const prototype_ty_id = try cg.resolveType(ty, .direct);
    198             try cg.prologue.emit(gpa, .OpFunction, .{
    199                 .id_result_type = return_ty_id,
    200                 .id_result = func_result_id,
    201                 .function_type = prototype_ty_id,
    202                 // Note: the backend will never be asked to generate an inline function
    203                 // (this is handled in sema), so we don't need to set function_control here.
    204                 .function_control = .{},
    205             });
    206 
    207             comptime assert(zig_call_abi_ver == 3);
    208             try cg.args.ensureUnusedCapacity(gpa, fn_info.param_types.len);
    209             for (fn_info.param_types.get(ip)) |param_ty_index| {
    210                 const param_ty: Type = .fromInterned(param_ty_index);
    211                 if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
    212 
    213                 const param_type_id = try cg.resolveType(param_ty, .direct);
    214                 const arg_result_id = cg.module.allocId();
    215                 try cg.prologue.emit(gpa, .OpFunctionParameter, .{
    216                     .id_result_type = param_type_id,
    217                     .id_result = arg_result_id,
    218                 });
    219                 cg.args.appendAssumeCapacity(arg_result_id);
    220             }
    221 
    222             // TODO: This could probably be done in a better way...
    223             const root_block_id = cg.module.allocId();
    224 
    225             // The root block of a function declaration should appear before OpVariable instructions,
    226             // so it is generated into the function's prologue.
    227             try cg.prologue.emit(gpa, .OpLabel, .{
    228                 .id_result = root_block_id,
    229             });
    230             cg.block_label = root_block_id;
    231 
    232             const main_body = cg.air.getMainBody();
    233             switch (cg.control_flow) {
    234                 .structured => {
    235                     _ = try cg.genStructuredBody(.selection, main_body);
    236                     // We always expect paths to here to end, but we still need the block
    237                     // to act as a dummy merge block.
    238                     try cg.body.emit(gpa, .OpUnreachable, {});
    239                 },
    240                 .unstructured => {
    241                     try cg.genBody(main_body);
    242                 },
    243             }
    244             try cg.body.emit(gpa, .OpFunctionEnd, {});
    245             // Append the actual code into the functions section.
    246             try cg.module.sections.functions.append(gpa, cg.prologue);
    247             try cg.module.sections.functions.append(gpa, cg.body);
    248 
    249             // Temporarily generate a test kernel declaration if this is a test function.
    250             if (is_test) {
    251                 try cg.generateTestEntryPoint(nav.fqn.toSlice(ip), spv_decl_index, func_result_id);
    252             }
    253 
    254             try cg.module.debugName(func_result_id, nav.fqn.toSlice(ip));
    255         },
    256         .global => {
    257             const key = ip.indexToKey(val.toIntern()).@"extern";
    258 
    259             const storage_class = cg.module.storageClass(nav.getAddrspace());
    260             assert(storage_class != .generic); // These should be instance globals
    261 
    262             const ty_id = try cg.resolveType(ty, .indirect);
    263             const ptr_ty_id = try cg.module.ptrType(ty_id, storage_class);
    264 
    265             try cg.module.sections.globals.emit(gpa, .OpVariable, .{
    266                 .id_result_type = ptr_ty_id,
    267                 .id_result = result_id,
    268                 .storage_class = storage_class,
    269             });
    270 
    271             switch (target.os.tag) {
    272                 .vulkan, .opengl => {
    273                     if (ty.zigTypeTag(zcu) == .@"struct") {
    274                         switch (storage_class) {
    275                             .uniform, .push_constant => try cg.module.decorate(ty_id, .block),
    276                             else => {},
    277                         }
    278                     }
    279 
    280                     try cg.module.decorate(ptr_ty_id, .{
    281                         .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) },
    282                     });
    283 
    284                     if (key.decoration) |decoration| switch (decoration) {
    285                         .location => |location| {
    286                             if (storage_class != .output and storage_class != .input and storage_class != .uniform_constant) {
    287                                 return cg.fail("storage class must be one of (output, input, uniform_constant) but is {s}", .{@tagName(storage_class)});
    288                             }
    289                             try cg.module.decorate(result_id, .{
    290                                 .location = .{ .location = location },
    291                             });
    292                         },
    293                         .descriptor => |descriptor| {
    294                             if (storage_class != .storage_buffer and storage_class != .uniform and storage_class != .uniform_constant) {
    295                                 return cg.fail("storage class must be one of (storage_buffer, uniform, uniform_constant) but is {s}", .{@tagName(storage_class)});
    296                             }
    297                             try cg.module.decorate(result_id, .{
    298                                 .binding = .{ .binding_point = descriptor.binding },
    299                             });
    300 
    301                             try cg.module.decorate(result_id, .{
    302                                 .descriptor_set = .{ .descriptor_set = descriptor.set },
    303                             });
    304                         },
    305                     };
    306                 },
    307                 else => {},
    308             }
    309 
    310             if (std.meta.stringToEnum(spec.BuiltIn, nav.fqn.toSlice(ip))) |builtin| {
    311                 try cg.module.decorate(result_id, .{ .built_in = .{ .built_in = builtin } });
    312             }
    313 
    314             try cg.module.debugName(result_id, nav.fqn.toSlice(ip));
    315         },
    316         .invocation_global => {
    317             const maybe_init_val: ?Value = switch (ip.indexToKey(val.toIntern())) {
    318                 .func => unreachable,
    319                 .variable => |variable| .fromInterned(variable.init),
    320                 .@"extern" => null,
    321                 else => val,
    322             };
    323 
    324             const ty_id = try cg.resolveType(ty, .indirect);
    325             const ptr_ty_id = try cg.module.ptrType(ty_id, .function);
    326 
    327             if (maybe_init_val) |init_val| {
    328                 // TODO: Combine with resolveAnonDecl?
    329                 const void_ty_id = try cg.resolveType(.void, .direct);
    330                 const initializer_proto_ty_id = try cg.module.functionType(void_ty_id, &.{});
    331 
    332                 const initializer_id = cg.module.allocId();
    333                 try cg.prologue.emit(gpa, .OpFunction, .{
    334                     .id_result_type = try cg.resolveType(.void, .direct),
    335                     .id_result = initializer_id,
    336                     .function_control = .{},
    337                     .function_type = initializer_proto_ty_id,
    338                 });
    339 
    340                 const root_block_id = cg.module.allocId();
    341                 try cg.prologue.emit(gpa, .OpLabel, .{
    342                     .id_result = root_block_id,
    343                 });
    344                 cg.block_label = root_block_id;
    345 
    346                 const val_id = try cg.constant(ty, init_val, .indirect);
    347                 try cg.body.emit(gpa, .OpStore, .{
    348                     .pointer = result_id,
    349                     .object = val_id,
    350                 });
    351 
    352                 try cg.body.emit(gpa, .OpReturn, {});
    353                 try cg.body.emit(gpa, .OpFunctionEnd, {});
    354                 try cg.module.sections.functions.append(gpa, cg.prologue);
    355                 try cg.module.sections.functions.append(gpa, cg.body);
    356 
    357                 try cg.module.debugNameFmt(initializer_id, "initializer of {f}", .{nav.fqn.fmt(ip)});
    358 
    359                 try cg.module.sections.globals.emit(gpa, .OpExtInst, .{
    360                     .id_result_type = ptr_ty_id,
    361                     .id_result = result_id,
    362                     .set = try cg.module.importInstructionSet(.zig),
    363                     .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) },
    364                     .id_ref_4 = &.{initializer_id},
    365                 });
    366             } else {
    367                 try cg.module.sections.globals.emit(gpa, .OpExtInst, .{
    368                     .id_result_type = ptr_ty_id,
    369                     .id_result = result_id,
    370                     .set = try cg.module.importInstructionSet(.zig),
    371                     .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) },
    372                     .id_ref_4 = &.{},
    373                 });
    374             }
    375         },
    376     }
    377 
    378     cg.module.declPtr(spv_decl_index).end_dep = cg.module.decl_deps.items.len;
    379 }
    380 
    381 pub fn fail(cg: *CodeGen, comptime format: []const u8, args: anytype) Error {
    382     @branchHint(.cold);
    383     const zcu = cg.module.zcu;
    384     const src_loc = zcu.navSrcLoc(cg.owner_nav);
    385     assert(cg.error_msg == null);
    386     cg.error_msg = try Zcu.ErrorMsg.create(zcu.gpa, src_loc, format, args);
    387     return error.CodegenFail;
    388 }
    389 
    390 pub fn todo(cg: *CodeGen, comptime format: []const u8, args: anytype) Error {
    391     return cg.fail("TODO (SPIR-V): " ++ format, args);
    392 }
    393 
    394 /// This imports the "default" extended instruction set for the target
    395 /// For OpenCL, OpenCL.std.100. For Vulkan and OpenGL, GLSL.std.450.
    396 fn importExtendedSet(cg: *CodeGen) !Id {
    397     const target = cg.module.zcu.getTarget();
    398     return switch (target.os.tag) {
    399         .opencl, .amdhsa => try cg.module.importInstructionSet(.@"OpenCL.std"),
    400         .vulkan, .opengl => try cg.module.importInstructionSet(.@"GLSL.std.450"),
    401         else => unreachable,
    402     };
    403 }
    404 
    405 /// Fetch the result-id for a previously generated instruction or constant.
    406 fn resolve(cg: *CodeGen, inst: Air.Inst.Ref) !Id {
    407     const pt = cg.pt;
    408     const zcu = cg.module.zcu;
    409     const ip = &zcu.intern_pool;
    410     if (try cg.air.value(inst, pt)) |val| {
    411         const ty = cg.typeOf(inst);
    412         if (ty.zigTypeTag(zcu) == .@"fn") {
    413             const fn_nav = switch (zcu.intern_pool.indexToKey(val.ip_index)) {
    414                 .@"extern" => |@"extern"| @"extern".owner_nav,
    415                 .func => |func| func.owner_nav,
    416                 else => unreachable,
    417             };
    418             const spv_decl_index = try cg.module.resolveNav(ip, fn_nav);
    419             try cg.module.decl_deps.append(cg.module.gpa, spv_decl_index);
    420             return cg.module.declPtr(spv_decl_index).result_id;
    421         }
    422 
    423         return try cg.constant(ty, val, .direct);
    424     }
    425     const index = inst.toIndex().?;
    426     return cg.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
    427 }
    428 
    429 fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id {
    430     const gpa = cg.module.gpa;
    431 
    432     // TODO: This cannot be a function at this point, but it should probably be handled anyway.
    433 
    434     const zcu = cg.module.zcu;
    435     const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
    436     const ty_id = try cg.resolveType(ty, .indirect);
    437 
    438     const spv_decl_index = blk: {
    439         const entry = try cg.module.uav_link.getOrPut(gpa, .{ val, .function });
    440         if (entry.found_existing) {
    441             try cg.addFunctionDep(entry.value_ptr.*, .function);
    442             return cg.module.declPtr(entry.value_ptr.*).result_id;
    443         }
    444 
    445         const spv_decl_index = try cg.module.allocDecl(.invocation_global);
    446         try cg.addFunctionDep(spv_decl_index, .function);
    447         entry.value_ptr.* = spv_decl_index;
    448         break :blk spv_decl_index;
    449     };
    450 
    451     // TODO: At some point we will be able to generate this all constant here, but then all of
    452     //   constant() will need to be implemented such that it doesn't generate any at-runtime code.
    453     // NOTE: Because this is a global, we really only want to initialize it once. Therefore the
    454     //   constant lowering of this value will need to be deferred to an initializer similar to
    455     //   other globals.
    456 
    457     const result_id = cg.module.declPtr(spv_decl_index).result_id;
    458 
    459     {
    460         // Save the current state so that we can temporarily generate into a different function.
    461         // TODO: This should probably be made a little more robust.
    462         const func_prologue = cg.prologue;
    463         const func_body = cg.body;
    464         const block_label = cg.block_label;
    465         defer {
    466             cg.prologue = func_prologue;
    467             cg.body = func_body;
    468             cg.block_label = block_label;
    469         }
    470 
    471         cg.prologue = .{};
    472         cg.body = .{};
    473         defer {
    474             cg.prologue.deinit(gpa);
    475             cg.body.deinit(gpa);
    476         }
    477 
    478         const void_ty_id = try cg.resolveType(.void, .direct);
    479         const initializer_proto_ty_id = try cg.module.functionType(void_ty_id, &.{});
    480 
    481         const initializer_id = cg.module.allocId();
    482         try cg.prologue.emit(gpa, .OpFunction, .{
    483             .id_result_type = try cg.resolveType(.void, .direct),
    484             .id_result = initializer_id,
    485             .function_control = .{},
    486             .function_type = initializer_proto_ty_id,
    487         });
    488         const root_block_id = cg.module.allocId();
    489         try cg.prologue.emit(gpa, .OpLabel, .{
    490             .id_result = root_block_id,
    491         });
    492         cg.block_label = root_block_id;
    493 
    494         const val_id = try cg.constant(ty, .fromInterned(val), .indirect);
    495         try cg.body.emit(gpa, .OpStore, .{
    496             .pointer = result_id,
    497             .object = val_id,
    498         });
    499 
    500         try cg.body.emit(gpa, .OpReturn, {});
    501         try cg.body.emit(gpa, .OpFunctionEnd, {});
    502 
    503         try cg.module.sections.functions.append(gpa, cg.prologue);
    504         try cg.module.sections.functions.append(gpa, cg.body);
    505 
    506         try cg.module.debugNameFmt(initializer_id, "initializer of __anon_{d}", .{@intFromEnum(val)});
    507 
    508         const fn_decl_ptr_ty_id = try cg.module.ptrType(ty_id, .function);
    509         try cg.module.sections.globals.emit(gpa, .OpExtInst, .{
    510             .id_result_type = fn_decl_ptr_ty_id,
    511             .id_result = result_id,
    512             .set = try cg.module.importInstructionSet(.zig),
    513             .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) },
    514             .id_ref_4 = &.{initializer_id},
    515         });
    516     }
    517 
    518     return result_id;
    519 }
    520 
    521 fn addFunctionDep(cg: *CodeGen, decl_index: Module.Decl.Index, storage_class: StorageClass) !void {
    522     const gpa = cg.module.gpa;
    523     const target = cg.module.zcu.getTarget();
    524     if (target.cpu.has(.spirv, .v1_4)) {
    525         try cg.module.decl_deps.append(gpa, decl_index);
    526     } else {
    527         // Before version 1.4, the interface’s storage classes are limited to the Input and Output
    528         if (storage_class == .input or storage_class == .output) {
    529             try cg.module.decl_deps.append(gpa, decl_index);
    530         }
    531     }
    532 }
    533 
    534 /// Start a new SPIR-V block, Emits the label of the new block, and stores which
    535 /// block we are currently generating.
    536 /// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to
    537 /// keep track of the previous block.
    538 fn beginSpvBlock(cg: *CodeGen, label: Id) !void {
    539     try cg.body.emit(cg.module.gpa, .OpLabel, .{ .id_result = label });
    540     cg.block_label = label;
    541 }
    542 
    543 /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if
    544 /// the Int64 capability is enabled).
    545 /// Note: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).
    546 /// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there
    547 /// is no way of knowing whether those are actually supported.
    548 /// TODO: Maybe this should be cached?
    549 fn largestSupportedIntBits(cg: *CodeGen) u16 {
    550     const target = cg.module.zcu.getTarget();
    551     if (target.cpu.has(.spirv, .int64) or target.cpu.arch == .spirv64) {
    552         return 64;
    553     }
    554     return 32;
    555 }
    556 
    557 const ArithmeticTypeInfo = struct {
    558     const Class = enum {
    559         bool,
    560         /// A regular, **native**, integer.
    561         /// This is only returned when the backend supports this int as a native type (when
    562         /// the relevant capability is enabled).
    563         integer,
    564         /// A regular float. These are all required to be natively supported. Floating points
    565         /// for which the relevant capability is not enabled are not emulated.
    566         float,
    567         /// An integer of a 'strange' size (which' bit size is not the same as its backing
    568         /// type. **Note**: this may **also** include power-of-2 integers for which the
    569         /// relevant capability is not enabled), but still within the limits of the largest
    570         /// natively supported integer type.
    571         strange_integer,
    572         /// An integer with more bits than the largest natively supported integer type.
    573         composite_integer,
    574     };
    575 
    576     /// A classification of the inner type.
    577     /// These scenarios will all have to be handled slightly different.
    578     class: Class,
    579     /// The number of bits in the inner type.
    580     /// This is the actual number of bits of the type, not the size of the backing integer.
    581     bits: u16,
    582     /// The number of bits required to store the type.
    583     /// For `integer` and `float`, this is equal to `bits`.
    584     /// For `strange_integer` and `bool` this is the size of the backing integer.
    585     /// For `composite_integer` this is the elements count.
    586     backing_bits: u16,
    587     /// Null if this type is a scalar, or the length of the vector otherwise.
    588     vector_len: ?u32,
    589     /// Whether the inner type is signed. Only relevant for integers.
    590     signedness: std.builtin.Signedness,
    591 };
    592 
    593 fn arithmeticTypeInfo(cg: *CodeGen, ty: Type) ArithmeticTypeInfo {
    594     const zcu = cg.module.zcu;
    595     const target = cg.module.zcu.getTarget();
    596     var scalar_ty = ty.scalarType(zcu);
    597     if (scalar_ty.zigTypeTag(zcu) == .@"enum") {
    598         scalar_ty = scalar_ty.intTagType(zcu);
    599     }
    600     const vector_len = if (ty.isVector(zcu)) ty.vectorLen(zcu) else null;
    601     return switch (scalar_ty.zigTypeTag(zcu)) {
    602         .bool => .{
    603             .bits = 1, // Doesn't matter for this class.
    604             .backing_bits = cg.module.backingIntBits(1).@"0",
    605             .vector_len = vector_len,
    606             .signedness = .unsigned, // Technically, but doesn't matter for this class.
    607             .class = .bool,
    608         },
    609         .float => .{
    610             .bits = scalar_ty.floatBits(target),
    611             .backing_bits = scalar_ty.floatBits(target), // TODO: F80?
    612             .vector_len = vector_len,
    613             .signedness = .signed, // Technically, but doesn't matter for this class.
    614             .class = .float,
    615         },
    616         .int => blk: {
    617             const int_info = scalar_ty.intInfo(zcu);
    618             // TODO: Maybe it's useful to also return this value.
    619             const backing_bits, const big_int = cg.module.backingIntBits(int_info.bits);
    620             break :blk .{
    621                 .bits = int_info.bits,
    622                 .backing_bits = backing_bits,
    623                 .vector_len = vector_len,
    624                 .signedness = int_info.signedness,
    625                 .class = class: {
    626                     if (big_int) break :class .composite_integer;
    627                     break :class if (backing_bits == int_info.bits) .integer else .strange_integer;
    628                 },
    629             };
    630         },
    631         .@"enum" => unreachable,
    632         .vector => unreachable,
    633         else => unreachable, // Unhandled arithmetic type
    634     };
    635 }
    636 
    637 /// Checks whether the type can be directly translated to SPIR-V vectors
    638 fn isSpvVector(cg: *CodeGen, ty: Type) bool {
    639     const zcu = cg.module.zcu;
    640     const target = cg.module.zcu.getTarget();
    641     if (ty.zigTypeTag(zcu) != .vector) return false;
    642 
    643     // TODO: This check must be expanded for types that can be represented
    644     // as integers (enums / packed structs?) and types that are represented
    645     // by multiple SPIR-V values.
    646     const scalar_ty = ty.scalarType(zcu);
    647     switch (scalar_ty.zigTypeTag(zcu)) {
    648         .bool,
    649         .int,
    650         .float,
    651         => {},
    652         else => return false,
    653     }
    654 
    655     const elem_ty = ty.childType(zcu);
    656     const len = ty.vectorLen(zcu);
    657 
    658     if (elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type) {
    659         if (len > 1 and len <= 4) return true;
    660         if (target.cpu.has(.spirv, .vector16)) return (len == 8 or len == 16);
    661     }
    662 
    663     return false;
    664 }
    665 
    666 /// Emits a bool constant in a particular representation.
    667 fn constBool(cg: *CodeGen, value: bool, repr: Repr) !Id {
    668     return switch (repr) {
    669         .indirect => cg.constInt(.u1, @intFromBool(value)),
    670         .direct => cg.module.constBool(value),
    671     };
    672 }
    673 
    674 /// Emits an integer constant.
    675 /// This function, unlike Module.constInt, takes care to bitcast
    676 /// the value to an unsigned int first for Kernels.
    677 fn constInt(cg: *CodeGen, ty: Type, value: anytype) !Id {
    678     const zcu = cg.module.zcu;
    679     const target = cg.module.zcu.getTarget();
    680     const scalar_ty = ty.scalarType(zcu);
    681     const int_info = scalar_ty.intInfo(zcu);
    682     // Use backing bits so that negatives are sign extended
    683     const backing_bits, const big_int = cg.module.backingIntBits(int_info.bits);
    684     assert(backing_bits != 0); // u0 is comptime
    685 
    686     const result_ty_id = try cg.resolveType(scalar_ty, .indirect);
    687     const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) {
    688         .int => |int| int.signedness,
    689         .comptime_int => if (value < 0) .signed else .unsigned,
    690         else => unreachable,
    691     };
    692     if (@sizeOf(@TypeOf(value)) >= 4 and big_int) {
    693         const value64: u64 = switch (signedness) {
    694             .signed => @bitCast(@as(i64, @intCast(value))),
    695             .unsigned => @as(u64, @intCast(value)),
    696         };
    697         assert(backing_bits == 64);
    698         return cg.constructComposite(result_ty_id, &.{
    699             try cg.constInt(.u32, @as(u32, @truncate(value64))),
    700             try cg.constInt(.u32, @as(u32, @truncate(value64 << 32))),
    701         });
    702     }
    703 
    704     const final_value: spec.LiteralContextDependentNumber = switch (target.os.tag) {
    705         .opencl, .amdhsa => blk: {
    706             const value64: u64 = switch (signedness) {
    707                 .signed => @bitCast(@as(i64, @intCast(value))),
    708                 .unsigned => @as(u64, @intCast(value)),
    709             };
    710 
    711             // Manually truncate the value to the right amount of bits.
    712             const truncated_value = if (backing_bits == 64)
    713                 value64
    714             else
    715                 value64 & (@as(u64, 1) << @intCast(backing_bits)) - 1;
    716 
    717             break :blk switch (backing_bits) {
    718                 1...32 => .{ .uint32 = @truncate(truncated_value) },
    719                 33...64 => .{ .uint64 = truncated_value },
    720                 else => unreachable,
    721             };
    722         },
    723         else => switch (backing_bits) {
    724             1...32 => if (signedness == .signed) .{ .int32 = @intCast(value) } else .{ .uint32 = @intCast(value) },
    725             33...64 => if (signedness == .signed) .{ .int64 = value } else .{ .uint64 = value },
    726             else => unreachable,
    727         },
    728     };
    729 
    730     const result_id = try cg.module.constant(result_ty_id, final_value);
    731 
    732     if (!ty.isVector(zcu)) return result_id;
    733     return cg.constructCompositeSplat(ty, result_id);
    734 }
    735 
    736 pub fn constructComposite(cg: *CodeGen, result_ty_id: Id, constituents: []const Id) !Id {
    737     const gpa = cg.module.gpa;
    738     const result_id = cg.module.allocId();
    739     try cg.body.emit(gpa, .OpCompositeConstruct, .{
    740         .id_result_type = result_ty_id,
    741         .id_result = result_id,
    742         .constituents = constituents,
    743     });
    744     return result_id;
    745 }
    746 
    747 /// Construct a composite at runtime with all lanes set to the same value.
    748 /// ty must be an aggregate type.
    749 fn constructCompositeSplat(cg: *CodeGen, ty: Type, constituent: Id) !Id {
    750     const gpa = cg.module.gpa;
    751     const zcu = cg.module.zcu;
    752     const n: usize = @intCast(ty.arrayLen(zcu));
    753 
    754     const scratch_top = cg.id_scratch.items.len;
    755     defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
    756 
    757     const constituents = try cg.id_scratch.addManyAsSlice(gpa, n);
    758     @memset(constituents, constituent);
    759 
    760     const result_ty_id = try cg.resolveType(ty, .direct);
    761     return cg.constructComposite(result_ty_id, constituents);
    762 }
    763 
    764 /// This function generates a load for a constant in direct (ie, non-memory) representation.
    765 /// When the constant is simple, it can be generated directly using OpConstant instructions.
    766 /// When the constant is more complicated however, it needs to be constructed using multiple values. This
    767 /// is done by emitting a sequence of instructions that initialize the value.
    768 //
    769 /// This function should only be called during function code generation.
    770 fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
    771     const gpa = cg.module.gpa;
    772 
    773     // Note: Using intern_map can only be used with constants that DO NOT generate any runtime code!!
    774     // Ideally that should be all constants in the future, or it should be cleaned up somehow. For
    775     // now, only use the intern_map on case-by-case basis by breaking to :cache.
    776     if (cg.module.intern_map.get(.{ val.toIntern(), repr })) |id| {
    777         return id;
    778     }
    779 
    780     const pt = cg.pt;
    781     const zcu = cg.module.zcu;
    782     const target = cg.module.zcu.getTarget();
    783     const result_ty_id = try cg.resolveType(ty, repr);
    784     const ip = &zcu.intern_pool;
    785 
    786     log.debug("lowering constant: ty = {f}, val = {f}, key = {s}", .{ ty.fmt(pt), val.fmtValue(pt), @tagName(ip.indexToKey(val.toIntern())) });
    787     if (val.isUndef(zcu)) {
    788         return cg.module.constUndef(result_ty_id);
    789     }
    790 
    791     const cacheable_id = cache: {
    792         switch (ip.indexToKey(val.toIntern())) {
    793             .int_type,
    794             .ptr_type,
    795             .array_type,
    796             .vector_type,
    797             .opt_type,
    798             .anyframe_type,
    799             .error_union_type,
    800             .simple_type,
    801             .struct_type,
    802             .tuple_type,
    803             .union_type,
    804             .opaque_type,
    805             .enum_type,
    806             .func_type,
    807             .error_set_type,
    808             .inferred_error_set_type,
    809             => unreachable, // types, not values
    810 
    811             .undef => unreachable, // handled above
    812 
    813             .variable,
    814             .@"extern",
    815             .func,
    816             .enum_literal,
    817             .empty_enum_value,
    818             => unreachable, // non-runtime values
    819 
    820             .simple_value => |simple_value| switch (simple_value) {
    821                 .undefined,
    822                 .void,
    823                 .null,
    824                 .empty_tuple,
    825                 .@"unreachable",
    826                 => unreachable, // non-runtime values
    827 
    828                 .false, .true => break :cache try cg.constBool(val.toBool(), repr),
    829             },
    830             .int => {
    831                 if (ty.isSignedInt(zcu)) {
    832                     break :cache try cg.constInt(ty, val.toSignedInt(zcu));
    833                 } else {
    834                     break :cache try cg.constInt(ty, val.toUnsignedInt(zcu));
    835                 }
    836             },
    837             .float => {
    838                 const lit: spec.LiteralContextDependentNumber = switch (ty.floatBits(target)) {
    839                     16 => .{ .uint32 = @as(u16, @bitCast(val.toFloat(f16, zcu))) },
    840                     32 => .{ .float32 = val.toFloat(f32, zcu) },
    841                     64 => .{ .float64 = val.toFloat(f64, zcu) },
    842                     80, 128 => unreachable, // TODO
    843                     else => unreachable,
    844                 };
    845                 break :cache try cg.module.constant(result_ty_id, lit);
    846             },
    847             .err => |err| {
    848                 const value = try pt.getErrorValue(err.name);
    849                 break :cache try cg.constInt(ty, value);
    850             },
    851             .error_union => |error_union| {
    852                 // TODO: Error unions may be constructed with constant instructions if the payload type
    853                 // allows it. For now, just generate it here regardless.
    854                 const err_ty = ty.errorUnionSet(zcu);
    855                 const payload_ty = ty.errorUnionPayload(zcu);
    856                 const err_val_id = switch (error_union.val) {
    857                     .err_name => |err_name| try cg.constInt(
    858                         err_ty,
    859                         try pt.getErrorValue(err_name),
    860                     ),
    861                     .payload => try cg.constInt(err_ty, 0),
    862                 };
    863                 const eu_layout = cg.errorUnionLayout(payload_ty);
    864                 if (!eu_layout.payload_has_bits) {
    865                     // We use the error type directly as the type.
    866                     break :cache err_val_id;
    867                 }
    868 
    869                 const payload_val_id = switch (error_union.val) {
    870                     .err_name => try cg.constant(payload_ty, .undef, .indirect),
    871                     .payload => |p| try cg.constant(payload_ty, .fromInterned(p), .indirect),
    872                 };
    873 
    874                 var constituents: [2]Id = undefined;
    875                 var types: [2]Type = undefined;
    876                 if (eu_layout.error_first) {
    877                     constituents[0] = err_val_id;
    878                     constituents[1] = payload_val_id;
    879                     types = .{ err_ty, payload_ty };
    880                 } else {
    881                     constituents[0] = payload_val_id;
    882                     constituents[1] = err_val_id;
    883                     types = .{ payload_ty, err_ty };
    884                 }
    885 
    886                 const comp_ty_id = try cg.resolveType(ty, .direct);
    887                 return try cg.constructComposite(comp_ty_id, &constituents);
    888             },
    889             .enum_tag => {
    890                 const int_val = try val.intFromEnum(ty, pt);
    891                 const int_ty = ty.intTagType(zcu);
    892                 break :cache try cg.constant(int_ty, int_val, repr);
    893             },
    894             .ptr => return cg.constantPtr(val),
    895             .slice => |slice| {
    896                 const ptr_id = try cg.constantPtr(.fromInterned(slice.ptr));
    897                 const len_id = try cg.constant(.usize, .fromInterned(slice.len), .indirect);
    898                 const comp_ty_id = try cg.resolveType(ty, .direct);
    899                 return try cg.constructComposite(comp_ty_id, &.{ ptr_id, len_id });
    900             },
    901             .opt => {
    902                 const payload_ty = ty.optionalChild(zcu);
    903                 const maybe_payload_val = val.optionalValue(zcu);
    904 
    905                 if (!payload_ty.hasRuntimeBits(zcu)) {
    906                     break :cache try cg.constBool(maybe_payload_val != null, .indirect);
    907                 } else if (ty.optionalReprIsPayload(zcu)) {
    908                     // Optional representation is a nullable pointer or slice.
    909                     if (maybe_payload_val) |payload_val| {
    910                         return try cg.constant(payload_ty, payload_val, .indirect);
    911                     } else {
    912                         break :cache try cg.module.constNull(result_ty_id);
    913                     }
    914                 }
    915 
    916                 // Optional representation is a structure.
    917                 // { Payload, Bool }
    918 
    919                 const has_pl_id = try cg.constBool(maybe_payload_val != null, .indirect);
    920                 const payload_id = if (maybe_payload_val) |payload_val|
    921                     try cg.constant(payload_ty, payload_val, .indirect)
    922                 else
    923                     try cg.module.constUndef(try cg.resolveType(payload_ty, .indirect));
    924 
    925                 const comp_ty_id = try cg.resolveType(ty, .direct);
    926                 return try cg.constructComposite(comp_ty_id, &.{ payload_id, has_pl_id });
    927             },
    928             .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
    929                 inline .array_type, .vector_type => |array_type, tag| {
    930                     const elem_ty: Type = .fromInterned(array_type.child);
    931 
    932                     const scratch_top = cg.id_scratch.items.len;
    933                     defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
    934                     const constituents = try cg.id_scratch.addManyAsSlice(gpa, @intCast(ty.arrayLenIncludingSentinel(zcu)));
    935 
    936                     const child_repr: Repr = switch (tag) {
    937                         .array_type => .indirect,
    938                         .vector_type => .direct,
    939                         else => unreachable,
    940                     };
    941 
    942                     switch (aggregate.storage) {
    943                         .bytes => |bytes| {
    944                             // TODO: This is really space inefficient, perhaps there is a better
    945                             // way to do it?
    946                             for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| {
    947                                 constituent.* = try cg.constInt(elem_ty, byte);
    948                             }
    949                         },
    950                         .elems => |elems| {
    951                             for (constituents, elems) |*constituent, elem| {
    952                                 constituent.* = try cg.constant(elem_ty, .fromInterned(elem), child_repr);
    953                             }
    954                         },
    955                         .repeated_elem => |elem| {
    956                             @memset(constituents, try cg.constant(elem_ty, .fromInterned(elem), child_repr));
    957                         },
    958                     }
    959 
    960                     const comp_ty_id = try cg.resolveType(ty, .direct);
    961                     return cg.constructComposite(comp_ty_id, constituents);
    962                 },
    963                 .struct_type => {
    964                     const struct_type = zcu.typeToStruct(ty).?;
    965 
    966                     if (struct_type.layout == .@"packed") {
    967                         // TODO: composite int
    968                         // TODO: endianness
    969                         const bits: u16 = @intCast(ty.bitSize(zcu));
    970                         const bytes = std.mem.alignForward(u16, cg.module.backingIntBits(bits).@"0", 8) / 8;
    971                         var limbs: [8]u8 = undefined;
    972                         @memset(&limbs, 0);
    973                         val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable;
    974                         const backing_ty: Type = .fromInterned(struct_type.backingIntTypeUnordered(ip));
    975                         return try cg.constInt(backing_ty, @as(u64, @bitCast(limbs)));
    976                     }
    977 
    978                     var types = std.array_list.Managed(Type).init(gpa);
    979                     defer types.deinit();
    980 
    981                     var constituents = std.array_list.Managed(Id).init(gpa);
    982                     defer constituents.deinit();
    983 
    984                     var it = struct_type.iterateRuntimeOrder(ip);
    985                     while (it.next()) |field_index| {
    986                         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
    987                         if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
    988                             // This is a zero-bit field - we only needed it for the alignment.
    989                             continue;
    990                         }
    991 
    992                         // TODO: Padding?
    993                         const field_val = try val.fieldValue(pt, field_index);
    994                         const field_id = try cg.constant(field_ty, field_val, .indirect);
    995 
    996                         try types.append(field_ty);
    997                         try constituents.append(field_id);
    998                     }
    999 
   1000                     const comp_ty_id = try cg.resolveType(ty, .direct);
   1001                     return try cg.constructComposite(comp_ty_id, constituents.items);
   1002                 },
   1003                 .tuple_type => return cg.todo("implement tuple types", .{}),
   1004                 else => unreachable,
   1005             },
   1006             .un => |un| {
   1007                 if (un.tag == .none) {
   1008                     assert(ty.containerLayout(zcu) == .@"packed"); // TODO
   1009                     const int_ty = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu)));
   1010                     return try cg.constInt(int_ty, Value.toUnsignedInt(.fromInterned(un.val), zcu));
   1011                 }
   1012                 const active_field = ty.unionTagFieldIndex(.fromInterned(un.tag), zcu).?;
   1013                 const union_obj = zcu.typeToUnion(ty).?;
   1014                 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[active_field]);
   1015                 const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(zcu))
   1016                     try cg.constant(field_ty, .fromInterned(un.val), .direct)
   1017                 else
   1018                     null;
   1019                 return try cg.unionInit(ty, active_field, payload);
   1020             },
   1021             .memoized_call => unreachable,
   1022         }
   1023     };
   1024 
   1025     try cg.module.intern_map.putNoClobber(gpa, .{ val.toIntern(), repr }, cacheable_id);
   1026 
   1027     return cacheable_id;
   1028 }
   1029 
   1030 fn constantPtr(cg: *CodeGen, ptr_val: Value) !Id {
   1031     const pt = cg.pt;
   1032     const zcu = cg.module.zcu;
   1033     const gpa = cg.module.gpa;
   1034 
   1035     if (ptr_val.isUndef(zcu)) {
   1036         const result_ty = ptr_val.typeOf(zcu);
   1037         const result_ty_id = try cg.resolveType(result_ty, .direct);
   1038         return cg.module.constUndef(result_ty_id);
   1039     }
   1040 
   1041     var arena = std.heap.ArenaAllocator.init(gpa);
   1042     defer arena.deinit();
   1043 
   1044     const derivation = try ptr_val.pointerDerivation(arena.allocator(), pt);
   1045     return cg.derivePtr(derivation);
   1046 }
   1047 
   1048 fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {
   1049     const gpa = cg.module.gpa;
   1050     const pt = cg.pt;
   1051     const zcu = cg.module.zcu;
   1052     const target = zcu.getTarget();
   1053     switch (derivation) {
   1054         .comptime_alloc_ptr, .comptime_field_ptr => unreachable,
   1055         .int => |int| {
   1056             if (target.os.tag != .opencl) {
   1057                 if (int.ptr_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) {
   1058                     return cg.fail(
   1059                         "cannot cast integer to pointer with address space '{s}'",
   1060                         .{@tagName(int.ptr_ty.ptrAddressSpace(zcu))},
   1061                     );
   1062                 }
   1063             }
   1064             const result_ty_id = try cg.resolveType(int.ptr_ty, .direct);
   1065             // TODO: This can probably be an OpSpecConstantOp Bitcast, but
   1066             // that is not implemented by Mesa yet. Therefore, just generate it
   1067             // as a runtime operation.
   1068             const result_ptr_id = cg.module.allocId();
   1069             const value_id = try cg.constInt(.usize, int.addr);
   1070             try cg.body.emit(gpa, .OpConvertUToPtr, .{
   1071                 .id_result_type = result_ty_id,
   1072                 .id_result = result_ptr_id,
   1073                 .integer_value = value_id,
   1074             });
   1075             return result_ptr_id;
   1076         },
   1077         .nav_ptr => |nav| {
   1078             const result_ptr_ty = try pt.navPtrType(nav);
   1079             return cg.constantNavRef(result_ptr_ty, nav);
   1080         },
   1081         .uav_ptr => |uav| {
   1082             const result_ptr_ty: Type = .fromInterned(uav.orig_ty);
   1083             return cg.constantUavRef(result_ptr_ty, uav);
   1084         },
   1085         .eu_payload_ptr => @panic("TODO"),
   1086         .opt_payload_ptr => @panic("TODO"),
   1087         .field_ptr => |field| {
   1088             const parent_ptr_id = try cg.derivePtr(field.parent.*);
   1089             const parent_ptr_ty = try field.parent.ptrType(pt);
   1090             return cg.structFieldPtr(field.result_ptr_ty, parent_ptr_ty, parent_ptr_id, field.field_idx);
   1091         },
   1092         .elem_ptr => |elem| {
   1093             const parent_ptr_id = try cg.derivePtr(elem.parent.*);
   1094             const parent_ptr_ty = try elem.parent.ptrType(pt);
   1095             const index_id = try cg.constInt(.usize, elem.elem_idx);
   1096             return cg.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id);
   1097         },
   1098         .offset_and_cast => |oac| {
   1099             const parent_ptr_id = try cg.derivePtr(oac.parent.*);
   1100             const parent_ptr_ty = try oac.parent.ptrType(pt);
   1101             const result_ty_id = try cg.resolveType(oac.new_ptr_ty, .direct);
   1102             const child_size = oac.new_ptr_ty.childType(zcu).abiSize(zcu);
   1103 
   1104             if (parent_ptr_ty.childType(zcu).isVector(zcu) and oac.byte_offset % child_size == 0) {
   1105                 // Vector element ptr accesses are derived as offset_and_cast.
   1106                 // We can just use OpAccessChain.
   1107                 return cg.accessChain(
   1108                     result_ty_id,
   1109                     parent_ptr_id,
   1110                     &.{@intCast(@divExact(oac.byte_offset, child_size))},
   1111                 );
   1112             }
   1113 
   1114             if (oac.byte_offset == 0) {
   1115                 // Allow changing the pointer type child only to restructure arrays.
   1116                 // e.g. [3][2]T to T is fine, as is [2]T -> [2][1]T.
   1117                 const result_ptr_id = cg.module.allocId();
   1118                 try cg.body.emit(gpa, .OpBitcast, .{
   1119                     .id_result_type = result_ty_id,
   1120                     .id_result = result_ptr_id,
   1121                     .operand = parent_ptr_id,
   1122                 });
   1123                 return result_ptr_id;
   1124             }
   1125 
   1126             return cg.fail("cannot perform pointer cast: '{f}' to '{f}'", .{
   1127                 parent_ptr_ty.fmt(pt),
   1128                 oac.new_ptr_ty.fmt(pt),
   1129             });
   1130         },
   1131     }
   1132 }
   1133 
   1134 fn constantUavRef(
   1135     cg: *CodeGen,
   1136     ty: Type,
   1137     uav: InternPool.Key.Ptr.BaseAddr.Uav,
   1138 ) !Id {
   1139     // TODO: Merge this function with constantDeclRef.
   1140 
   1141     const zcu = cg.module.zcu;
   1142     const ip = &zcu.intern_pool;
   1143     const ty_id = try cg.resolveType(ty, .direct);
   1144     const uav_ty: Type = .fromInterned(ip.typeOf(uav.val));
   1145 
   1146     switch (ip.indexToKey(uav.val)) {
   1147         .func => unreachable, // TODO
   1148         .@"extern" => assert(!ip.isFunctionType(uav_ty.toIntern())),
   1149         else => {},
   1150     }
   1151 
   1152     // const is_fn_body = decl_ty.zigTypeTag(zcu) == .@"fn";
   1153     if (!uav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
   1154         // Pointer to nothing - return undefined
   1155         return cg.module.constUndef(ty_id);
   1156     }
   1157 
   1158     // Uav refs are always generic.
   1159     assert(ty.ptrAddressSpace(zcu) == .generic);
   1160     const uav_ty_id = try cg.resolveType(uav_ty, .indirect);
   1161     const decl_ptr_ty_id = try cg.module.ptrType(uav_ty_id, .function);
   1162     const ptr_id = try cg.resolveUav(uav.val);
   1163 
   1164     if (decl_ptr_ty_id != ty_id) {
   1165         // Differing pointer types, insert a cast.
   1166         const casted_ptr_id = cg.module.allocId();
   1167         try cg.body.emit(cg.module.gpa, .OpBitcast, .{
   1168             .id_result_type = ty_id,
   1169             .id_result = casted_ptr_id,
   1170             .operand = ptr_id,
   1171         });
   1172         return casted_ptr_id;
   1173     } else {
   1174         return ptr_id;
   1175     }
   1176 }
   1177 
   1178 fn constantNavRef(cg: *CodeGen, ty: Type, nav_index: InternPool.Nav.Index) !Id {
   1179     const zcu = cg.module.zcu;
   1180     const ip = &zcu.intern_pool;
   1181     const ty_id = try cg.resolveType(ty, .direct);
   1182     const nav = ip.getNav(nav_index);
   1183     const nav_ty: Type = .fromInterned(nav.typeOf(ip));
   1184 
   1185     switch (nav.status) {
   1186         .unresolved => unreachable,
   1187         .type_resolved => {}, // this is not a function or extern
   1188         .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
   1189             .func => {
   1190                 // TODO: Properly lower function pointers. For now we are going to hack around it and
   1191                 // just generate an empty pointer. Function pointers are represented by a pointer to usize.
   1192                 return try cg.module.constUndef(ty_id);
   1193             },
   1194             .@"extern" => if (ip.isFunctionType(nav_ty.toIntern())) @panic("TODO"),
   1195             else => {},
   1196         },
   1197     }
   1198 
   1199     if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
   1200         // Pointer to nothing - return undefined.
   1201         return cg.module.constUndef(ty_id);
   1202     }
   1203 
   1204     const spv_decl_index = try cg.module.resolveNav(ip, nav_index);
   1205     const spv_decl = cg.module.declPtr(spv_decl_index);
   1206     const spv_decl_result_id = spv_decl.result_id;
   1207     assert(spv_decl.kind != .func);
   1208 
   1209     const storage_class = cg.module.storageClass(nav.getAddrspace());
   1210     try cg.addFunctionDep(spv_decl_index, storage_class);
   1211 
   1212     const nav_ty_id = try cg.resolveType(nav_ty, .indirect);
   1213     const decl_ptr_ty_id = try cg.module.ptrType(nav_ty_id, storage_class);
   1214 
   1215     if (decl_ptr_ty_id != ty_id) {
   1216         // Differing pointer types, insert a cast.
   1217         const casted_ptr_id = cg.module.allocId();
   1218         try cg.body.emit(cg.module.gpa, .OpBitcast, .{
   1219             .id_result_type = ty_id,
   1220             .id_result = casted_ptr_id,
   1221             .operand = spv_decl_result_id,
   1222         });
   1223         return casted_ptr_id;
   1224     }
   1225 
   1226     return spv_decl_result_id;
   1227 }
   1228 
   1229 // Turn a Zig type's name into a cache reference.
   1230 fn resolveTypeName(cg: *CodeGen, ty: Type) ![]const u8 {
   1231     const gpa = cg.module.gpa;
   1232     var aw: std.Io.Writer.Allocating = .init(gpa);
   1233     defer aw.deinit();
   1234     ty.print(&aw.writer, cg.pt, null) catch |err| switch (err) {
   1235         error.WriteFailed => return error.OutOfMemory,
   1236     };
   1237     return try aw.toOwnedSlice();
   1238 }
   1239 
   1240 /// Generate a union type. Union types are always generated with the
   1241 /// most aligned field active. If the tag alignment is greater
   1242 /// than that of the payload, a regular union (non-packed, with both tag and
   1243 /// payload), will be generated as follows:
   1244 ///  struct {
   1245 ///    tag: TagType,
   1246 ///    payload: MostAlignedFieldType,
   1247 ///    payload_padding: [payload_size - @sizeOf(MostAlignedFieldType)]u8,
   1248 ///    padding: [padding_size]u8,
   1249 ///  }
   1250 /// If the payload alignment is greater than that of the tag:
   1251 ///  struct {
   1252 ///    payload: MostAlignedFieldType,
   1253 ///    payload_padding: [payload_size - @sizeOf(MostAlignedFieldType)]u8,
   1254 ///    tag: TagType,
   1255 ///    padding: [padding_size]u8,
   1256 ///  }
   1257 /// If any of the fields' size is 0, it will be omitted.
   1258 fn resolveUnionType(cg: *CodeGen, ty: Type) !Id {
   1259     const gpa = cg.module.gpa;
   1260     const zcu = cg.module.zcu;
   1261     const ip = &zcu.intern_pool;
   1262     const union_obj = zcu.typeToUnion(ty).?;
   1263 
   1264     if (union_obj.flagsUnordered(ip).layout == .@"packed") {
   1265         return try cg.module.intType(.unsigned, @intCast(ty.bitSize(zcu)));
   1266     }
   1267 
   1268     const layout = cg.unionLayout(ty);
   1269     if (!layout.has_payload) {
   1270         // No payload, so represent this as just the tag type.
   1271         return try cg.resolveType(.fromInterned(union_obj.enum_tag_ty), .indirect);
   1272     }
   1273 
   1274     var member_types: [4]Id = undefined;
   1275     var member_names: [4][]const u8 = undefined;
   1276 
   1277     const u8_ty_id = try cg.resolveType(.u8, .direct);
   1278 
   1279     if (layout.tag_size != 0) {
   1280         const tag_ty_id = try cg.resolveType(.fromInterned(union_obj.enum_tag_ty), .indirect);
   1281         member_types[layout.tag_index] = tag_ty_id;
   1282         member_names[layout.tag_index] = "(tag)";
   1283     }
   1284 
   1285     if (layout.payload_size != 0) {
   1286         const payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect);
   1287         member_types[layout.payload_index] = payload_ty_id;
   1288         member_names[layout.payload_index] = "(payload)";
   1289     }
   1290 
   1291     if (layout.payload_padding_size != 0) {
   1292         const len_id = try cg.constInt(.u32, layout.payload_padding_size);
   1293         const payload_padding_ty_id = try cg.module.arrayType(len_id, u8_ty_id);
   1294         member_types[layout.payload_padding_index] = payload_padding_ty_id;
   1295         member_names[layout.payload_padding_index] = "(payload padding)";
   1296     }
   1297 
   1298     if (layout.padding_size != 0) {
   1299         const len_id = try cg.constInt(.u32, layout.padding_size);
   1300         const padding_ty_id = try cg.module.arrayType(len_id, u8_ty_id);
   1301         member_types[layout.padding_index] = padding_ty_id;
   1302         member_names[layout.padding_index] = "(padding)";
   1303     }
   1304 
   1305     const result_id = try cg.module.structType(
   1306         member_types[0..layout.total_fields],
   1307         member_names[0..layout.total_fields],
   1308         null,
   1309         .none,
   1310     );
   1311 
   1312     const type_name = try cg.resolveTypeName(ty);
   1313     defer gpa.free(type_name);
   1314     try cg.module.debugName(result_id, type_name);
   1315 
   1316     return result_id;
   1317 }
   1318 
   1319 fn resolveFnReturnType(cg: *CodeGen, ret_ty: Type) !Id {
   1320     const zcu = cg.module.zcu;
   1321     if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   1322         // If the return type is an error set or an error union, then we make this
   1323         // anyerror return type instead, so that it can be coerced into a function
   1324         // pointer type which has anyerror as the return type.
   1325         if (ret_ty.isError(zcu)) {
   1326             return cg.resolveType(.anyerror, .direct);
   1327         } else {
   1328             return cg.resolveType(.void, .direct);
   1329         }
   1330     }
   1331 
   1332     return try cg.resolveType(ret_ty, .direct);
   1333 }
   1334 
   1335 fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
   1336     const gpa = cg.module.gpa;
   1337     const pt = cg.pt;
   1338     const zcu = cg.module.zcu;
   1339     const ip = &zcu.intern_pool;
   1340     const target = cg.module.zcu.getTarget();
   1341 
   1342     log.debug("resolveType: ty = {f}", .{ty.fmt(pt)});
   1343 
   1344     switch (ty.zigTypeTag(zcu)) {
   1345         .noreturn => {
   1346             assert(repr == .direct);
   1347             return try cg.module.voidType();
   1348         },
   1349         .void => switch (repr) {
   1350             .direct => return try cg.module.voidType(),
   1351             .indirect => {
   1352                 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
   1353                 return try cg.module.opaqueType("void");
   1354             },
   1355         },
   1356         .bool => switch (repr) {
   1357             .direct => return try cg.module.boolType(),
   1358             .indirect => return try cg.resolveType(.u1, .indirect),
   1359         },
   1360         .int => {
   1361             const int_info = ty.intInfo(zcu);
   1362             if (int_info.bits == 0) {
   1363                 assert(repr == .indirect);
   1364                 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
   1365                 return try cg.module.opaqueType("u0");
   1366             }
   1367             return try cg.module.intType(int_info.signedness, int_info.bits);
   1368         },
   1369         .@"enum" => return try cg.resolveType(ty.intTagType(zcu), repr),
   1370         .float => {
   1371             const bits = ty.floatBits(target);
   1372             const supported = switch (bits) {
   1373                 16 => target.cpu.has(.spirv, .float16),
   1374                 32 => true,
   1375                 64 => target.cpu.has(.spirv, .float64),
   1376                 else => false,
   1377             };
   1378 
   1379             if (!supported) {
   1380                 return cg.fail(
   1381                     "floating point width of {} bits is not supported for the current SPIR-V feature set",
   1382                     .{bits},
   1383                 );
   1384             }
   1385 
   1386             return try cg.module.floatType(bits);
   1387         },
   1388         .array => {
   1389             const elem_ty = ty.childType(zcu);
   1390             const elem_ty_id = try cg.resolveType(elem_ty, .indirect);
   1391             const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(zcu)) orelse {
   1392                 return cg.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(zcu)});
   1393             };
   1394 
   1395             if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   1396                 assert(repr == .indirect);
   1397                 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
   1398                 return try cg.module.opaqueType("zero-sized-array");
   1399             } else if (total_len == 0) {
   1400                 // The size of the array would be 0, but that is not allowed in SPIR-V.
   1401                 // This path can be reached for example when there is a slicing of a pointer
   1402                 // that produces a zero-length array. In all cases where this type can be generated,
   1403                 // this should be an indirect path.
   1404                 assert(repr == .indirect);
   1405                 // In this case, we have an array of a non-zero sized type. In this case,
   1406                 // generate an array of 1 element instead, so that ptr_elem_ptr instructions
   1407                 // can be lowered to ptrAccessChain instead of manually performing the math.
   1408                 const len_id = try cg.constInt(.u32, 1);
   1409                 return try cg.module.arrayType(len_id, elem_ty_id);
   1410             } else {
   1411                 const total_len_id = try cg.constInt(.u32, total_len);
   1412                 const result_id = try cg.module.arrayType(total_len_id, elem_ty_id);
   1413                 switch (target.os.tag) {
   1414                     .vulkan, .opengl => {
   1415                         try cg.module.decorate(result_id, .{
   1416                             .array_stride = .{
   1417                                 .array_stride = @intCast(elem_ty.abiSize(zcu)),
   1418                             },
   1419                         });
   1420                     },
   1421                     else => {},
   1422                 }
   1423                 return result_id;
   1424             }
   1425         },
   1426         .vector => {
   1427             const elem_ty = ty.childType(zcu);
   1428             const elem_ty_id = try cg.resolveType(elem_ty, repr);
   1429             const len = ty.vectorLen(zcu);
   1430             if (cg.isSpvVector(ty)) return try cg.module.vectorType(len, elem_ty_id);
   1431             const len_id = try cg.constInt(.u32, len);
   1432             return try cg.module.arrayType(len_id, elem_ty_id);
   1433         },
   1434         .@"fn" => switch (repr) {
   1435             .direct => {
   1436                 const fn_info = zcu.typeToFunc(ty).?;
   1437 
   1438                 comptime assert(zig_call_abi_ver == 3);
   1439                 assert(!fn_info.is_var_args);
   1440                 switch (fn_info.cc) {
   1441                     .auto,
   1442                     .spirv_kernel,
   1443                     .spirv_fragment,
   1444                     .spirv_vertex,
   1445                     .spirv_device,
   1446                     => {},
   1447                     else => unreachable,
   1448                 }
   1449 
   1450                 const return_ty_id = try cg.resolveFnReturnType(.fromInterned(fn_info.return_type));
   1451 
   1452                 const scratch_top = cg.id_scratch.items.len;
   1453                 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   1454                 const param_ty_ids = try cg.id_scratch.addManyAsSlice(gpa, fn_info.param_types.len);
   1455 
   1456                 var param_index: usize = 0;
   1457                 for (fn_info.param_types.get(ip)) |param_ty_index| {
   1458                     const param_ty: Type = .fromInterned(param_ty_index);
   1459                     if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1460 
   1461                     param_ty_ids[param_index] = try cg.resolveType(param_ty, .direct);
   1462                     param_index += 1;
   1463                 }
   1464 
   1465                 return try cg.module.functionType(return_ty_id, param_ty_ids[0..param_index]);
   1466             },
   1467             .indirect => {
   1468                 // TODO: Represent function pointers properly.
   1469                 // For now, just use an usize type.
   1470                 return try cg.resolveType(.usize, .indirect);
   1471             },
   1472         },
   1473         .pointer => {
   1474             const ptr_info = ty.ptrInfo(zcu);
   1475 
   1476             const child_ty: Type = .fromInterned(ptr_info.child);
   1477             const child_ty_id = try cg.resolveType(child_ty, .indirect);
   1478             const storage_class = cg.module.storageClass(ptr_info.flags.address_space);
   1479             const ptr_ty_id = try cg.module.ptrType(child_ty_id, storage_class);
   1480 
   1481             if (ptr_info.flags.size != .slice) {
   1482                 return ptr_ty_id;
   1483             }
   1484 
   1485             const size_ty_id = try cg.resolveType(.usize, .direct);
   1486             return try cg.module.structType(
   1487                 &.{ ptr_ty_id, size_ty_id },
   1488                 &.{ "ptr", "len" },
   1489                 null,
   1490                 .none,
   1491             );
   1492         },
   1493         .@"struct" => {
   1494             const struct_type = switch (ip.indexToKey(ty.toIntern())) {
   1495                 .tuple_type => |tuple| {
   1496                     const scratch_top = cg.id_scratch.items.len;
   1497                     defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   1498                     const member_types = try cg.id_scratch.addManyAsSlice(gpa, tuple.values.len);
   1499 
   1500                     var member_index: usize = 0;
   1501                     for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| {
   1502                         if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue;
   1503 
   1504                         member_types[member_index] = try cg.resolveType(.fromInterned(field_ty), .indirect);
   1505                         member_index += 1;
   1506                     }
   1507 
   1508                     const result_id = try cg.module.structType(
   1509                         member_types[0..member_index],
   1510                         null,
   1511                         null,
   1512                         .none,
   1513                     );
   1514                     const type_name = try cg.resolveTypeName(ty);
   1515                     defer gpa.free(type_name);
   1516                     try cg.module.debugName(result_id, type_name);
   1517                     return result_id;
   1518                 },
   1519                 .struct_type => ip.loadStructType(ty.toIntern()),
   1520                 else => unreachable,
   1521             };
   1522 
   1523             if (struct_type.layout == .@"packed") {
   1524                 return try cg.resolveType(.fromInterned(struct_type.backingIntTypeUnordered(ip)), .direct);
   1525             }
   1526 
   1527             var member_types = std.array_list.Managed(Id).init(gpa);
   1528             defer member_types.deinit();
   1529 
   1530             var member_names = std.array_list.Managed([]const u8).init(gpa);
   1531             defer member_names.deinit();
   1532 
   1533             var member_offsets = std.array_list.Managed(u32).init(gpa);
   1534             defer member_offsets.deinit();
   1535 
   1536             var it = struct_type.iterateRuntimeOrder(ip);
   1537             while (it.next()) |field_index| {
   1538                 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
   1539                 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   1540 
   1541                 const field_name = struct_type.fieldName(ip, field_index);
   1542                 try member_types.append(try cg.resolveType(field_ty, .indirect));
   1543                 try member_names.append(field_name.toSlice(ip));
   1544                 try member_offsets.append(@intCast(ty.structFieldOffset(field_index, zcu)));
   1545             }
   1546 
   1547             const result_id = try cg.module.structType(
   1548                 member_types.items,
   1549                 member_names.items,
   1550                 member_offsets.items,
   1551                 ty.toIntern(),
   1552             );
   1553 
   1554             const type_name = try cg.resolveTypeName(ty);
   1555             defer gpa.free(type_name);
   1556             try cg.module.debugName(result_id, type_name);
   1557 
   1558             return result_id;
   1559         },
   1560         .optional => {
   1561             const payload_ty = ty.optionalChild(zcu);
   1562             if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   1563                 // Just use a bool.
   1564                 // Note: Always generate the bool with indirect format, to save on some sanity
   1565                 // Perform the conversion to a direct bool when the field is extracted.
   1566                 return try cg.resolveType(.bool, .indirect);
   1567             }
   1568 
   1569             const payload_ty_id = try cg.resolveType(payload_ty, .indirect);
   1570             if (ty.optionalReprIsPayload(zcu)) {
   1571                 // Optional is actually a pointer or a slice.
   1572                 return payload_ty_id;
   1573             }
   1574 
   1575             const bool_ty_id = try cg.resolveType(.bool, .indirect);
   1576 
   1577             return try cg.module.structType(
   1578                 &.{ payload_ty_id, bool_ty_id },
   1579                 &.{ "payload", "valid" },
   1580                 null,
   1581                 .none,
   1582             );
   1583         },
   1584         .@"union" => return try cg.resolveUnionType(ty),
   1585         .error_set => {
   1586             const err_int_ty = try pt.errorIntType();
   1587             return try cg.resolveType(err_int_ty, repr);
   1588         },
   1589         .error_union => {
   1590             const payload_ty = ty.errorUnionPayload(zcu);
   1591             const err_ty = ty.errorUnionSet(zcu);
   1592             const error_ty_id = try cg.resolveType(err_ty, .indirect);
   1593 
   1594             const eu_layout = cg.errorUnionLayout(payload_ty);
   1595             if (!eu_layout.payload_has_bits) {
   1596                 return error_ty_id;
   1597             }
   1598 
   1599             const payload_ty_id = try cg.resolveType(payload_ty, .indirect);
   1600 
   1601             var member_types: [2]Id = undefined;
   1602             var member_names: [2][]const u8 = undefined;
   1603             if (eu_layout.error_first) {
   1604                 // Put the error first
   1605                 member_types = .{ error_ty_id, payload_ty_id };
   1606                 member_names = .{ "error", "payload" };
   1607                 // TODO: ABI padding?
   1608             } else {
   1609                 // Put the payload first.
   1610                 member_types = .{ payload_ty_id, error_ty_id };
   1611                 member_names = .{ "payload", "error" };
   1612                 // TODO: ABI padding?
   1613             }
   1614 
   1615             return try cg.module.structType(&member_types, &member_names, null, .none);
   1616         },
   1617         .@"opaque" => {
   1618             if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
   1619             const type_name = try cg.resolveTypeName(ty);
   1620             defer gpa.free(type_name);
   1621             return try cg.module.opaqueType(type_name);
   1622         },
   1623 
   1624         .null,
   1625         .undefined,
   1626         .enum_literal,
   1627         .comptime_float,
   1628         .comptime_int,
   1629         .type,
   1630         => unreachable, // Must be comptime.
   1631 
   1632         .frame, .@"anyframe" => unreachable, // TODO
   1633     }
   1634 }
   1635 
   1636 const ErrorUnionLayout = struct {
   1637     payload_has_bits: bool,
   1638     error_first: bool,
   1639 
   1640     fn errorFieldIndex(cg: @This()) u32 {
   1641         assert(cg.payload_has_bits);
   1642         return if (cg.error_first) 0 else 1;
   1643     }
   1644 
   1645     fn payloadFieldIndex(cg: @This()) u32 {
   1646         assert(cg.payload_has_bits);
   1647         return if (cg.error_first) 1 else 0;
   1648     }
   1649 };
   1650 
   1651 fn errorUnionLayout(cg: *CodeGen, payload_ty: Type) ErrorUnionLayout {
   1652     const zcu = cg.module.zcu;
   1653 
   1654     const error_align = Type.abiAlignment(.anyerror, zcu);
   1655     const payload_align = payload_ty.abiAlignment(zcu);
   1656 
   1657     const error_first = error_align.compare(.gt, payload_align);
   1658     return .{
   1659         .payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu),
   1660         .error_first = error_first,
   1661     };
   1662 }
   1663 
   1664 const UnionLayout = struct {
   1665     /// If false, this union is represented
   1666     /// by only an integer of the tag type.
   1667     has_payload: bool,
   1668     tag_size: u32,
   1669     tag_index: u32,
   1670     /// Note: This is the size of the payload type itcg, NOT the size of the ENTIRE payload.
   1671     /// Use `has_payload` instead!!
   1672     payload_ty: Type,
   1673     payload_size: u32,
   1674     payload_index: u32,
   1675     payload_padding_size: u32,
   1676     payload_padding_index: u32,
   1677     padding_size: u32,
   1678     padding_index: u32,
   1679     total_fields: u32,
   1680 };
   1681 
   1682 fn unionLayout(cg: *CodeGen, ty: Type) UnionLayout {
   1683     const zcu = cg.module.zcu;
   1684     const ip = &zcu.intern_pool;
   1685     const layout = ty.unionGetLayout(zcu);
   1686     const union_obj = zcu.typeToUnion(ty).?;
   1687 
   1688     var union_layout: UnionLayout = .{
   1689         .has_payload = layout.payload_size != 0,
   1690         .tag_size = @intCast(layout.tag_size),
   1691         .tag_index = undefined,
   1692         .payload_ty = undefined,
   1693         .payload_size = undefined,
   1694         .payload_index = undefined,
   1695         .payload_padding_size = undefined,
   1696         .payload_padding_index = undefined,
   1697         .padding_size = @intCast(layout.padding),
   1698         .padding_index = undefined,
   1699         .total_fields = undefined,
   1700     };
   1701 
   1702     if (union_layout.has_payload) {
   1703         const most_aligned_field = layout.most_aligned_field;
   1704         const most_aligned_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[most_aligned_field]);
   1705         union_layout.payload_ty = most_aligned_field_ty;
   1706         union_layout.payload_size = @intCast(most_aligned_field_ty.abiSize(zcu));
   1707     } else {
   1708         union_layout.payload_size = 0;
   1709     }
   1710 
   1711     union_layout.payload_padding_size = @intCast(layout.payload_size - union_layout.payload_size);
   1712 
   1713     const tag_first = layout.tag_align.compare(.gte, layout.payload_align);
   1714     var field_index: u32 = 0;
   1715 
   1716     if (union_layout.tag_size != 0 and tag_first) {
   1717         union_layout.tag_index = field_index;
   1718         field_index += 1;
   1719     }
   1720 
   1721     if (union_layout.payload_size != 0) {
   1722         union_layout.payload_index = field_index;
   1723         field_index += 1;
   1724     }
   1725 
   1726     if (union_layout.payload_padding_size != 0) {
   1727         union_layout.payload_padding_index = field_index;
   1728         field_index += 1;
   1729     }
   1730 
   1731     if (union_layout.tag_size != 0 and !tag_first) {
   1732         union_layout.tag_index = field_index;
   1733         field_index += 1;
   1734     }
   1735 
   1736     if (union_layout.padding_size != 0) {
   1737         union_layout.padding_index = field_index;
   1738         field_index += 1;
   1739     }
   1740 
   1741     union_layout.total_fields = field_index;
   1742 
   1743     return union_layout;
   1744 }
   1745 
   1746 /// This structure represents a "temporary" value: Something we are currently
   1747 /// operating on. It typically lives no longer than the function that
   1748 /// implements a particular AIR operation. These are used to easier
   1749 /// implement vectorizable operations (see Vectorization and the build*
   1750 /// functions), and typically are only used for vectors of primitive types.
   1751 const Temporary = struct {
   1752     /// The type of the temporary. This is here mainly
   1753     /// for easier bookkeeping. Because we will never really
   1754     /// store Temporaries, they only cause extra stack space,
   1755     /// therefore no real storage is wasted.
   1756     ty: Type,
   1757     /// The value that this temporary holds. This is not necessarily
   1758     /// a value that is actually usable, or a single value: It is virtual
   1759     /// until materialize() is called, at which point is turned into
   1760     /// the usual SPIR-V representation of `cg.ty`.
   1761     value: Temporary.Value,
   1762 
   1763     const Value = union(enum) {
   1764         singleton: Id,
   1765         exploded_vector: IdRange,
   1766     };
   1767 
   1768     fn init(ty: Type, singleton: Id) Temporary {
   1769         return .{ .ty = ty, .value = .{ .singleton = singleton } };
   1770     }
   1771 
   1772     fn materialize(temp: Temporary, cg: *CodeGen) !Id {
   1773         const gpa = cg.module.gpa;
   1774         const zcu = cg.module.zcu;
   1775         switch (temp.value) {
   1776             .singleton => |id| return id,
   1777             .exploded_vector => |range| {
   1778                 assert(temp.ty.isVector(zcu));
   1779                 assert(temp.ty.vectorLen(zcu) == range.len);
   1780 
   1781                 const scratch_top = cg.id_scratch.items.len;
   1782                 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   1783                 const constituents = try cg.id_scratch.addManyAsSlice(gpa, range.len);
   1784                 for (constituents, 0..range.len) |*id, i| {
   1785                     id.* = range.at(i);
   1786                 }
   1787 
   1788                 const result_ty_id = try cg.resolveType(temp.ty, .direct);
   1789                 return cg.constructComposite(result_ty_id, constituents);
   1790             },
   1791         }
   1792     }
   1793 
   1794     fn vectorization(temp: Temporary, cg: *CodeGen) Vectorization {
   1795         return .fromType(temp.ty, cg);
   1796     }
   1797 
   1798     fn pun(temp: Temporary, new_ty: Type) Temporary {
   1799         return .{
   1800             .ty = new_ty,
   1801             .value = temp.value,
   1802         };
   1803     }
   1804 
   1805     /// 'Explode' a temporary into separate elements. This turns a vector
   1806     /// into a bag of elements.
   1807     fn explode(temp: Temporary, cg: *CodeGen) !IdRange {
   1808         const zcu = cg.module.zcu;
   1809 
   1810         // If the value is a scalar, then this is a no-op.
   1811         if (!temp.ty.isVector(zcu)) {
   1812             return switch (temp.value) {
   1813                 .singleton => |id| .{ .base = @intFromEnum(id), .len = 1 },
   1814                 .exploded_vector => |range| range,
   1815             };
   1816         }
   1817 
   1818         const ty_id = try cg.resolveType(temp.ty.scalarType(zcu), .direct);
   1819         const n = temp.ty.vectorLen(zcu);
   1820         const results = cg.module.allocIds(n);
   1821 
   1822         const id = switch (temp.value) {
   1823             .singleton => |id| id,
   1824             .exploded_vector => |range| return range,
   1825         };
   1826 
   1827         for (0..n) |i| {
   1828             const indexes = [_]u32{@intCast(i)};
   1829             try cg.body.emit(cg.module.gpa, .OpCompositeExtract, .{
   1830                 .id_result_type = ty_id,
   1831                 .id_result = results.at(i),
   1832                 .composite = id,
   1833                 .indexes = &indexes,
   1834             });
   1835         }
   1836 
   1837         return results;
   1838     }
   1839 };
   1840 
   1841 /// Initialize a `Temporary` from an AIR value.
   1842 fn temporary(cg: *CodeGen, inst: Air.Inst.Ref) !Temporary {
   1843     return .{
   1844         .ty = cg.typeOf(inst),
   1845         .value = .{ .singleton = try cg.resolve(inst) },
   1846     };
   1847 }
   1848 
   1849 /// This union describes how a particular operation should be vectorized.
   1850 /// That depends on the operation and number of components of the inputs.
   1851 const Vectorization = union(enum) {
   1852     /// This is an operation between scalars.
   1853     scalar,
   1854     /// This operation is unrolled into separate operations.
   1855     /// Inputs may still be SPIR-V vectors, for example,
   1856     /// when the operation can't be vectorized in SPIR-V.
   1857     /// Value is number of components.
   1858     unrolled: u32,
   1859 
   1860     /// Derive a vectorization from a particular type
   1861     fn fromType(ty: Type, cg: *CodeGen) Vectorization {
   1862         const zcu = cg.module.zcu;
   1863         if (!ty.isVector(zcu)) return .scalar;
   1864         return .{ .unrolled = ty.vectorLen(zcu) };
   1865     }
   1866 
   1867     /// Given two vectorization methods, compute a "unification": a fallback
   1868     /// that works for both, according to the following rules:
   1869     /// - Scalars may broadcast
   1870     /// - SPIR-V vectorized operations will unroll
   1871     /// - Prefer scalar > unrolled
   1872     fn unify(a: Vectorization, b: Vectorization) Vectorization {
   1873         if (a == .scalar and b == .scalar) return .scalar;
   1874         if (a == .unrolled or b == .unrolled) {
   1875             if (a == .unrolled and b == .unrolled) assert(a.components() == b.components());
   1876             if (a == .unrolled) return .{ .unrolled = a.components() };
   1877             return .{ .unrolled = b.components() };
   1878         }
   1879         unreachable;
   1880     }
   1881 
   1882     /// Query the number of components that inputs of this operation have.
   1883     /// Note: for broadcasting scalars, this returns the number of elements
   1884     /// that the broadcasted vector would have.
   1885     fn components(vec: Vectorization) u32 {
   1886         return switch (vec) {
   1887             .scalar => 1,
   1888             .unrolled => |n| n,
   1889         };
   1890     }
   1891 
   1892     /// Turns `ty` into the result-type of the entire operation.
   1893     /// `ty` may be a scalar or vector, it doesn't matter.
   1894     fn resultType(vec: Vectorization, cg: *CodeGen, ty: Type) !Type {
   1895         const pt = cg.pt;
   1896         const zcu = cg.module.zcu;
   1897         const scalar_ty = ty.scalarType(zcu);
   1898         return switch (vec) {
   1899             .scalar => scalar_ty,
   1900             .unrolled => |n| try pt.vectorType(.{ .len = n, .child = scalar_ty.toIntern() }),
   1901         };
   1902     }
   1903 
   1904     /// Before a temporary can be used, some setup may need to be one. This function implements
   1905     /// this setup, and returns a new type that holds the relevant information on how to access
   1906     /// elements of the input.
   1907     fn prepare(vec: Vectorization, cg: *CodeGen, tmp: Temporary) !PreparedOperand {
   1908         const zcu = cg.module.zcu;
   1909         const is_vector = tmp.ty.isVector(zcu);
   1910         const value: PreparedOperand.Value = switch (tmp.value) {
   1911             .singleton => |id| switch (vec) {
   1912                 .scalar => blk: {
   1913                     assert(!is_vector);
   1914                     break :blk .{ .scalar = id };
   1915                 },
   1916                 .unrolled => blk: {
   1917                     if (is_vector) break :blk .{ .vector_exploded = try tmp.explode(cg) };
   1918                     break :blk .{ .scalar_broadcast = id };
   1919                 },
   1920             },
   1921             .exploded_vector => |range| switch (vec) {
   1922                 .scalar => unreachable,
   1923                 .unrolled => |n| blk: {
   1924                     assert(range.len == n);
   1925                     break :blk .{ .vector_exploded = range };
   1926                 },
   1927             },
   1928         };
   1929 
   1930         return .{
   1931             .ty = tmp.ty,
   1932             .value = value,
   1933         };
   1934     }
   1935 
   1936     /// Finalize the results of an operation back into a temporary. `results` is
   1937     /// a list of result-ids of the operation.
   1938     fn finalize(vec: Vectorization, ty: Type, results: IdRange) Temporary {
   1939         assert(vec.components() == results.len);
   1940         return .{
   1941             .ty = ty,
   1942             .value = switch (vec) {
   1943                 .scalar => .{ .singleton = results.at(0) },
   1944                 .unrolled => .{ .exploded_vector = results },
   1945             },
   1946         };
   1947     }
   1948 
   1949     /// This struct represents an operand that has gone through some setup, and is
   1950     /// ready to be used as part of an operation.
   1951     const PreparedOperand = struct {
   1952         ty: Type,
   1953         value: PreparedOperand.Value,
   1954 
   1955         /// The types of value that a prepared operand can hold internally. Depends
   1956         /// on the operation and input value.
   1957         const Value = union(enum) {
   1958             /// A single scalar value that is used by a scalar operation.
   1959             scalar: Id,
   1960             /// A single scalar that is broadcasted in an unrolled operation.
   1961             scalar_broadcast: Id,
   1962             /// A vector represented by a consecutive list of IDs that is used in an unrolled operation.
   1963             vector_exploded: IdRange,
   1964         };
   1965 
   1966         /// Query the value at a particular index of the operation. Note that
   1967         /// the index is *not* the component/lane, but the index of the *operation*.
   1968         fn at(op: PreparedOperand, i: usize) Id {
   1969             switch (op.value) {
   1970                 .scalar => |id| {
   1971                     assert(i == 0);
   1972                     return id;
   1973                 },
   1974                 .scalar_broadcast => |id| return id,
   1975                 .vector_exploded => |range| return range.at(i),
   1976             }
   1977         }
   1978     };
   1979 };
   1980 
   1981 /// A utility function to compute the vectorization style of
   1982 /// a list of values. These values may be any of the following:
   1983 /// - A `Vectorization` instance
   1984 /// - A Type, in which case the vectorization is computed via `Vectorization.fromType`.
   1985 /// - A Temporary, in which case the vectorization is computed via `Temporary.vectorization`.
   1986 fn vectorization(cg: *CodeGen, args: anytype) Vectorization {
   1987     var v: Vectorization = undefined;
   1988     assert(args.len >= 1);
   1989     inline for (args, 0..) |arg, i| {
   1990         const iv: Vectorization = switch (@TypeOf(arg)) {
   1991             Vectorization => arg,
   1992             Type => Vectorization.fromType(arg, cg),
   1993             Temporary => arg.vectorization(cg),
   1994             else => @compileError("invalid type"),
   1995         };
   1996         if (i == 0) {
   1997             v = iv;
   1998         } else {
   1999             v = v.unify(iv);
   2000         }
   2001     }
   2002     return v;
   2003 }
   2004 
   2005 /// This function builds an OpSConvert of OpUConvert depending on the
   2006 /// signedness of the types.
   2007 fn buildConvert(cg: *CodeGen, dst_ty: Type, src: Temporary) !Temporary {
   2008     const zcu = cg.module.zcu;
   2009 
   2010     const dst_ty_id = try cg.resolveType(dst_ty.scalarType(zcu), .direct);
   2011     const src_ty_id = try cg.resolveType(src.ty.scalarType(zcu), .direct);
   2012 
   2013     const v = cg.vectorization(.{ dst_ty, src });
   2014     const result_ty = try v.resultType(cg, dst_ty);
   2015 
   2016     // We can directly compare integers, because those type-IDs are cached.
   2017     if (dst_ty_id == src_ty_id) {
   2018         // Nothing to do, type-pun to the right value.
   2019         // Note, Caller guarantees that the types fit (or caller will normalize after),
   2020         // so we don't have to normalize here.
   2021         // Note, dst_ty may be a scalar type even if we expect a vector, so we have to
   2022         // convert to the right type here.
   2023         return src.pun(result_ty);
   2024     }
   2025 
   2026     const ops = v.components();
   2027     const results = cg.module.allocIds(ops);
   2028 
   2029     const op_result_ty = dst_ty.scalarType(zcu);
   2030     const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
   2031 
   2032     const opcode: Opcode = blk: {
   2033         if (dst_ty.scalarType(zcu).isAnyFloat()) break :blk .OpFConvert;
   2034         if (dst_ty.scalarType(zcu).isSignedInt(zcu)) break :blk .OpSConvert;
   2035         break :blk .OpUConvert;
   2036     };
   2037 
   2038     const op_src = try v.prepare(cg, src);
   2039 
   2040     for (0..ops) |i| {
   2041         try cg.body.emitRaw(cg.module.gpa, opcode, 3);
   2042         cg.body.writeOperand(Id, op_result_ty_id);
   2043         cg.body.writeOperand(Id, results.at(i));
   2044         cg.body.writeOperand(Id, op_src.at(i));
   2045     }
   2046 
   2047     return v.finalize(result_ty, results);
   2048 }
   2049 
   2050 fn buildFma(cg: *CodeGen, a: Temporary, b: Temporary, c: Temporary) !Temporary {
   2051     const zcu = cg.module.zcu;
   2052     const target = cg.module.zcu.getTarget();
   2053 
   2054     const v = cg.vectorization(.{ a, b, c });
   2055     const ops = v.components();
   2056     const results = cg.module.allocIds(ops);
   2057 
   2058     const op_result_ty = a.ty.scalarType(zcu);
   2059     const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
   2060     const result_ty = try v.resultType(cg, a.ty);
   2061 
   2062     const op_a = try v.prepare(cg, a);
   2063     const op_b = try v.prepare(cg, b);
   2064     const op_c = try v.prepare(cg, c);
   2065 
   2066     const set = try cg.importExtendedSet();
   2067     const opcode: u32 = switch (target.os.tag) {
   2068         .opencl => @intFromEnum(spec.OpenClOpcode.fma),
   2069         // NOTE: Vulkan's FMA instruction does *NOT* produce the right values!
   2070         //       its precision guarantees do NOT match zigs and it does NOT match OpenCLs!
   2071         //       it needs to be emulated!
   2072         .vulkan, .opengl => @intFromEnum(spec.GlslOpcode.Fma),
   2073         else => unreachable,
   2074     };
   2075 
   2076     for (0..ops) |i| {
   2077         try cg.body.emit(cg.module.gpa, .OpExtInst, .{
   2078             .id_result_type = op_result_ty_id,
   2079             .id_result = results.at(i),
   2080             .set = set,
   2081             .instruction = .{ .inst = opcode },
   2082             .id_ref_4 = &.{ op_a.at(i), op_b.at(i), op_c.at(i) },
   2083         });
   2084     }
   2085 
   2086     return v.finalize(result_ty, results);
   2087 }
   2088 
   2089 fn buildSelect(cg: *CodeGen, condition: Temporary, lhs: Temporary, rhs: Temporary) !Temporary {
   2090     const zcu = cg.module.zcu;
   2091 
   2092     const v = cg.vectorization(.{ condition, lhs, rhs });
   2093     const ops = v.components();
   2094     const results = cg.module.allocIds(ops);
   2095 
   2096     const op_result_ty = lhs.ty.scalarType(zcu);
   2097     const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
   2098     const result_ty = try v.resultType(cg, lhs.ty);
   2099 
   2100     assert(condition.ty.scalarType(zcu).zigTypeTag(zcu) == .bool);
   2101 
   2102     const cond = try v.prepare(cg, condition);
   2103     const object_1 = try v.prepare(cg, lhs);
   2104     const object_2 = try v.prepare(cg, rhs);
   2105 
   2106     for (0..ops) |i| {
   2107         try cg.body.emit(cg.module.gpa, .OpSelect, .{
   2108             .id_result_type = op_result_ty_id,
   2109             .id_result = results.at(i),
   2110             .condition = cond.at(i),
   2111             .object_1 = object_1.at(i),
   2112             .object_2 = object_2.at(i),
   2113         });
   2114     }
   2115 
   2116     return v.finalize(result_ty, results);
   2117 }
   2118 
   2119 fn buildCmp(cg: *CodeGen, opcode: Opcode, lhs: Temporary, rhs: Temporary) !Temporary {
   2120     const v = cg.vectorization(.{ lhs, rhs });
   2121     const ops = v.components();
   2122     const results = cg.module.allocIds(ops);
   2123 
   2124     const op_result_ty: Type = .bool;
   2125     const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
   2126     const result_ty = try v.resultType(cg, Type.bool);
   2127 
   2128     const op_lhs = try v.prepare(cg, lhs);
   2129     const op_rhs = try v.prepare(cg, rhs);
   2130 
   2131     for (0..ops) |i| {
   2132         try cg.body.emitRaw(cg.module.gpa, opcode, 4);
   2133         cg.body.writeOperand(Id, op_result_ty_id);
   2134         cg.body.writeOperand(Id, results.at(i));
   2135         cg.body.writeOperand(Id, op_lhs.at(i));
   2136         cg.body.writeOperand(Id, op_rhs.at(i));
   2137     }
   2138 
   2139     return v.finalize(result_ty, results);
   2140 }
   2141 
   2142 const UnaryOp = enum {
   2143     l_not,
   2144     bit_not,
   2145     i_neg,
   2146     f_neg,
   2147     i_abs,
   2148     f_abs,
   2149     clz,
   2150     ctz,
   2151     floor,
   2152     ceil,
   2153     trunc,
   2154     round,
   2155     sqrt,
   2156     sin,
   2157     cos,
   2158     tan,
   2159     exp,
   2160     exp2,
   2161     log,
   2162     log2,
   2163     log10,
   2164 
   2165     pub fn extInstOpcode(op: UnaryOp, target: *const std.Target) ?u32 {
   2166         return switch (target.os.tag) {
   2167             .opencl => @intFromEnum(@as(spec.OpenClOpcode, switch (op) {
   2168                 .i_abs => .s_abs,
   2169                 .f_abs => .fabs,
   2170                 .clz => .clz,
   2171                 .ctz => .ctz,
   2172                 .floor => .floor,
   2173                 .ceil => .ceil,
   2174                 .trunc => .trunc,
   2175                 .round => .round,
   2176                 .sqrt => .sqrt,
   2177                 .sin => .sin,
   2178                 .cos => .cos,
   2179                 .tan => .tan,
   2180                 .exp => .exp,
   2181                 .exp2 => .exp2,
   2182                 .log => .log,
   2183                 .log2 => .log2,
   2184                 .log10 => .log10,
   2185                 else => return null,
   2186             })),
   2187             // Note: We'll need to check these for floating point accuracy
   2188             // Vulkan does not put tight requirements on these, for correction
   2189             // we might want to emulate them at some point.
   2190             .vulkan, .opengl => @intFromEnum(@as(spec.GlslOpcode, switch (op) {
   2191                 .i_abs => .SAbs,
   2192                 .f_abs => .FAbs,
   2193                 .floor => .Floor,
   2194                 .ceil => .Ceil,
   2195                 .trunc => .Trunc,
   2196                 .round => .Round,
   2197                 .sin => .Sin,
   2198                 .cos => .Cos,
   2199                 .tan => .Tan,
   2200                 .sqrt => .Sqrt,
   2201                 .exp => .Exp,
   2202                 .exp2 => .Exp2,
   2203                 .log => .Log,
   2204                 .log2 => .Log2,
   2205                 else => return null,
   2206             })),
   2207             else => unreachable,
   2208         };
   2209     }
   2210 };
   2211 
   2212 fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary {
   2213     const zcu = cg.module.zcu;
   2214     const target = cg.module.zcu.getTarget();
   2215     const v = cg.vectorization(.{operand});
   2216     const ops = v.components();
   2217     const results = cg.module.allocIds(ops);
   2218     const op_result_ty = operand.ty.scalarType(zcu);
   2219     const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
   2220     const result_ty = try v.resultType(cg, operand.ty);
   2221     const op_operand = try v.prepare(cg, operand);
   2222 
   2223     if (op.extInstOpcode(target)) |opcode| {
   2224         const set = try cg.importExtendedSet();
   2225         for (0..ops) |i| {
   2226             try cg.body.emit(cg.module.gpa, .OpExtInst, .{
   2227                 .id_result_type = op_result_ty_id,
   2228                 .id_result = results.at(i),
   2229                 .set = set,
   2230                 .instruction = .{ .inst = opcode },
   2231                 .id_ref_4 = &.{op_operand.at(i)},
   2232             });
   2233         }
   2234     } else {
   2235         const opcode: Opcode = switch (op) {
   2236             .l_not => .OpLogicalNot,
   2237             .bit_not => .OpNot,
   2238             .i_neg => .OpSNegate,
   2239             .f_neg => .OpFNegate,
   2240             else => return cg.todo(
   2241                 "implement unary operation '{s}' for {s} os",
   2242                 .{ @tagName(op), @tagName(target.os.tag) },
   2243             ),
   2244         };
   2245         for (0..ops) |i| {
   2246             try cg.body.emitRaw(cg.module.gpa, opcode, 3);
   2247             cg.body.writeOperand(Id, op_result_ty_id);
   2248             cg.body.writeOperand(Id, results.at(i));
   2249             cg.body.writeOperand(Id, op_operand.at(i));
   2250         }
   2251     }
   2252 
   2253     return v.finalize(result_ty, results);
   2254 }
   2255 
   2256 fn buildBinary(cg: *CodeGen, opcode: Opcode, lhs: Temporary, rhs: Temporary) !Temporary {
   2257     const zcu = cg.module.zcu;
   2258 
   2259     const v = cg.vectorization(.{ lhs, rhs });
   2260     const ops = v.components();
   2261     const results = cg.module.allocIds(ops);
   2262 
   2263     const op_result_ty = lhs.ty.scalarType(zcu);
   2264     const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
   2265     const result_ty = try v.resultType(cg, lhs.ty);
   2266 
   2267     const op_lhs = try v.prepare(cg, lhs);
   2268     const op_rhs = try v.prepare(cg, rhs);
   2269 
   2270     for (0..ops) |i| {
   2271         try cg.body.emitRaw(cg.module.gpa, opcode, 4);
   2272         cg.body.writeOperand(Id, op_result_ty_id);
   2273         cg.body.writeOperand(Id, results.at(i));
   2274         cg.body.writeOperand(Id, op_lhs.at(i));
   2275         cg.body.writeOperand(Id, op_rhs.at(i));
   2276     }
   2277 
   2278     return v.finalize(result_ty, results);
   2279 }
   2280 
   2281 /// This function builds an extended multiplication, either OpSMulExtended or OpUMulExtended on Vulkan,
   2282 /// or OpIMul and s_mul_hi or u_mul_hi on OpenCL.
   2283 fn buildWideMul(
   2284     cg: *CodeGen,
   2285     signedness: std.builtin.Signedness,
   2286     lhs: Temporary,
   2287     rhs: Temporary,
   2288 ) !struct { Temporary, Temporary } {
   2289     const pt = cg.pt;
   2290     const zcu = cg.module.zcu;
   2291     const comp = zcu.comp;
   2292     const gpa = comp.gpa;
   2293     const io = comp.io;
   2294     const target = cg.module.zcu.getTarget();
   2295     const ip = &zcu.intern_pool;
   2296 
   2297     const v = lhs.vectorization(cg).unify(rhs.vectorization(cg));
   2298     const ops = v.components();
   2299 
   2300     const arith_op_ty = lhs.ty.scalarType(zcu);
   2301     const arith_op_ty_id = try cg.resolveType(arith_op_ty, .direct);
   2302 
   2303     const lhs_op = try v.prepare(cg, lhs);
   2304     const rhs_op = try v.prepare(cg, rhs);
   2305 
   2306     const value_results = cg.module.allocIds(ops);
   2307     const overflow_results = cg.module.allocIds(ops);
   2308 
   2309     switch (target.os.tag) {
   2310         .opencl => {
   2311             // Currently, SPIRV-LLVM-Translator based backends cannot deal with OpSMulExtended and
   2312             // OpUMulExtended. For these we will use the OpenCL s_mul_hi to compute the high-order bits
   2313             // instead.
   2314             const set = try cg.importExtendedSet();
   2315             const overflow_inst: spec.OpenClOpcode = switch (signedness) {
   2316                 .signed => .s_mul_hi,
   2317                 .unsigned => .u_mul_hi,
   2318             };
   2319 
   2320             for (0..ops) |i| {
   2321                 try cg.body.emit(gpa, .OpIMul, .{
   2322                     .id_result_type = arith_op_ty_id,
   2323                     .id_result = value_results.at(i),
   2324                     .operand_1 = lhs_op.at(i),
   2325                     .operand_2 = rhs_op.at(i),
   2326                 });
   2327 
   2328                 try cg.body.emit(gpa, .OpExtInst, .{
   2329                     .id_result_type = arith_op_ty_id,
   2330                     .id_result = overflow_results.at(i),
   2331                     .set = set,
   2332                     .instruction = .{ .inst = @intFromEnum(overflow_inst) },
   2333                     .id_ref_4 = &.{ lhs_op.at(i), rhs_op.at(i) },
   2334                 });
   2335             }
   2336         },
   2337         .vulkan, .opengl => {
   2338             // Operations return a struct{T, T}
   2339             // where T is maybe vectorized.
   2340             const op_result_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{
   2341                 .types = &.{ arith_op_ty.toIntern(), arith_op_ty.toIntern() },
   2342                 .values = &.{ .none, .none },
   2343             }));
   2344             const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
   2345 
   2346             const opcode: Opcode = switch (signedness) {
   2347                 .signed => .OpSMulExtended,
   2348                 .unsigned => .OpUMulExtended,
   2349             };
   2350 
   2351             for (0..ops) |i| {
   2352                 const op_result = cg.module.allocId();
   2353 
   2354                 try cg.body.emitRaw(gpa, opcode, 4);
   2355                 cg.body.writeOperand(Id, op_result_ty_id);
   2356                 cg.body.writeOperand(Id, op_result);
   2357                 cg.body.writeOperand(Id, lhs_op.at(i));
   2358                 cg.body.writeOperand(Id, rhs_op.at(i));
   2359 
   2360                 // The above operation returns a struct. We might want to expand
   2361                 // Temporary to deal with the fact that these are structs eventually,
   2362                 // but for now, take the struct apart and return two separate vectors.
   2363 
   2364                 try cg.body.emit(gpa, .OpCompositeExtract, .{
   2365                     .id_result_type = arith_op_ty_id,
   2366                     .id_result = value_results.at(i),
   2367                     .composite = op_result,
   2368                     .indexes = &.{0},
   2369                 });
   2370 
   2371                 try cg.body.emit(gpa, .OpCompositeExtract, .{
   2372                     .id_result_type = arith_op_ty_id,
   2373                     .id_result = overflow_results.at(i),
   2374                     .composite = op_result,
   2375                     .indexes = &.{1},
   2376                 });
   2377             }
   2378         },
   2379         else => unreachable,
   2380     }
   2381 
   2382     const result_ty = try v.resultType(cg, lhs.ty);
   2383     return .{
   2384         v.finalize(result_ty, value_results),
   2385         v.finalize(result_ty, overflow_results),
   2386     };
   2387 }
   2388 
   2389 /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure.
   2390 /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry-
   2391 /// points. The test executor will then be able to invoke these to run the tests.
   2392 /// Note that tests are lowered according to std.builtin.TestFn, which is `fn () anyerror!void`.
   2393 /// (anyerror!void has the same layout as anyerror).
   2394 /// Each test declaration generates a function like.
   2395 ///   %anyerror = OpTypeInt 0 16
   2396 ///   %p_invocation_globals_struct_ty = ...
   2397 ///   %p_anyerror = OpTypePointer CrossWorkgroup %anyerror
   2398 ///   %K = OpTypeFunction %void %p_invocation_globals_struct_ty %p_anyerror
   2399 ///
   2400 ///   %test = OpFunction %void %K
   2401 ///   %p_invocation_globals = OpFunctionParameter p_invocation_globals_struct_ty
   2402 ///   %p_err = OpFunctionParameter %p_anyerror
   2403 ///   %lbl = OpLabel
   2404 ///   %result = OpFunctionCall %anyerror %func %p_invocation_globals
   2405 ///   OpStore %p_err %result
   2406 ///   OpFunctionEnd
   2407 /// TODO is to also write out the error as a function call parameter, and to somehow fetch
   2408 /// the name of an error in the text executor.
   2409 fn generateTestEntryPoint(
   2410     cg: *CodeGen,
   2411     name: []const u8,
   2412     spv_decl_index: Module.Decl.Index,
   2413     test_id: Id,
   2414 ) !void {
   2415     const gpa = cg.module.gpa;
   2416     const zcu = cg.module.zcu;
   2417     const target = cg.module.zcu.getTarget();
   2418 
   2419     const anyerror_ty_id = try cg.resolveType(.anyerror, .direct);
   2420     const ptr_anyerror_ty = try cg.pt.ptrType(.{
   2421         .child = .anyerror_type,
   2422         .flags = .{ .address_space = .global },
   2423     });
   2424     const ptr_anyerror_ty_id = try cg.resolveType(ptr_anyerror_ty, .direct);
   2425 
   2426     const kernel_id = cg.module.declPtr(spv_decl_index).result_id;
   2427 
   2428     const section = &cg.module.sections.functions;
   2429 
   2430     const p_error_id = cg.module.allocId();
   2431     switch (target.os.tag) {
   2432         .opencl, .amdhsa => {
   2433             const void_ty_id = try cg.resolveType(.void, .direct);
   2434             const kernel_proto_ty_id = try cg.module.functionType(void_ty_id, &.{ptr_anyerror_ty_id});
   2435 
   2436             try section.emit(gpa, .OpFunction, .{
   2437                 .id_result_type = try cg.resolveType(.void, .direct),
   2438                 .id_result = kernel_id,
   2439                 .function_control = .{},
   2440                 .function_type = kernel_proto_ty_id,
   2441             });
   2442 
   2443             try section.emit(gpa, .OpFunctionParameter, .{
   2444                 .id_result_type = ptr_anyerror_ty_id,
   2445                 .id_result = p_error_id,
   2446             });
   2447 
   2448             try section.emit(gpa, .OpLabel, .{
   2449                 .id_result = cg.module.allocId(),
   2450             });
   2451         },
   2452         .vulkan, .opengl => {
   2453             if (cg.module.error_buffer == null) {
   2454                 const spv_err_decl_index = try cg.module.allocDecl(.global);
   2455                 const err_buf_result_id = cg.module.declPtr(spv_err_decl_index).result_id;
   2456 
   2457                 const buffer_struct_ty_id = try cg.module.structType(
   2458                     &.{anyerror_ty_id},
   2459                     &.{"error_out"},
   2460                     null,
   2461                     .none,
   2462                 );
   2463                 try cg.module.decorate(buffer_struct_ty_id, .block);
   2464                 try cg.module.decorateMember(buffer_struct_ty_id, 0, .{ .offset = .{ .byte_offset = 0 } });
   2465 
   2466                 const ptr_buffer_struct_ty_id = cg.module.allocId();
   2467                 try cg.module.sections.globals.emit(gpa, .OpTypePointer, .{
   2468                     .id_result = ptr_buffer_struct_ty_id,
   2469                     .storage_class = cg.module.storageClass(.global),
   2470                     .type = buffer_struct_ty_id,
   2471                 });
   2472 
   2473                 try cg.module.sections.globals.emit(gpa, .OpVariable, .{
   2474                     .id_result_type = ptr_buffer_struct_ty_id,
   2475                     .id_result = err_buf_result_id,
   2476                     .storage_class = cg.module.storageClass(.global),
   2477                 });
   2478                 try cg.module.decorate(err_buf_result_id, .{ .descriptor_set = .{ .descriptor_set = 0 } });
   2479                 try cg.module.decorate(err_buf_result_id, .{ .binding = .{ .binding_point = 0 } });
   2480 
   2481                 cg.module.error_buffer = spv_err_decl_index;
   2482             }
   2483 
   2484             try cg.module.sections.execution_modes.emit(gpa, .OpExecutionMode, .{
   2485                 .entry_point = kernel_id,
   2486                 .mode = .{ .local_size = .{
   2487                     .x_size = 1,
   2488                     .y_size = 1,
   2489                     .z_size = 1,
   2490                 } },
   2491             });
   2492 
   2493             const void_ty_id = try cg.resolveType(.void, .direct);
   2494             const kernel_proto_ty_id = try cg.module.functionType(void_ty_id, &.{});
   2495             try section.emit(gpa, .OpFunction, .{
   2496                 .id_result_type = try cg.resolveType(.void, .direct),
   2497                 .id_result = kernel_id,
   2498                 .function_control = .{},
   2499                 .function_type = kernel_proto_ty_id,
   2500             });
   2501             try section.emit(gpa, .OpLabel, .{
   2502                 .id_result = cg.module.allocId(),
   2503             });
   2504 
   2505             const spv_err_decl_index = cg.module.error_buffer.?;
   2506             const buffer_id = cg.module.declPtr(spv_err_decl_index).result_id;
   2507             try cg.module.decl_deps.append(gpa, spv_err_decl_index);
   2508 
   2509             const zero_id = try cg.constInt(.u32, 0);
   2510             try section.emit(gpa, .OpInBoundsAccessChain, .{
   2511                 .id_result_type = ptr_anyerror_ty_id,
   2512                 .id_result = p_error_id,
   2513                 .base = buffer_id,
   2514                 .indexes = &.{zero_id},
   2515             });
   2516         },
   2517         else => unreachable,
   2518     }
   2519 
   2520     const error_id = cg.module.allocId();
   2521     try section.emit(gpa, .OpFunctionCall, .{
   2522         .id_result_type = anyerror_ty_id,
   2523         .id_result = error_id,
   2524         .function = test_id,
   2525     });
   2526     // Note: Convert to direct not required.
   2527     try section.emit(gpa, .OpStore, .{
   2528         .pointer = p_error_id,
   2529         .object = error_id,
   2530         .memory_access = .{
   2531             .aligned = .{ .literal_integer = @intCast(Type.abiAlignment(.anyerror, zcu).toByteUnits().?) },
   2532         },
   2533     });
   2534     try section.emit(gpa, .OpReturn, {});
   2535     try section.emit(gpa, .OpFunctionEnd, {});
   2536 
   2537     // Just generate a quick other name because the intel runtime crashes when the entry-
   2538     // point name is the same as a different OpName.
   2539     const test_name = try std.fmt.allocPrint(cg.module.arena, "test {s}", .{name});
   2540 
   2541     const execution_mode: spec.ExecutionModel = switch (target.os.tag) {
   2542         .vulkan, .opengl => .gl_compute,
   2543         .opencl, .amdhsa => .kernel,
   2544         else => unreachable,
   2545     };
   2546 
   2547     try cg.module.declareEntryPoint(spv_decl_index, test_name, execution_mode, null);
   2548 }
   2549 
   2550 fn intFromBool(cg: *CodeGen, value: Temporary, result_ty: Type) !Temporary {
   2551     const zero_id = try cg.constInt(result_ty, 0);
   2552     const one_id = try cg.constInt(result_ty, 1);
   2553 
   2554     return try cg.buildSelect(
   2555         value,
   2556         Temporary.init(result_ty, one_id),
   2557         Temporary.init(result_ty, zero_id),
   2558     );
   2559 }
   2560 
   2561 /// Convert representation from indirect (in memory) to direct (in 'register')
   2562 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct).
   2563 fn convertToDirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id {
   2564     const pt = cg.pt;
   2565     const zcu = cg.module.zcu;
   2566     switch (ty.scalarType(zcu).zigTypeTag(zcu)) {
   2567         .bool => {
   2568             const false_id = try cg.constBool(false, .indirect);
   2569             const operand_ty = blk: {
   2570                 if (!ty.isVector(zcu)) break :blk Type.u1;
   2571                 break :blk try pt.vectorType(.{
   2572                     .len = ty.vectorLen(zcu),
   2573                     .child = .u1_type,
   2574                 });
   2575             };
   2576 
   2577             const result = try cg.buildCmp(
   2578                 .OpINotEqual,
   2579                 Temporary.init(operand_ty, operand_id),
   2580                 Temporary.init(.u1, false_id),
   2581             );
   2582             return try result.materialize(cg);
   2583         },
   2584         else => return operand_id,
   2585     }
   2586 }
   2587 
   2588 /// Convert representation from direct (in 'register) to direct (in memory)
   2589 /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect).
   2590 fn convertToIndirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id {
   2591     const zcu = cg.module.zcu;
   2592     switch (ty.scalarType(zcu).zigTypeTag(zcu)) {
   2593         .bool => {
   2594             const result = try cg.intFromBool(.init(ty, operand_id), .u1);
   2595             return try result.materialize(cg);
   2596         },
   2597         else => return operand_id,
   2598     }
   2599 }
   2600 
   2601 fn extractField(cg: *CodeGen, result_ty: Type, object: Id, field: u32) !Id {
   2602     const result_ty_id = try cg.resolveType(result_ty, .indirect);
   2603     const result_id = cg.module.allocId();
   2604     const indexes = [_]u32{field};
   2605     try cg.body.emit(cg.module.gpa, .OpCompositeExtract, .{
   2606         .id_result_type = result_ty_id,
   2607         .id_result = result_id,
   2608         .composite = object,
   2609         .indexes = &indexes,
   2610     });
   2611     // Convert bools; direct structs have their field types as indirect values.
   2612     return try cg.convertToDirect(result_ty, result_id);
   2613 }
   2614 
   2615 fn extractVectorComponent(cg: *CodeGen, result_ty: Type, vector_id: Id, field: u32) !Id {
   2616     const result_ty_id = try cg.resolveType(result_ty, .direct);
   2617     const result_id = cg.module.allocId();
   2618     const indexes = [_]u32{field};
   2619     try cg.body.emit(cg.module.gpa, .OpCompositeExtract, .{
   2620         .id_result_type = result_ty_id,
   2621         .id_result = result_id,
   2622         .composite = vector_id,
   2623         .indexes = &indexes,
   2624     });
   2625     // Vector components are already stored in direct representation.
   2626     return result_id;
   2627 }
   2628 
   2629 const MemoryOptions = struct {
   2630     is_volatile: bool = false,
   2631 };
   2632 
   2633 fn load(cg: *CodeGen, value_ty: Type, ptr_id: Id, options: MemoryOptions) !Id {
   2634     const zcu = cg.module.zcu;
   2635     const alignment: u32 = @intCast(value_ty.abiAlignment(zcu).toByteUnits().?);
   2636     const indirect_value_ty_id = try cg.resolveType(value_ty, .indirect);
   2637     const result_id = cg.module.allocId();
   2638     const access: spec.MemoryAccess.Extended = .{
   2639         .@"volatile" = options.is_volatile,
   2640         .aligned = .{ .literal_integer = alignment },
   2641     };
   2642     try cg.body.emit(cg.module.gpa, .OpLoad, .{
   2643         .id_result_type = indirect_value_ty_id,
   2644         .id_result = result_id,
   2645         .pointer = ptr_id,
   2646         .memory_access = access,
   2647     });
   2648     return try cg.convertToDirect(value_ty, result_id);
   2649 }
   2650 
   2651 fn store(cg: *CodeGen, value_ty: Type, ptr_id: Id, value_id: Id, options: MemoryOptions) !void {
   2652     const indirect_value_id = try cg.convertToIndirect(value_ty, value_id);
   2653     const access: spec.MemoryAccess.Extended = .{ .@"volatile" = options.is_volatile };
   2654     try cg.body.emit(cg.module.gpa, .OpStore, .{
   2655         .pointer = ptr_id,
   2656         .object = indirect_value_id,
   2657         .memory_access = access,
   2658     });
   2659 }
   2660 
   2661 fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) !void {
   2662     for (body) |inst| {
   2663         try cg.genInst(inst);
   2664     }
   2665 }
   2666 
   2667 fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
   2668     const gpa = cg.module.gpa;
   2669     const zcu = cg.module.zcu;
   2670     const ip = &zcu.intern_pool;
   2671     if (cg.liveness.isUnused(inst) and !cg.air.mustLower(inst, ip))
   2672         return;
   2673 
   2674     const air_tags = cg.air.instructions.items(.tag);
   2675     const maybe_result_id: ?Id = switch (air_tags[@intFromEnum(inst)]) {
   2676         // zig fmt: off
   2677             .add, .add_wrap, .add_optimized => try cg.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),
   2678             .sub, .sub_wrap, .sub_optimized => try cg.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
   2679             .mul, .mul_wrap, .mul_optimized => try cg.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),
   2680 
   2681             .sqrt => try cg.airUnOpSimple(inst, .sqrt),
   2682             .sin => try cg.airUnOpSimple(inst, .sin),
   2683             .cos => try cg.airUnOpSimple(inst, .cos),
   2684             .tan => try cg.airUnOpSimple(inst, .tan),
   2685             .exp => try cg.airUnOpSimple(inst, .exp),
   2686             .exp2 => try cg.airUnOpSimple(inst, .exp2),
   2687             .log => try cg.airUnOpSimple(inst, .log),
   2688             .log2 => try cg.airUnOpSimple(inst, .log2),
   2689             .log10 => try cg.airUnOpSimple(inst, .log10),
   2690             .abs => try cg.airAbs(inst),
   2691             .floor => try cg.airUnOpSimple(inst, .floor),
   2692             .ceil => try cg.airUnOpSimple(inst, .ceil),
   2693             .round => try cg.airUnOpSimple(inst, .round),
   2694             .trunc_float => try cg.airUnOpSimple(inst, .trunc),
   2695             .neg, .neg_optimized => try cg.airUnOpSimple(inst, .f_neg),
   2696 
   2697             .div_float, .div_float_optimized => try cg.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv),
   2698             .div_floor, .div_floor_optimized => try cg.airDivFloor(inst),
   2699             .div_trunc, .div_trunc_optimized => try cg.airDivTrunc(inst),
   2700 
   2701             .rem, .rem_optimized => try cg.airArithOp(inst, .OpFRem, .OpSRem, .OpUMod),
   2702             .mod, .mod_optimized => try cg.airArithOp(inst, .OpFMod, .OpSMod, .OpUMod),
   2703 
   2704             .add_with_overflow => try cg.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),
   2705             .sub_with_overflow => try cg.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),
   2706             .mul_with_overflow => try cg.airMulOverflow(inst),
   2707             .shl_with_overflow => try cg.airShlOverflow(inst),
   2708 
   2709             .mul_add => try cg.airMulAdd(inst),
   2710 
   2711             .ctz => try cg.airClzCtz(inst, .ctz),
   2712             .clz => try cg.airClzCtz(inst, .clz),
   2713 
   2714             .select => try cg.airSelect(inst),
   2715 
   2716             .splat => try cg.airSplat(inst),
   2717             .reduce, .reduce_optimized => try cg.airReduce(inst),
   2718             .shuffle_one               => try cg.airShuffleOne(inst),
   2719             .shuffle_two               => try cg.airShuffleTwo(inst),
   2720 
   2721             .ptr_add => try cg.airPtrAdd(inst),
   2722             .ptr_sub => try cg.airPtrSub(inst),
   2723 
   2724             .bit_and  => try cg.airBinOpSimple(inst, .OpBitwiseAnd),
   2725             .bit_or   => try cg.airBinOpSimple(inst, .OpBitwiseOr),
   2726             .xor      => try cg.airBinOpSimple(inst, .OpBitwiseXor),
   2727             .bool_and => try cg.airBinOpSimple(inst, .OpLogicalAnd),
   2728             .bool_or  => try cg.airBinOpSimple(inst, .OpLogicalOr),
   2729 
   2730             .shl, .shl_exact => try cg.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical),
   2731             .shr, .shr_exact => try cg.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic),
   2732 
   2733             .min => try cg.airMinMax(inst, .min),
   2734             .max => try cg.airMinMax(inst, .max),
   2735 
   2736             .bitcast         => try cg.airBitCast(inst),
   2737             .intcast, .trunc => try cg.airIntCast(inst),
   2738             .float_from_int  => try cg.airFloatFromInt(inst),
   2739             .int_from_float  => try cg.airIntFromFloat(inst),
   2740             .fpext, .fptrunc => try cg.airFloatCast(inst),
   2741             .not             => try cg.airNot(inst),
   2742 
   2743             .array_to_slice => try cg.airArrayToSlice(inst),
   2744             .slice          => try cg.airSlice(inst),
   2745             .aggregate_init => try cg.airAggregateInit(inst),
   2746             .memcpy         => return cg.airMemcpy(inst),
   2747             .memmove        => return cg.airMemmove(inst),
   2748 
   2749             .slice_ptr      => try cg.airSliceField(inst, 0),
   2750             .slice_len      => try cg.airSliceField(inst, 1),
   2751             .slice_elem_ptr => try cg.airSliceElemPtr(inst),
   2752             .slice_elem_val => try cg.airSliceElemVal(inst),
   2753             .ptr_elem_ptr   => try cg.airPtrElemPtr(inst),
   2754             .ptr_elem_val   => try cg.airPtrElemVal(inst),
   2755             .array_elem_val => try cg.airArrayElemVal(inst),
   2756 
   2757             .set_union_tag => return cg.airSetUnionTag(inst),
   2758             .get_union_tag => try cg.airGetUnionTag(inst),
   2759             .union_init => try cg.airUnionInit(inst),
   2760 
   2761             .struct_field_val => try cg.airStructFieldVal(inst),
   2762             .field_parent_ptr => try cg.airFieldParentPtr(inst),
   2763 
   2764             .struct_field_ptr_index_0 => try cg.airStructFieldPtrIndex(inst, 0),
   2765             .struct_field_ptr_index_1 => try cg.airStructFieldPtrIndex(inst, 1),
   2766             .struct_field_ptr_index_2 => try cg.airStructFieldPtrIndex(inst, 2),
   2767             .struct_field_ptr_index_3 => try cg.airStructFieldPtrIndex(inst, 3),
   2768 
   2769             .cmp_eq     => try cg.airCmp(inst, .eq),
   2770             .cmp_neq    => try cg.airCmp(inst, .neq),
   2771             .cmp_gt     => try cg.airCmp(inst, .gt),
   2772             .cmp_gte    => try cg.airCmp(inst, .gte),
   2773             .cmp_lt     => try cg.airCmp(inst, .lt),
   2774             .cmp_lte    => try cg.airCmp(inst, .lte),
   2775             .cmp_vector => try cg.airVectorCmp(inst),
   2776 
   2777             .arg     => cg.airArg(),
   2778             .alloc   => try cg.airAlloc(inst),
   2779             // TODO: We probably need to have a special implementation of this for the C abi.
   2780             .ret_ptr => try cg.airAlloc(inst),
   2781             .block   => try cg.airBlock(inst),
   2782 
   2783             .load               => try cg.airLoad(inst),
   2784             .store, .store_safe => return cg.airStore(inst),
   2785 
   2786             .br             => return cg.airBr(inst),
   2787             // For now just ignore this instruction. This effectively falls back on the old implementation,
   2788             // this doesn't change anything for us.
   2789             .repeat         => return,
   2790             .breakpoint     => return,
   2791             .cond_br        => return cg.airCondBr(inst),
   2792             .loop           => return cg.airLoop(inst),
   2793             .ret            => return cg.airRet(inst),
   2794             .ret_safe       => return cg.airRet(inst), // TODO
   2795             .ret_load       => return cg.airRetLoad(inst),
   2796             .@"try"         => try cg.airTry(inst),
   2797             .switch_br      => return cg.airSwitchBr(inst),
   2798             .unreach, .trap => return cg.airUnreach(),
   2799 
   2800             .dbg_empty_stmt            => return,
   2801             .dbg_stmt                  => return cg.airDbgStmt(inst),
   2802             .dbg_inline_block          => try cg.airDbgInlineBlock(inst),
   2803             .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => return cg.airDbgVar(inst),
   2804 
   2805             .unwrap_errunion_err => try cg.airErrUnionErr(inst),
   2806             .unwrap_errunion_payload => try cg.airErrUnionPayload(inst),
   2807             .wrap_errunion_err => try cg.airWrapErrUnionErr(inst),
   2808             .wrap_errunion_payload => try cg.airWrapErrUnionPayload(inst),
   2809 
   2810             .is_null         => try cg.airIsNull(inst, false, .is_null),
   2811             .is_non_null     => try cg.airIsNull(inst, false, .is_non_null),
   2812             .is_null_ptr     => try cg.airIsNull(inst, true, .is_null),
   2813             .is_non_null_ptr => try cg.airIsNull(inst, true, .is_non_null),
   2814             .is_err          => try cg.airIsErr(inst, .is_err),
   2815             .is_non_err      => try cg.airIsErr(inst, .is_non_err),
   2816 
   2817             .optional_payload     => try cg.airUnwrapOptional(inst),
   2818             .optional_payload_ptr => try cg.airUnwrapOptionalPtr(inst),
   2819             .wrap_optional        => try cg.airWrapOptional(inst),
   2820 
   2821             .assembly => try cg.airAssembly(inst),
   2822 
   2823             .call              => try cg.airCall(inst, .auto),
   2824             .call_always_tail  => try cg.airCall(inst, .always_tail),
   2825             .call_never_tail   => try cg.airCall(inst, .never_tail),
   2826             .call_never_inline => try cg.airCall(inst, .never_inline),
   2827 
   2828             .work_item_id => try cg.airWorkItemId(inst),
   2829             .work_group_size => try cg.airWorkGroupSize(inst),
   2830             .work_group_id => try cg.airWorkGroupId(inst),
   2831 
   2832             // zig fmt: on
   2833 
   2834         else => |tag| return cg.todo("implement AIR tag {s}", .{@tagName(tag)}),
   2835     };
   2836 
   2837     const result_id = maybe_result_id orelse return;
   2838     try cg.inst_results.putNoClobber(gpa, inst, result_id);
   2839 }
   2840 
   2841 fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: Opcode) !?Id {
   2842     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2843     const lhs = try cg.temporary(bin_op.lhs);
   2844     const rhs = try cg.temporary(bin_op.rhs);
   2845 
   2846     const result = try cg.buildBinary(op, lhs, rhs);
   2847     return try result.materialize(cg);
   2848 }
   2849 
   2850 fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode) !?Id {
   2851     const zcu = cg.module.zcu;
   2852     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2853 
   2854     if (cg.typeOf(bin_op.lhs).isVector(zcu) and !cg.typeOf(bin_op.rhs).isVector(zcu)) {
   2855         return cg.fail("vector shift with scalar rhs", .{});
   2856     }
   2857 
   2858     const base = try cg.temporary(bin_op.lhs);
   2859     const shift = try cg.temporary(bin_op.rhs);
   2860 
   2861     const result_ty = cg.typeOfIndex(inst);
   2862 
   2863     const info = cg.arithmeticTypeInfo(result_ty);
   2864     switch (info.class) {
   2865         .composite_integer => return cg.todo("shift ops for composite integers", .{}),
   2866         .integer, .strange_integer => {},
   2867         .float, .bool => unreachable,
   2868     }
   2869 
   2870     // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
   2871     // so just manually upcast it if required.
   2872 
   2873     // Note: The sign may differ here between the shift and the base type, in case
   2874     // of an arithmetic right shift. SPIR-V still expects the same type,
   2875     // so in that case we have to cast convert to signed.
   2876     const casted_shift = try cg.buildConvert(base.ty.scalarType(zcu), shift);
   2877 
   2878     const shifted = switch (info.signedness) {
   2879         .unsigned => try cg.buildBinary(unsigned, base, casted_shift),
   2880         .signed => try cg.buildBinary(signed, base, casted_shift),
   2881     };
   2882 
   2883     const result = try cg.normalize(shifted, info);
   2884     return try result.materialize(cg);
   2885 }
   2886 
   2887 const MinMax = enum {
   2888     min,
   2889     max,
   2890 
   2891     pub fn extInstOpcode(
   2892         op: MinMax,
   2893         target: *const std.Target,
   2894         info: ArithmeticTypeInfo,
   2895     ) u32 {
   2896         return switch (target.os.tag) {
   2897             .opencl => @intFromEnum(@as(spec.OpenClOpcode, switch (info.class) {
   2898                 .float => switch (op) {
   2899                     .min => .fmin,
   2900                     .max => .fmax,
   2901                 },
   2902                 .integer, .strange_integer, .composite_integer => switch (info.signedness) {
   2903                     .signed => switch (op) {
   2904                         .min => .s_min,
   2905                         .max => .s_max,
   2906                     },
   2907                     .unsigned => switch (op) {
   2908                         .min => .u_min,
   2909                         .max => .u_max,
   2910                     },
   2911                 },
   2912                 .bool => unreachable,
   2913             })),
   2914             .vulkan, .opengl => @intFromEnum(@as(spec.GlslOpcode, switch (info.class) {
   2915                 .float => switch (op) {
   2916                     .min => .FMin,
   2917                     .max => .FMax,
   2918                 },
   2919                 .integer, .strange_integer, .composite_integer => switch (info.signedness) {
   2920                     .signed => switch (op) {
   2921                         .min => .SMin,
   2922                         .max => .SMax,
   2923                     },
   2924                     .unsigned => switch (op) {
   2925                         .min => .UMin,
   2926                         .max => .UMax,
   2927                     },
   2928                 },
   2929                 .bool => unreachable,
   2930             })),
   2931             else => unreachable,
   2932         };
   2933     }
   2934 };
   2935 
   2936 fn airMinMax(cg: *CodeGen, inst: Air.Inst.Index, op: MinMax) !?Id {
   2937     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   2938 
   2939     const lhs = try cg.temporary(bin_op.lhs);
   2940     const rhs = try cg.temporary(bin_op.rhs);
   2941 
   2942     const result = try cg.minMax(lhs, rhs, op);
   2943     return try result.materialize(cg);
   2944 }
   2945 
   2946 fn minMax(cg: *CodeGen, lhs: Temporary, rhs: Temporary, op: MinMax) !Temporary {
   2947     const zcu = cg.module.zcu;
   2948     const target = zcu.getTarget();
   2949     const info = cg.arithmeticTypeInfo(lhs.ty);
   2950 
   2951     const v = cg.vectorization(.{ lhs, rhs });
   2952     const ops = v.components();
   2953     const results = cg.module.allocIds(ops);
   2954 
   2955     const op_result_ty = lhs.ty.scalarType(zcu);
   2956     const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
   2957     const result_ty = try v.resultType(cg, lhs.ty);
   2958 
   2959     const op_lhs = try v.prepare(cg, lhs);
   2960     const op_rhs = try v.prepare(cg, rhs);
   2961 
   2962     const set = try cg.importExtendedSet();
   2963     const opcode = op.extInstOpcode(target, info);
   2964     for (0..ops) |i| {
   2965         try cg.body.emit(cg.module.gpa, .OpExtInst, .{
   2966             .id_result_type = op_result_ty_id,
   2967             .id_result = results.at(i),
   2968             .set = set,
   2969             .instruction = .{ .inst = opcode },
   2970             .id_ref_4 = &.{ op_lhs.at(i), op_rhs.at(i) },
   2971         });
   2972     }
   2973 
   2974     return v.finalize(result_ty, results);
   2975 }
   2976 
   2977 /// This function normalizes values to a canonical representation
   2978 /// after some arithmetic operation. This mostly consists of wrapping
   2979 /// behavior for strange integers:
   2980 /// - Unsigned integers are bitwise masked with a mask that only passes
   2981 ///   the valid bits through.
   2982 /// - Signed integers are also sign extended if they are negative.
   2983 /// All other values are returned unmodified (this makes strange integer
   2984 /// wrapping easier to use in generic operations).
   2985 fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporary {
   2986     const zcu = cg.module.zcu;
   2987     const ty = value.ty;
   2988     switch (info.class) {
   2989         .composite_integer, .integer, .bool, .float => return value,
   2990         .strange_integer => switch (info.signedness) {
   2991             .unsigned => {
   2992                 const mask_value = @as(u64, std.math.maxInt(u64)) >> @as(u6, @intCast(64 - info.bits));
   2993                 const mask_id = try cg.constInt(ty.scalarType(zcu), mask_value);
   2994                 return try cg.buildBinary(.OpBitwiseAnd, value, Temporary.init(ty.scalarType(zcu), mask_id));
   2995             },
   2996             .signed => {
   2997                 // Shift left and right so that we can copy the sight bit that way.
   2998                 const shift_amt_id = try cg.constInt(ty.scalarType(zcu), info.backing_bits - info.bits);
   2999                 const shift_amt: Temporary = .init(ty.scalarType(zcu), shift_amt_id);
   3000                 const left = try cg.buildBinary(.OpShiftLeftLogical, value, shift_amt);
   3001                 return try cg.buildBinary(.OpShiftRightArithmetic, left, shift_amt);
   3002             },
   3003         },
   3004     }
   3005 }
   3006 
   3007 fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3008     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   3009 
   3010     const lhs = try cg.temporary(bin_op.lhs);
   3011     const rhs = try cg.temporary(bin_op.rhs);
   3012 
   3013     const info = cg.arithmeticTypeInfo(lhs.ty);
   3014     switch (info.class) {
   3015         .composite_integer => unreachable, // TODO
   3016         .integer, .strange_integer => {
   3017             switch (info.signedness) {
   3018                 .unsigned => {
   3019                     const result = try cg.buildBinary(.OpUDiv, lhs, rhs);
   3020                     return try result.materialize(cg);
   3021                 },
   3022                 .signed => {},
   3023             }
   3024 
   3025             // For signed integers:
   3026             //   (a / b) - (a % b != 0 && a < 0 != b < 0);
   3027             // There shouldn't be any overflow issues.
   3028 
   3029             const div = try cg.buildBinary(.OpSDiv, lhs, rhs);
   3030             const rem = try cg.buildBinary(.OpSRem, lhs, rhs);
   3031             const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
   3032             const rem_non_zero = try cg.buildCmp(.OpINotEqual, rem, zero);
   3033             const lhs_rhs_xor = try cg.buildBinary(.OpBitwiseXor, lhs, rhs);
   3034             const signs_differ = try cg.buildCmp(.OpSLessThan, lhs_rhs_xor, zero);
   3035             const adjust = try cg.buildBinary(.OpLogicalAnd, rem_non_zero, signs_differ);
   3036             const result = try cg.buildBinary(.OpISub, div, try cg.intFromBool(adjust, div.ty));
   3037             return try result.materialize(cg);
   3038         },
   3039         .float => {
   3040             const div = try cg.buildBinary(.OpFDiv, lhs, rhs);
   3041             const result = try cg.buildUnary(.floor, div);
   3042             return try result.materialize(cg);
   3043         },
   3044         .bool => unreachable,
   3045     }
   3046 }
   3047 
   3048 fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3049     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   3050     const lhs = try cg.temporary(bin_op.lhs);
   3051     const rhs = try cg.temporary(bin_op.rhs);
   3052     const info = cg.arithmeticTypeInfo(lhs.ty);
   3053     switch (info.class) {
   3054         .composite_integer => unreachable, // TODO
   3055         .integer, .strange_integer => switch (info.signedness) {
   3056             .unsigned => {
   3057                 const result = try cg.buildBinary(.OpUDiv, lhs, rhs);
   3058                 return try result.materialize(cg);
   3059             },
   3060             .signed => {
   3061                 const result = try cg.buildBinary(.OpSDiv, lhs, rhs);
   3062                 return try result.materialize(cg);
   3063             },
   3064         },
   3065         .float => {
   3066             const div = try cg.buildBinary(.OpFDiv, lhs, rhs);
   3067             const result = try cg.buildUnary(.trunc, div);
   3068             return try result.materialize(cg);
   3069         },
   3070         .bool => unreachable,
   3071     }
   3072 }
   3073 
   3074 fn airUnOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: UnaryOp) !?Id {
   3075     const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   3076     const operand = try cg.temporary(un_op);
   3077     const result = try cg.buildUnary(op, operand);
   3078     return try result.materialize(cg);
   3079 }
   3080 
   3081 fn airArithOp(
   3082     cg: *CodeGen,
   3083     inst: Air.Inst.Index,
   3084     comptime fop: Opcode,
   3085     comptime sop: Opcode,
   3086     comptime uop: Opcode,
   3087 ) !?Id {
   3088     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   3089     const lhs = try cg.temporary(bin_op.lhs);
   3090     const rhs = try cg.temporary(bin_op.rhs);
   3091     const info = cg.arithmeticTypeInfo(lhs.ty);
   3092     const result = switch (info.class) {
   3093         .composite_integer => unreachable, // TODO
   3094         .integer, .strange_integer => switch (info.signedness) {
   3095             .signed => try cg.buildBinary(sop, lhs, rhs),
   3096             .unsigned => try cg.buildBinary(uop, lhs, rhs),
   3097         },
   3098         .float => try cg.buildBinary(fop, lhs, rhs),
   3099         .bool => unreachable,
   3100     };
   3101     return try result.materialize(cg);
   3102 }
   3103 
   3104 fn airAbs(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3105     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   3106     const operand = try cg.temporary(ty_op.operand);
   3107     // Note: operand_ty may be signed, while ty is always unsigned!
   3108     const result_ty = cg.typeOfIndex(inst);
   3109     const result = try cg.abs(result_ty, operand);
   3110     return try result.materialize(cg);
   3111 }
   3112 
   3113 fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary {
   3114     const zcu = cg.module.zcu;
   3115     const target = cg.module.zcu.getTarget();
   3116     const operand_info = cg.arithmeticTypeInfo(value.ty);
   3117     switch (operand_info.class) {
   3118         .float => return try cg.buildUnary(.f_abs, value),
   3119         .integer, .strange_integer => {
   3120             const abs_value = try cg.buildUnary(.i_abs, value);
   3121             switch (target.os.tag) {
   3122                 .vulkan, .opengl => {
   3123                     if (value.ty.intInfo(zcu).signedness == .signed) {
   3124                         return cg.todo("perform bitcast after @abs", .{});
   3125                     }
   3126                 },
   3127                 else => {},
   3128             }
   3129             return try cg.normalize(abs_value, cg.arithmeticTypeInfo(result_ty));
   3130         },
   3131         .composite_integer => unreachable, // TODO
   3132         .bool => unreachable,
   3133     }
   3134 }
   3135 
   3136 fn airAddSubOverflow(
   3137     cg: *CodeGen,
   3138     inst: Air.Inst.Index,
   3139     comptime add: Opcode,
   3140     u_opcode: Opcode,
   3141     s_opcode: Opcode,
   3142 ) !?Id {
   3143     // Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers,
   3144     // there is in both cases only one extra operation required. For signed operations,
   3145     // the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually
   3146     // normally set a carry bit. So the SPIR-V overflow operations are not particularly
   3147     // useful here.
   3148 
   3149     _ = s_opcode;
   3150 
   3151     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3152     const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
   3153     const lhs = try cg.temporary(extra.lhs);
   3154     const rhs = try cg.temporary(extra.rhs);
   3155     const result_ty = cg.typeOfIndex(inst);
   3156 
   3157     const info = cg.arithmeticTypeInfo(lhs.ty);
   3158     switch (info.class) {
   3159         .composite_integer => unreachable, // TODO
   3160         .strange_integer, .integer => {},
   3161         .float, .bool => unreachable,
   3162     }
   3163 
   3164     const sum = try cg.buildBinary(add, lhs, rhs);
   3165     const result = try cg.normalize(sum, info);
   3166     const overflowed = switch (info.signedness) {
   3167         // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.
   3168         // For subtraction the conditions need to be swapped.
   3169         .unsigned => try cg.buildCmp(u_opcode, result, lhs),
   3170         // For signed operations, we check the signs of the operands and the result.
   3171         .signed => blk: {
   3172             // Signed overflow detection using the sign bits of the operands and the result.
   3173             // For addition (a + b), overflow occurs if the operands have the same sign
   3174             // and the result's sign is different from the operands' sign.
   3175             //   (sign(a) == sign(b)) && (sign(a) != sign(result))
   3176             // For subtraction (a - b), overflow occurs if the operands have different signs
   3177             // and the result's sign is different from the minuend's (a's) sign.
   3178             //   (sign(a) != sign(b)) && (sign(a) != sign(result))
   3179             const zero: Temporary = .init(rhs.ty, try cg.constInt(rhs.ty, 0));
   3180             const lhs_is_neg = try cg.buildCmp(.OpSLessThan, lhs, zero);
   3181             const rhs_is_neg = try cg.buildCmp(.OpSLessThan, rhs, zero);
   3182             const result_is_neg = try cg.buildCmp(.OpSLessThan, result, zero);
   3183             const signs_match = try cg.buildCmp(.OpLogicalEqual, lhs_is_neg, rhs_is_neg);
   3184             const result_sign_differs = try cg.buildCmp(.OpLogicalNotEqual, lhs_is_neg, result_is_neg);
   3185             const overflow_condition = switch (add) {
   3186                 .OpIAdd => signs_match,
   3187                 .OpISub => try cg.buildUnary(.l_not, signs_match),
   3188                 else => unreachable,
   3189             };
   3190             break :blk try cg.buildCmp(.OpLogicalAnd, overflow_condition, result_sign_differs);
   3191         },
   3192     };
   3193 
   3194     const ov = try cg.intFromBool(overflowed, .u1);
   3195     const result_ty_id = try cg.resolveType(result_ty, .direct);
   3196     return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) });
   3197 }
   3198 
   3199 fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3200     const pt = cg.pt;
   3201     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3202     const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
   3203     const lhs = try cg.temporary(extra.lhs);
   3204     const rhs = try cg.temporary(extra.rhs);
   3205     const result_ty = cg.typeOfIndex(inst);
   3206 
   3207     const info = cg.arithmeticTypeInfo(lhs.ty);
   3208     switch (info.class) {
   3209         .composite_integer => unreachable, // TODO
   3210         .strange_integer, .integer => {},
   3211         .float, .bool => unreachable,
   3212     }
   3213 
   3214     // There are 3 cases which we have to deal with:
   3215     // - If info.bits < 32 / 2, we will upcast to 32 and check the higher bits
   3216     // - If info.bits > 32 / 2, we have to use extended multiplication
   3217     // - Additionally, if info.bits != 32, we'll have to check the high bits
   3218     //   of the result too.
   3219 
   3220     const largest_int_bits = cg.largestSupportedIntBits();
   3221     // If non-null, the number of bits that the multiplication should be performed in. If
   3222     // null, we have to use wide multiplication.
   3223     const maybe_op_ty_bits: ?u16 = switch (info.bits) {
   3224         0 => unreachable,
   3225         1...16 => 32,
   3226         17...32 => if (largest_int_bits > 32) 64 else null, // Upcast if we can.
   3227         33...64 => null, // Always use wide multiplication.
   3228         else => unreachable, // TODO: Composite integers
   3229     };
   3230 
   3231     const result, const overflowed = switch (info.signedness) {
   3232         .unsigned => blk: {
   3233             if (maybe_op_ty_bits) |op_ty_bits| {
   3234                 const op_ty = try pt.intType(.unsigned, op_ty_bits);
   3235                 const casted_lhs = try cg.buildConvert(op_ty, lhs);
   3236                 const casted_rhs = try cg.buildConvert(op_ty, rhs);
   3237                 const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs);
   3238                 const low_bits = try cg.buildConvert(lhs.ty, full_result);
   3239                 const result = try cg.normalize(low_bits, info);
   3240                 // Shift the result bits away to get the overflow bits.
   3241                 const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits));
   3242                 const overflow = try cg.buildBinary(.OpShiftRightLogical, full_result, shift);
   3243                 // Directly check if its zero in the op_ty without converting first.
   3244                 const zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));
   3245                 const overflowed = try cg.buildCmp(.OpINotEqual, zero, overflow);
   3246                 break :blk .{ result, overflowed };
   3247             }
   3248 
   3249             const low_bits, const high_bits = try cg.buildWideMul(.unsigned, lhs, rhs);
   3250 
   3251             // Truncate the result, if required.
   3252             const result = try cg.normalize(low_bits, info);
   3253 
   3254             // Overflow happened if the high-bits of the result are non-zero OR if the
   3255             // high bits of the low word of the result (those outside the range of the
   3256             // int) are nonzero.
   3257             const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
   3258             const high_overflowed = try cg.buildCmp(.OpINotEqual, zero, high_bits);
   3259 
   3260             // If no overflow bits in low_bits, no extra work needs to be done.
   3261             if (info.backing_bits == info.bits) break :blk .{ result, high_overflowed };
   3262 
   3263             // Shift the result bits away to get the overflow bits.
   3264             const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits));
   3265             const low_overflow = try cg.buildBinary(.OpShiftRightLogical, low_bits, shift);
   3266             const low_overflowed = try cg.buildCmp(.OpINotEqual, zero, low_overflow);
   3267 
   3268             const overflowed = try cg.buildCmp(.OpLogicalOr, low_overflowed, high_overflowed);
   3269 
   3270             break :blk .{ result, overflowed };
   3271         },
   3272         .signed => blk: {
   3273             // - lhs >= 0, rhxs >= 0: expect positive; overflow should be  0
   3274             // - lhs == 0          : expect positive; overflow should be  0
   3275             // -           rhs == 0: expect positive; overflow should be  0
   3276             // - lhs  > 0, rhs  < 0: expect negative; overflow should be -1
   3277             // - lhs  < 0, rhs  > 0: expect negative; overflow should be -1
   3278             // - lhs <= 0, rhs <= 0: expect positive; overflow should be  0
   3279             // ------
   3280             // overflow should be -1 when
   3281             //   (lhs > 0 && rhs < 0) || (lhs < 0 && rhs > 0)
   3282 
   3283             const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
   3284             const lhs_negative = try cg.buildCmp(.OpSLessThan, lhs, zero);
   3285             const rhs_negative = try cg.buildCmp(.OpSLessThan, rhs, zero);
   3286             const lhs_positive = try cg.buildCmp(.OpSGreaterThan, lhs, zero);
   3287             const rhs_positive = try cg.buildCmp(.OpSGreaterThan, rhs, zero);
   3288 
   3289             // Set to `true` if we expect -1.
   3290             const expected_overflow_bit = try cg.buildBinary(
   3291                 .OpLogicalOr,
   3292                 try cg.buildCmp(.OpLogicalAnd, lhs_positive, rhs_negative),
   3293                 try cg.buildCmp(.OpLogicalAnd, lhs_negative, rhs_positive),
   3294             );
   3295 
   3296             if (maybe_op_ty_bits) |op_ty_bits| {
   3297                 const op_ty = try pt.intType(.signed, op_ty_bits);
   3298                 // Assume normalized; sign bit is set. We want a sign extend.
   3299                 const casted_lhs = try cg.buildConvert(op_ty, lhs);
   3300                 const casted_rhs = try cg.buildConvert(op_ty, rhs);
   3301 
   3302                 const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs);
   3303 
   3304                 // Truncate to the result type.
   3305                 const low_bits = try cg.buildConvert(lhs.ty, full_result);
   3306                 const result = try cg.normalize(low_bits, info);
   3307 
   3308                 // Now, we need to check the overflow bits AND the sign
   3309                 // bit for the expected overflow bits.
   3310                 // To do that, shift out everything bit the sign bit and
   3311                 // then check what remains.
   3312                 const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits - 1));
   3313                 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
   3314                 // for negative cases.
   3315                 const overflow = try cg.buildBinary(.OpShiftRightArithmetic, full_result, shift);
   3316 
   3317                 const long_all_set: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, -1));
   3318                 const long_zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));
   3319                 const mask = try cg.buildSelect(expected_overflow_bit, long_all_set, long_zero);
   3320 
   3321                 const overflowed = try cg.buildCmp(.OpINotEqual, mask, overflow);
   3322 
   3323                 break :blk .{ result, overflowed };
   3324             }
   3325 
   3326             const low_bits, const high_bits = try cg.buildWideMul(.signed, lhs, rhs);
   3327 
   3328             // Truncate result if required.
   3329             const result = try cg.normalize(low_bits, info);
   3330 
   3331             const all_set: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, -1));
   3332             const mask = try cg.buildSelect(expected_overflow_bit, all_set, zero);
   3333 
   3334             // Like with unsigned, overflow happened if high_bits are not the ones we expect,
   3335             // and we also need to check some ones from the low bits.
   3336 
   3337             const high_overflowed = try cg.buildCmp(.OpINotEqual, mask, high_bits);
   3338 
   3339             // If no overflow bits in low_bits, no extra work needs to be done.
   3340             // Careful, we still have to check the sign bit, so this branch
   3341             // only goes for i33 and such.
   3342             if (info.backing_bits == info.bits + 1) break :blk .{ result, high_overflowed };
   3343 
   3344             // Shift the result bits away to get the overflow bits.
   3345             const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits - 1));
   3346             // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
   3347             // for negative cases.
   3348             const low_overflow = try cg.buildBinary(.OpShiftRightArithmetic, low_bits, shift);
   3349             const low_overflowed = try cg.buildCmp(.OpINotEqual, mask, low_overflow);
   3350 
   3351             const overflowed = try cg.buildCmp(.OpLogicalOr, low_overflowed, high_overflowed);
   3352 
   3353             break :blk .{ result, overflowed };
   3354         },
   3355     };
   3356 
   3357     const ov = try cg.intFromBool(overflowed, .u1);
   3358 
   3359     const result_ty_id = try cg.resolveType(result_ty, .direct);
   3360     return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) });
   3361 }
   3362 
   3363 fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3364     const zcu = cg.module.zcu;
   3365 
   3366     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3367     const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
   3368 
   3369     if (cg.typeOf(extra.lhs).isVector(zcu) and !cg.typeOf(extra.rhs).isVector(zcu)) {
   3370         return cg.fail("vector shift with scalar rhs", .{});
   3371     }
   3372 
   3373     const base = try cg.temporary(extra.lhs);
   3374     const shift = try cg.temporary(extra.rhs);
   3375 
   3376     const result_ty = cg.typeOfIndex(inst);
   3377 
   3378     const info = cg.arithmeticTypeInfo(base.ty);
   3379     switch (info.class) {
   3380         .composite_integer => unreachable, // TODO
   3381         .integer, .strange_integer => {},
   3382         .float, .bool => unreachable,
   3383     }
   3384 
   3385     // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
   3386     // so just manually upcast it if required.
   3387     const casted_shift = try cg.buildConvert(base.ty.scalarType(zcu), shift);
   3388 
   3389     const left = try cg.buildBinary(.OpShiftLeftLogical, base, casted_shift);
   3390     const result = try cg.normalize(left, info);
   3391 
   3392     const right = switch (info.signedness) {
   3393         .unsigned => try cg.buildBinary(.OpShiftRightLogical, result, casted_shift),
   3394         .signed => try cg.buildBinary(.OpShiftRightArithmetic, result, casted_shift),
   3395     };
   3396 
   3397     const overflowed = try cg.buildCmp(.OpINotEqual, base, right);
   3398     const ov = try cg.intFromBool(overflowed, .u1);
   3399 
   3400     const result_ty_id = try cg.resolveType(result_ty, .direct);
   3401     return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) });
   3402 }
   3403 
   3404 fn airMulAdd(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3405     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   3406     const extra = cg.air.extraData(Air.Bin, pl_op.payload).data;
   3407 
   3408     const a = try cg.temporary(extra.lhs);
   3409     const b = try cg.temporary(extra.rhs);
   3410     const c = try cg.temporary(pl_op.operand);
   3411 
   3412     const result_ty = cg.typeOfIndex(inst);
   3413     const info = cg.arithmeticTypeInfo(result_ty);
   3414     assert(info.class == .float); // .mul_add is only emitted for floats
   3415 
   3416     const result = try cg.buildFma(a, b, c);
   3417     return try result.materialize(cg);
   3418 }
   3419 
   3420 fn airClzCtz(cg: *CodeGen, inst: Air.Inst.Index, op: UnaryOp) !?Id {
   3421     if (cg.liveness.isUnused(inst)) return null;
   3422 
   3423     const zcu = cg.module.zcu;
   3424     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   3425     const operand = try cg.temporary(ty_op.operand);
   3426 
   3427     const scalar_result_ty = cg.typeOfIndex(inst).scalarType(zcu);
   3428 
   3429     const info = cg.arithmeticTypeInfo(operand.ty);
   3430     switch (info.class) {
   3431         .composite_integer => unreachable, // TODO
   3432         .integer, .strange_integer => {},
   3433         .float, .bool => unreachable,
   3434     }
   3435 
   3436     const count = try cg.buildUnary(op, operand);
   3437 
   3438     // Result of OpenCL ctz/clz returns operand.ty, and we want result_ty.
   3439     // result_ty is always large enough to hold the result, so we might have to down
   3440     // cast it.
   3441     const result = try cg.buildConvert(scalar_result_ty, count);
   3442     return try result.materialize(cg);
   3443 }
   3444 
   3445 fn airSelect(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3446     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   3447     const extra = cg.air.extraData(Air.Bin, pl_op.payload).data;
   3448     const pred = try cg.temporary(pl_op.operand);
   3449     const a = try cg.temporary(extra.lhs);
   3450     const b = try cg.temporary(extra.rhs);
   3451 
   3452     const result = try cg.buildSelect(pred, a, b);
   3453     return try result.materialize(cg);
   3454 }
   3455 
   3456 fn airSplat(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3457     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   3458 
   3459     const operand_id = try cg.resolve(ty_op.operand);
   3460     const result_ty = cg.typeOfIndex(inst);
   3461 
   3462     return try cg.constructCompositeSplat(result_ty, operand_id);
   3463 }
   3464 
   3465 fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3466     const zcu = cg.module.zcu;
   3467     const reduce = cg.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
   3468     const operand = try cg.resolve(reduce.operand);
   3469     const operand_ty = cg.typeOf(reduce.operand);
   3470     const scalar_ty = operand_ty.scalarType(zcu);
   3471     const scalar_ty_id = try cg.resolveType(scalar_ty, .direct);
   3472     const info = cg.arithmeticTypeInfo(operand_ty);
   3473     const len = operand_ty.vectorLen(zcu);
   3474     const first = try cg.extractVectorComponent(scalar_ty, operand, 0);
   3475 
   3476     switch (reduce.operation) {
   3477         .Min, .Max => |op| {
   3478             var result: Temporary = .init(scalar_ty, first);
   3479             const cmp_op: MinMax = switch (op) {
   3480                 .Max => .max,
   3481                 .Min => .min,
   3482                 else => unreachable,
   3483             };
   3484             for (1..len) |i| {
   3485                 const lhs = result;
   3486                 const rhs_id = try cg.extractVectorComponent(scalar_ty, operand, @intCast(i));
   3487                 const rhs: Temporary = .init(scalar_ty, rhs_id);
   3488 
   3489                 result = try cg.minMax(lhs, rhs, cmp_op);
   3490             }
   3491 
   3492             return try result.materialize(cg);
   3493         },
   3494         else => {},
   3495     }
   3496 
   3497     var result_id = first;
   3498 
   3499     const opcode: Opcode = switch (info.class) {
   3500         .bool => switch (reduce.operation) {
   3501             .And => .OpLogicalAnd,
   3502             .Or => .OpLogicalOr,
   3503             .Xor => .OpLogicalNotEqual,
   3504             else => unreachable,
   3505         },
   3506         .strange_integer, .integer => switch (reduce.operation) {
   3507             .And => .OpBitwiseAnd,
   3508             .Or => .OpBitwiseOr,
   3509             .Xor => .OpBitwiseXor,
   3510             .Add => .OpIAdd,
   3511             .Mul => .OpIMul,
   3512             else => unreachable,
   3513         },
   3514         .float => switch (reduce.operation) {
   3515             .Add => .OpFAdd,
   3516             .Mul => .OpFMul,
   3517             else => unreachable,
   3518         },
   3519         .composite_integer => unreachable, // TODO
   3520     };
   3521 
   3522     for (1..len) |i| {
   3523         const lhs = result_id;
   3524         const rhs = try cg.extractVectorComponent(scalar_ty, operand, @intCast(i));
   3525         result_id = cg.module.allocId();
   3526 
   3527         try cg.body.emitRaw(cg.module.gpa, opcode, 4);
   3528         cg.body.writeOperand(Id, scalar_ty_id);
   3529         cg.body.writeOperand(Id, result_id);
   3530         cg.body.writeOperand(Id, lhs);
   3531         cg.body.writeOperand(Id, rhs);
   3532     }
   3533 
   3534     return result_id;
   3535 }
   3536 
   3537 fn airShuffleOne(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3538     const zcu = cg.module.zcu;
   3539     const gpa = zcu.gpa;
   3540 
   3541     const unwrapped = cg.air.unwrapShuffleOne(zcu, inst);
   3542     const mask = unwrapped.mask;
   3543     const result_ty = unwrapped.result_ty;
   3544     const elem_ty = result_ty.childType(zcu);
   3545     const operand = try cg.resolve(unwrapped.operand);
   3546 
   3547     const scratch_top = cg.id_scratch.items.len;
   3548     defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   3549     const constituents = try cg.id_scratch.addManyAsSlice(gpa, mask.len);
   3550 
   3551     for (constituents, mask) |*id, mask_elem| {
   3552         id.* = switch (mask_elem.unwrap()) {
   3553             .elem => |idx| try cg.extractVectorComponent(elem_ty, operand, idx),
   3554             .value => |val| try cg.constant(elem_ty, .fromInterned(val), .direct),
   3555         };
   3556     }
   3557 
   3558     const result_ty_id = try cg.resolveType(result_ty, .direct);
   3559     return try cg.constructComposite(result_ty_id, constituents);
   3560 }
   3561 
   3562 fn airShuffleTwo(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3563     const zcu = cg.module.zcu;
   3564     const gpa = zcu.gpa;
   3565 
   3566     const unwrapped = cg.air.unwrapShuffleTwo(zcu, inst);
   3567     const mask = unwrapped.mask;
   3568     const result_ty = unwrapped.result_ty;
   3569     const elem_ty = result_ty.childType(zcu);
   3570     const elem_ty_id = try cg.resolveType(elem_ty, .direct);
   3571     const operand_a = try cg.resolve(unwrapped.operand_a);
   3572     const operand_b = try cg.resolve(unwrapped.operand_b);
   3573 
   3574     const scratch_top = cg.id_scratch.items.len;
   3575     defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   3576     const constituents = try cg.id_scratch.addManyAsSlice(gpa, mask.len);
   3577 
   3578     for (constituents, mask) |*id, mask_elem| {
   3579         id.* = switch (mask_elem.unwrap()) {
   3580             .a_elem => |idx| try cg.extractVectorComponent(elem_ty, operand_a, idx),
   3581             .b_elem => |idx| try cg.extractVectorComponent(elem_ty, operand_b, idx),
   3582             .undef => try cg.module.constUndef(elem_ty_id),
   3583         };
   3584     }
   3585 
   3586     const result_ty_id = try cg.resolveType(result_ty, .direct);
   3587     return try cg.constructComposite(result_ty_id, constituents);
   3588 }
   3589 
   3590 fn accessChainId(
   3591     cg: *CodeGen,
   3592     result_ty_id: Id,
   3593     base: Id,
   3594     indices: []const Id,
   3595 ) !Id {
   3596     const result_id = cg.module.allocId();
   3597     try cg.body.emit(cg.module.gpa, .OpInBoundsAccessChain, .{
   3598         .id_result_type = result_ty_id,
   3599         .id_result = result_id,
   3600         .base = base,
   3601         .indexes = indices,
   3602     });
   3603     return result_id;
   3604 }
   3605 
   3606 /// AccessChain is essentially PtrAccessChain with 0 as initial argument. The effective
   3607 /// difference lies in whether the resulting type of the first dereference will be the
   3608 /// same as that of the base pointer, or that of a dereferenced base pointer. AccessChain
   3609 /// is the latter and PtrAccessChain is the former.
   3610 fn accessChain(
   3611     cg: *CodeGen,
   3612     result_ty_id: Id,
   3613     base: Id,
   3614     indices: []const u32,
   3615 ) !Id {
   3616     const gpa = cg.module.gpa;
   3617     const scratch_top = cg.id_scratch.items.len;
   3618     defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   3619     const ids = try cg.id_scratch.addManyAsSlice(gpa, indices.len);
   3620     for (indices, ids) |index, *id| {
   3621         id.* = try cg.constInt(.u32, index);
   3622     }
   3623     return try cg.accessChainId(result_ty_id, base, ids);
   3624 }
   3625 
   3626 fn ptrAccessChain(
   3627     cg: *CodeGen,
   3628     result_ty_id: Id,
   3629     base: Id,
   3630     element: Id,
   3631     indices: []const u32,
   3632 ) !Id {
   3633     const gpa = cg.module.gpa;
   3634     const target = cg.module.zcu.getTarget();
   3635 
   3636     const scratch_top = cg.id_scratch.items.len;
   3637     defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   3638     const ids = try cg.id_scratch.addManyAsSlice(gpa, indices.len);
   3639     for (indices, ids) |index, *id| {
   3640         id.* = try cg.constInt(.u32, index);
   3641     }
   3642 
   3643     const result_id = cg.module.allocId();
   3644     switch (target.os.tag) {
   3645         .opencl, .amdhsa => {
   3646             try cg.body.emit(gpa, .OpInBoundsPtrAccessChain, .{
   3647                 .id_result_type = result_ty_id,
   3648                 .id_result = result_id,
   3649                 .base = base,
   3650                 .element = element,
   3651                 .indexes = ids,
   3652             });
   3653         },
   3654         .vulkan, .opengl => {
   3655             try cg.body.emit(gpa, .OpPtrAccessChain, .{
   3656                 .id_result_type = result_ty_id,
   3657                 .id_result = result_id,
   3658                 .base = base,
   3659                 .element = element,
   3660                 .indexes = ids,
   3661             });
   3662         },
   3663         else => unreachable,
   3664     }
   3665     return result_id;
   3666 }
   3667 
   3668 fn ptrAdd(cg: *CodeGen, result_ty: Type, ptr_ty: Type, ptr_id: Id, offset_id: Id) !Id {
   3669     const zcu = cg.module.zcu;
   3670     const result_ty_id = try cg.resolveType(result_ty, .direct);
   3671 
   3672     switch (ptr_ty.ptrSize(zcu)) {
   3673         .one => {
   3674             // Pointer to array
   3675             // TODO: Is this correct?
   3676             return try cg.accessChainId(result_ty_id, ptr_id, &.{offset_id});
   3677         },
   3678         .c, .many => {
   3679             return try cg.ptrAccessChain(result_ty_id, ptr_id, offset_id, &.{});
   3680         },
   3681         .slice => {
   3682             // TODO: This is probably incorrect. A slice should be returned here, though this is what llvm does.
   3683             const slice_ptr_id = try cg.extractField(result_ty, ptr_id, 0);
   3684             return try cg.ptrAccessChain(result_ty_id, slice_ptr_id, offset_id, &.{});
   3685         },
   3686     }
   3687 }
   3688 
   3689 fn airPtrAdd(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3690     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3691     const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
   3692     const ptr_id = try cg.resolve(bin_op.lhs);
   3693     const offset_id = try cg.resolve(bin_op.rhs);
   3694     const ptr_ty = cg.typeOf(bin_op.lhs);
   3695     const result_ty = cg.typeOfIndex(inst);
   3696 
   3697     return try cg.ptrAdd(result_ty, ptr_ty, ptr_id, offset_id);
   3698 }
   3699 
   3700 fn airPtrSub(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3701     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3702     const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
   3703     const ptr_id = try cg.resolve(bin_op.lhs);
   3704     const ptr_ty = cg.typeOf(bin_op.lhs);
   3705     const offset_id = try cg.resolve(bin_op.rhs);
   3706     const offset_ty = cg.typeOf(bin_op.rhs);
   3707     const offset_ty_id = try cg.resolveType(offset_ty, .direct);
   3708     const result_ty = cg.typeOfIndex(inst);
   3709 
   3710     const negative_offset_id = cg.module.allocId();
   3711     try cg.body.emit(cg.module.gpa, .OpSNegate, .{
   3712         .id_result_type = offset_ty_id,
   3713         .id_result = negative_offset_id,
   3714         .operand = offset_id,
   3715     });
   3716     return try cg.ptrAdd(result_ty, ptr_ty, ptr_id, negative_offset_id);
   3717 }
   3718 
   3719 fn cmp(
   3720     cg: *CodeGen,
   3721     op: std.math.CompareOperator,
   3722     lhs: Temporary,
   3723     rhs: Temporary,
   3724 ) !Temporary {
   3725     const gpa = cg.module.gpa;
   3726     const pt = cg.pt;
   3727     const zcu = cg.module.zcu;
   3728     const ip = &zcu.intern_pool;
   3729     const scalar_ty = lhs.ty.scalarType(zcu);
   3730     const is_vector = lhs.ty.isVector(zcu);
   3731 
   3732     switch (scalar_ty.zigTypeTag(zcu)) {
   3733         .int, .bool, .float => {},
   3734         .@"enum" => {
   3735             assert(!is_vector);
   3736             const ty = lhs.ty.intTagType(zcu);
   3737             return try cg.cmp(op, lhs.pun(ty), rhs.pun(ty));
   3738         },
   3739         .@"struct" => {
   3740             const struct_ty = zcu.typeToPackedStruct(scalar_ty).?;
   3741             const ty: Type = .fromInterned(struct_ty.backingIntTypeUnordered(ip));
   3742             return try cg.cmp(op, lhs.pun(ty), rhs.pun(ty));
   3743         },
   3744         .error_set => {
   3745             assert(!is_vector);
   3746             const err_int_ty = try pt.errorIntType();
   3747             return try cg.cmp(op, lhs.pun(err_int_ty), rhs.pun(err_int_ty));
   3748         },
   3749         .pointer => {
   3750             assert(!is_vector);
   3751             // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are
   3752             // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using
   3753             // OpConvertPtrToU...
   3754 
   3755             const usize_ty_id = try cg.resolveType(.usize, .direct);
   3756 
   3757             const lhs_int_id = cg.module.allocId();
   3758             try cg.body.emit(gpa, .OpConvertPtrToU, .{
   3759                 .id_result_type = usize_ty_id,
   3760                 .id_result = lhs_int_id,
   3761                 .pointer = try lhs.materialize(cg),
   3762             });
   3763 
   3764             const rhs_int_id = cg.module.allocId();
   3765             try cg.body.emit(gpa, .OpConvertPtrToU, .{
   3766                 .id_result_type = usize_ty_id,
   3767                 .id_result = rhs_int_id,
   3768                 .pointer = try rhs.materialize(cg),
   3769             });
   3770 
   3771             const lhs_int: Temporary = .init(.usize, lhs_int_id);
   3772             const rhs_int: Temporary = .init(.usize, rhs_int_id);
   3773             return try cg.cmp(op, lhs_int, rhs_int);
   3774         },
   3775         .optional => {
   3776             assert(!is_vector);
   3777 
   3778             const ty = lhs.ty;
   3779 
   3780             const payload_ty = ty.optionalChild(zcu);
   3781             if (ty.optionalReprIsPayload(zcu)) {
   3782                 assert(payload_ty.hasRuntimeBitsIgnoreComptime(zcu));
   3783                 assert(!payload_ty.isSlice(zcu));
   3784 
   3785                 return try cg.cmp(op, lhs.pun(payload_ty), rhs.pun(payload_ty));
   3786             }
   3787 
   3788             const lhs_id = try lhs.materialize(cg);
   3789             const rhs_id = try rhs.materialize(cg);
   3790 
   3791             const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu))
   3792                 try cg.extractField(.bool, lhs_id, 1)
   3793             else
   3794                 try cg.convertToDirect(.bool, lhs_id);
   3795 
   3796             const rhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu))
   3797                 try cg.extractField(.bool, rhs_id, 1)
   3798             else
   3799                 try cg.convertToDirect(.bool, rhs_id);
   3800 
   3801             const lhs_valid: Temporary = .init(.bool, lhs_valid_id);
   3802             const rhs_valid: Temporary = .init(.bool, rhs_valid_id);
   3803 
   3804             if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   3805                 return try cg.cmp(op, lhs_valid, rhs_valid);
   3806             }
   3807 
   3808             // a = lhs_valid
   3809             // b = rhs_valid
   3810             // c = lhs_pl == rhs_pl
   3811             //
   3812             // For op == .eq we have:
   3813             //   a == b && a -> c
   3814             // = a == b && (!a || c)
   3815             //
   3816             // For op == .neq we have
   3817             //   a == b && a -> c
   3818             // = !(a == b && a -> c)
   3819             // = a != b || !(a -> c
   3820             // = a != b || !(!a || c)
   3821             // = a != b || a && !c
   3822 
   3823             const lhs_pl_id = try cg.extractField(payload_ty, lhs_id, 0);
   3824             const rhs_pl_id = try cg.extractField(payload_ty, rhs_id, 0);
   3825 
   3826             const lhs_pl: Temporary = .init(payload_ty, lhs_pl_id);
   3827             const rhs_pl: Temporary = .init(payload_ty, rhs_pl_id);
   3828 
   3829             return switch (op) {
   3830                 .eq => try cg.buildBinary(
   3831                     .OpLogicalAnd,
   3832                     try cg.cmp(.eq, lhs_valid, rhs_valid),
   3833                     try cg.buildBinary(
   3834                         .OpLogicalOr,
   3835                         try cg.buildUnary(.l_not, lhs_valid),
   3836                         try cg.cmp(.eq, lhs_pl, rhs_pl),
   3837                     ),
   3838                 ),
   3839                 .neq => try cg.buildBinary(
   3840                     .OpLogicalOr,
   3841                     try cg.cmp(.neq, lhs_valid, rhs_valid),
   3842                     try cg.buildBinary(
   3843                         .OpLogicalAnd,
   3844                         lhs_valid,
   3845                         try cg.cmp(.neq, lhs_pl, rhs_pl),
   3846                     ),
   3847                 ),
   3848                 else => unreachable,
   3849             };
   3850         },
   3851         else => |ty| return cg.todo("implement cmp operation for '{s}' type", .{@tagName(ty)}),
   3852     }
   3853 
   3854     const info = cg.arithmeticTypeInfo(scalar_ty);
   3855     const pred: Opcode = switch (info.class) {
   3856         .composite_integer => unreachable, // TODO
   3857         .float => switch (op) {
   3858             .eq => .OpFOrdEqual,
   3859             .neq => .OpFUnordNotEqual,
   3860             .lt => .OpFOrdLessThan,
   3861             .lte => .OpFOrdLessThanEqual,
   3862             .gt => .OpFOrdGreaterThan,
   3863             .gte => .OpFOrdGreaterThanEqual,
   3864         },
   3865         .bool => switch (op) {
   3866             .eq => .OpLogicalEqual,
   3867             .neq => .OpLogicalNotEqual,
   3868             else => unreachable,
   3869         },
   3870         .integer, .strange_integer => switch (info.signedness) {
   3871             .signed => switch (op) {
   3872                 .eq => .OpIEqual,
   3873                 .neq => .OpINotEqual,
   3874                 .lt => .OpSLessThan,
   3875                 .lte => .OpSLessThanEqual,
   3876                 .gt => .OpSGreaterThan,
   3877                 .gte => .OpSGreaterThanEqual,
   3878             },
   3879             .unsigned => switch (op) {
   3880                 .eq => .OpIEqual,
   3881                 .neq => .OpINotEqual,
   3882                 .lt => .OpULessThan,
   3883                 .lte => .OpULessThanEqual,
   3884                 .gt => .OpUGreaterThan,
   3885                 .gte => .OpUGreaterThanEqual,
   3886             },
   3887         },
   3888     };
   3889 
   3890     return try cg.buildCmp(pred, lhs, rhs);
   3891 }
   3892 
   3893 fn airCmp(
   3894     cg: *CodeGen,
   3895     inst: Air.Inst.Index,
   3896     comptime op: std.math.CompareOperator,
   3897 ) !?Id {
   3898     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   3899     const lhs = try cg.temporary(bin_op.lhs);
   3900     const rhs = try cg.temporary(bin_op.rhs);
   3901 
   3902     const result = try cg.cmp(op, lhs, rhs);
   3903     return try result.materialize(cg);
   3904 }
   3905 
   3906 fn airVectorCmp(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3907     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   3908     const vec_cmp = cg.air.extraData(Air.VectorCmp, ty_pl.payload).data;
   3909     const lhs = try cg.temporary(vec_cmp.lhs);
   3910     const rhs = try cg.temporary(vec_cmp.rhs);
   3911     const op = vec_cmp.compareOperator();
   3912 
   3913     const result = try cg.cmp(op, lhs, rhs);
   3914     return try result.materialize(cg);
   3915 }
   3916 
   3917 /// Bitcast one type to another. Note: both types, input, output are expected in **direct** representation.
   3918 fn bitCast(
   3919     cg: *CodeGen,
   3920     dst_ty: Type,
   3921     src_ty: Type,
   3922     src_id: Id,
   3923 ) !Id {
   3924     const gpa = cg.module.gpa;
   3925     const zcu = cg.module.zcu;
   3926     const target = zcu.getTarget();
   3927     const src_ty_id = try cg.resolveType(src_ty, .direct);
   3928     const dst_ty_id = try cg.resolveType(dst_ty, .direct);
   3929 
   3930     const result_id = blk: {
   3931         if (src_ty_id == dst_ty_id) break :blk src_id;
   3932 
   3933         // TODO: Some more cases are missing here
   3934         //   See fn bitCast in llvm.zig
   3935 
   3936         if (src_ty.zigTypeTag(zcu) == .int and dst_ty.isPtrAtRuntime(zcu)) {
   3937             if (target.os.tag != .opencl) {
   3938                 if (dst_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) {
   3939                     return cg.fail(
   3940                         "cannot cast integer to pointer with address space '{s}'",
   3941                         .{@tagName(dst_ty.ptrAddressSpace(zcu))},
   3942                     );
   3943                 }
   3944             }
   3945 
   3946             const result_id = cg.module.allocId();
   3947             try cg.body.emit(gpa, .OpConvertUToPtr, .{
   3948                 .id_result_type = dst_ty_id,
   3949                 .id_result = result_id,
   3950                 .integer_value = src_id,
   3951             });
   3952             break :blk result_id;
   3953         }
   3954 
   3955         // We can only use OpBitcast for specific conversions: between numerical types, and
   3956         // between pointers. If the resolved spir-v types fall into this category then emit OpBitcast,
   3957         // otherwise use a temporary and perform a pointer cast.
   3958         const can_bitcast = (src_ty.isNumeric(zcu) and dst_ty.isNumeric(zcu)) or (src_ty.isPtrAtRuntime(zcu) and dst_ty.isPtrAtRuntime(zcu));
   3959         if (can_bitcast) {
   3960             const result_id = cg.module.allocId();
   3961             try cg.body.emit(gpa, .OpBitcast, .{
   3962                 .id_result_type = dst_ty_id,
   3963                 .id_result = result_id,
   3964                 .operand = src_id,
   3965             });
   3966 
   3967             break :blk result_id;
   3968         }
   3969 
   3970         const dst_ptr_ty_id = try cg.module.ptrType(dst_ty_id, .function);
   3971 
   3972         const src_ty_indirect_id = try cg.resolveType(src_ty, .indirect);
   3973         const tmp_id = try cg.alloc(src_ty_indirect_id, null);
   3974         try cg.store(src_ty, tmp_id, src_id, .{});
   3975         const casted_ptr_id = cg.module.allocId();
   3976         try cg.body.emit(gpa, .OpBitcast, .{
   3977             .id_result_type = dst_ptr_ty_id,
   3978             .id_result = casted_ptr_id,
   3979             .operand = tmp_id,
   3980         });
   3981         break :blk try cg.load(dst_ty, casted_ptr_id, .{});
   3982     };
   3983 
   3984     // Because strange integers use sign-extended representation, we may need to normalize
   3985     // the result here.
   3986     // TODO: This detail could cause stuff like @as(*const i1, @ptrCast(&@as(u1, 1))) to break
   3987     // should we change the representation of strange integers?
   3988     if (dst_ty.zigTypeTag(zcu) == .int) {
   3989         const info = cg.arithmeticTypeInfo(dst_ty);
   3990         const result = try cg.normalize(Temporary.init(dst_ty, result_id), info);
   3991         return try result.materialize(cg);
   3992     }
   3993 
   3994     return result_id;
   3995 }
   3996 
   3997 fn airBitCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   3998     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   3999     const operand_ty = cg.typeOf(ty_op.operand);
   4000     const result_ty = cg.typeOfIndex(inst);
   4001     if (operand_ty.toIntern() == .bool_type) {
   4002         const operand = try cg.temporary(ty_op.operand);
   4003         const result = try cg.intFromBool(operand, .u1);
   4004         return try result.materialize(cg);
   4005     }
   4006     const operand_id = try cg.resolve(ty_op.operand);
   4007     return try cg.bitCast(result_ty, operand_ty, operand_id);
   4008 }
   4009 
   4010 fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4011     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4012     const src = try cg.temporary(ty_op.operand);
   4013     const dst_ty = cg.typeOfIndex(inst);
   4014 
   4015     const src_info = cg.arithmeticTypeInfo(src.ty);
   4016     const dst_info = cg.arithmeticTypeInfo(dst_ty);
   4017 
   4018     if (src_info.backing_bits == dst_info.backing_bits) {
   4019         return try src.materialize(cg);
   4020     }
   4021 
   4022     const converted = try cg.buildConvert(dst_ty, src);
   4023 
   4024     // Make sure to normalize the result if shrinking.
   4025     // Because strange ints are sign extended in their backing
   4026     // type, we don't need to normalize when growing the type. The
   4027     // representation is already the same.
   4028     const result = if (dst_info.bits < src_info.bits)
   4029         try cg.normalize(converted, dst_info)
   4030     else
   4031         converted;
   4032 
   4033     return try result.materialize(cg);
   4034 }
   4035 
   4036 fn intFromPtr(cg: *CodeGen, operand_id: Id) !Id {
   4037     const result_type_id = try cg.resolveType(.usize, .direct);
   4038     const result_id = cg.module.allocId();
   4039     try cg.body.emit(cg.module.gpa, .OpConvertPtrToU, .{
   4040         .id_result_type = result_type_id,
   4041         .id_result = result_id,
   4042         .pointer = operand_id,
   4043     });
   4044     return result_id;
   4045 }
   4046 
   4047 fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4048     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4049     const operand_ty = cg.typeOf(ty_op.operand);
   4050     const operand_id = try cg.resolve(ty_op.operand);
   4051     const result_ty = cg.typeOfIndex(inst);
   4052     return try cg.floatFromInt(result_ty, operand_ty, operand_id);
   4053 }
   4054 
   4055 fn floatFromInt(cg: *CodeGen, result_ty: Type, operand_ty: Type, operand_id: Id) !Id {
   4056     const gpa = cg.module.gpa;
   4057     const operand_info = cg.arithmeticTypeInfo(operand_ty);
   4058     const result_id = cg.module.allocId();
   4059     const result_ty_id = try cg.resolveType(result_ty, .direct);
   4060     switch (operand_info.signedness) {
   4061         .signed => try cg.body.emit(gpa, .OpConvertSToF, .{
   4062             .id_result_type = result_ty_id,
   4063             .id_result = result_id,
   4064             .signed_value = operand_id,
   4065         }),
   4066         .unsigned => try cg.body.emit(gpa, .OpConvertUToF, .{
   4067             .id_result_type = result_ty_id,
   4068             .id_result = result_id,
   4069             .unsigned_value = operand_id,
   4070         }),
   4071     }
   4072     return result_id;
   4073 }
   4074 
   4075 fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4076     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4077     const operand_id = try cg.resolve(ty_op.operand);
   4078     const result_ty = cg.typeOfIndex(inst);
   4079     return try cg.intFromFloat(result_ty, operand_id);
   4080 }
   4081 
   4082 fn intFromFloat(cg: *CodeGen, result_ty: Type, operand_id: Id) !Id {
   4083     const gpa = cg.module.gpa;
   4084     const result_info = cg.arithmeticTypeInfo(result_ty);
   4085     const result_ty_id = try cg.resolveType(result_ty, .direct);
   4086     const result_id = cg.module.allocId();
   4087     switch (result_info.signedness) {
   4088         .signed => try cg.body.emit(gpa, .OpConvertFToS, .{
   4089             .id_result_type = result_ty_id,
   4090             .id_result = result_id,
   4091             .float_value = operand_id,
   4092         }),
   4093         .unsigned => try cg.body.emit(gpa, .OpConvertFToU, .{
   4094             .id_result_type = result_ty_id,
   4095             .id_result = result_id,
   4096             .float_value = operand_id,
   4097         }),
   4098     }
   4099     return result_id;
   4100 }
   4101 
   4102 fn airFloatCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4103     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4104     const operand = try cg.temporary(ty_op.operand);
   4105     const dest_ty = cg.typeOfIndex(inst);
   4106     const result = try cg.buildConvert(dest_ty, operand);
   4107     return try result.materialize(cg);
   4108 }
   4109 
   4110 fn airNot(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4111     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4112     const operand = try cg.temporary(ty_op.operand);
   4113     const result_ty = cg.typeOfIndex(inst);
   4114     const info = cg.arithmeticTypeInfo(result_ty);
   4115 
   4116     const result = switch (info.class) {
   4117         .bool => try cg.buildUnary(.l_not, operand),
   4118         .float => unreachable,
   4119         .composite_integer => unreachable, // TODO
   4120         .strange_integer, .integer => blk: {
   4121             const complement = try cg.buildUnary(.bit_not, operand);
   4122             break :blk try cg.normalize(complement, info);
   4123         },
   4124     };
   4125 
   4126     return try result.materialize(cg);
   4127 }
   4128 
   4129 fn airArrayToSlice(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4130     const zcu = cg.module.zcu;
   4131     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4132     const array_ptr_ty = cg.typeOf(ty_op.operand);
   4133     const array_ty = array_ptr_ty.childType(zcu);
   4134     const slice_ty = cg.typeOfIndex(inst);
   4135     const elem_ptr_ty = slice_ty.slicePtrFieldType(zcu);
   4136 
   4137     const elem_ptr_ty_id = try cg.resolveType(elem_ptr_ty, .direct);
   4138 
   4139     const array_ptr_id = try cg.resolve(ty_op.operand);
   4140     const len_id = try cg.constInt(.usize, array_ty.arrayLen(zcu));
   4141 
   4142     const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(zcu))
   4143         // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type.
   4144         try cg.bitCast(elem_ptr_ty, array_ptr_ty, array_ptr_id)
   4145     else
   4146         // Convert the pointer-to-array to a pointer to the first element.
   4147         try cg.accessChain(elem_ptr_ty_id, array_ptr_id, &.{0});
   4148 
   4149     const slice_ty_id = try cg.resolveType(slice_ty, .direct);
   4150     return try cg.constructComposite(slice_ty_id, &.{ elem_ptr_id, len_id });
   4151 }
   4152 
   4153 fn airSlice(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4154     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4155     const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
   4156     const ptr_id = try cg.resolve(bin_op.lhs);
   4157     const len_id = try cg.resolve(bin_op.rhs);
   4158     const slice_ty = cg.typeOfIndex(inst);
   4159     const slice_ty_id = try cg.resolveType(slice_ty, .direct);
   4160     return try cg.constructComposite(slice_ty_id, &.{ ptr_id, len_id });
   4161 }
   4162 
   4163 fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4164     const gpa = cg.module.gpa;
   4165     const pt = cg.pt;
   4166     const zcu = cg.module.zcu;
   4167     const ip = &zcu.intern_pool;
   4168     const target = cg.module.zcu.getTarget();
   4169     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4170     const result_ty = cg.typeOfIndex(inst);
   4171     const len: usize = @intCast(result_ty.arrayLen(zcu));
   4172     const elements: []const Air.Inst.Ref = @ptrCast(cg.air.extra.items[ty_pl.payload..][0..len]);
   4173 
   4174     switch (result_ty.zigTypeTag(zcu)) {
   4175         .@"struct" => {
   4176             if (zcu.typeToPackedStruct(result_ty)) |struct_type| {
   4177                 comptime assert(Type.packed_struct_layout_version == 2);
   4178                 const backing_int_ty: Type = .fromInterned(struct_type.backingIntTypeUnordered(ip));
   4179                 var running_int_id = try cg.constInt(backing_int_ty, 0);
   4180                 var running_bits: u16 = 0;
   4181                 for (struct_type.field_types.get(ip), elements) |field_ty_ip, element| {
   4182                     const field_ty: Type = .fromInterned(field_ty_ip);
   4183                     if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   4184                     const field_id = try cg.resolve(element);
   4185                     const ty_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
   4186                     const field_int_ty = try cg.pt.intType(.unsigned, ty_bit_size);
   4187                     const field_int_id = blk: {
   4188                         if (field_ty.isPtrAtRuntime(zcu)) {
   4189                             assert(target.cpu.arch == .spirv64 and
   4190                                 field_ty.ptrAddressSpace(zcu) == .storage_buffer);
   4191                             break :blk try cg.intFromPtr(field_id);
   4192                         }
   4193                         break :blk try cg.bitCast(field_int_ty, field_ty, field_id);
   4194                     };
   4195                     const shift_rhs = try cg.constInt(backing_int_ty, running_bits);
   4196                     const extended_int_conv = try cg.buildConvert(backing_int_ty, .{
   4197                         .ty = field_int_ty,
   4198                         .value = .{ .singleton = field_int_id },
   4199                     });
   4200                     const shifted = try cg.buildBinary(.OpShiftLeftLogical, extended_int_conv, .{
   4201                         .ty = backing_int_ty,
   4202                         .value = .{ .singleton = shift_rhs },
   4203                     });
   4204                     const running_int_tmp = try cg.buildBinary(
   4205                         .OpBitwiseOr,
   4206                         .{ .ty = backing_int_ty, .value = .{ .singleton = running_int_id } },
   4207                         shifted,
   4208                     );
   4209                     running_int_id = try running_int_tmp.materialize(cg);
   4210                     running_bits += ty_bit_size;
   4211                 }
   4212                 return running_int_id;
   4213             }
   4214 
   4215             const scratch_top = cg.id_scratch.items.len;
   4216             defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   4217             const constituents = try cg.id_scratch.addManyAsSlice(gpa, elements.len);
   4218 
   4219             const types = try gpa.alloc(Type, elements.len);
   4220             defer gpa.free(types);
   4221 
   4222             var index: usize = 0;
   4223 
   4224             switch (ip.indexToKey(result_ty.toIntern())) {
   4225                 .tuple_type => |tuple| {
   4226                     for (tuple.types.get(ip), elements, 0..) |field_ty, element, i| {
   4227                         if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue;
   4228                         assert(Type.fromInterned(field_ty).hasRuntimeBits(zcu));
   4229 
   4230                         const id = try cg.resolve(element);
   4231                         types[index] = .fromInterned(field_ty);
   4232                         constituents[index] = try cg.convertToIndirect(.fromInterned(field_ty), id);
   4233                         index += 1;
   4234                     }
   4235                 },
   4236                 .struct_type => {
   4237                     const struct_type = ip.loadStructType(result_ty.toIntern());
   4238                     var it = struct_type.iterateRuntimeOrder(ip);
   4239                     for (elements, 0..) |element, i| {
   4240                         const field_index = it.next().?;
   4241                         if ((try result_ty.structFieldValueComptime(pt, i)) != null) continue;
   4242                         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
   4243                         assert(field_ty.hasRuntimeBitsIgnoreComptime(zcu));
   4244 
   4245                         const id = try cg.resolve(element);
   4246                         types[index] = field_ty;
   4247                         constituents[index] = try cg.convertToIndirect(field_ty, id);
   4248                         index += 1;
   4249                     }
   4250                 },
   4251                 else => unreachable,
   4252             }
   4253 
   4254             const result_ty_id = try cg.resolveType(result_ty, .direct);
   4255             return try cg.constructComposite(result_ty_id, constituents[0..index]);
   4256         },
   4257         .vector => {
   4258             const n_elems = result_ty.vectorLen(zcu);
   4259             const scratch_top = cg.id_scratch.items.len;
   4260             defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   4261             const elem_ids = try cg.id_scratch.addManyAsSlice(gpa, n_elems);
   4262 
   4263             for (elements, 0..) |element, i| {
   4264                 elem_ids[i] = try cg.resolve(element);
   4265             }
   4266 
   4267             const result_ty_id = try cg.resolveType(result_ty, .direct);
   4268             return try cg.constructComposite(result_ty_id, elem_ids);
   4269         },
   4270         .array => {
   4271             const array_info = result_ty.arrayInfo(zcu);
   4272             const n_elems: usize = @intCast(result_ty.arrayLenIncludingSentinel(zcu));
   4273             const scratch_top = cg.id_scratch.items.len;
   4274             defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   4275             const elem_ids = try cg.id_scratch.addManyAsSlice(gpa, n_elems);
   4276 
   4277             for (elements, 0..) |element, i| {
   4278                 const id = try cg.resolve(element);
   4279                 elem_ids[i] = try cg.convertToIndirect(array_info.elem_type, id);
   4280             }
   4281 
   4282             if (array_info.sentinel) |sentinel_val| {
   4283                 elem_ids[n_elems - 1] = try cg.constant(array_info.elem_type, sentinel_val, .indirect);
   4284             }
   4285 
   4286             const result_ty_id = try cg.resolveType(result_ty, .direct);
   4287             return try cg.constructComposite(result_ty_id, elem_ids);
   4288         },
   4289         else => unreachable,
   4290     }
   4291 }
   4292 
   4293 fn sliceOrArrayLen(cg: *CodeGen, operand_id: Id, ty: Type) !Id {
   4294     const zcu = cg.module.zcu;
   4295     switch (ty.ptrSize(zcu)) {
   4296         .slice => return cg.extractField(.usize, operand_id, 1),
   4297         .one => {
   4298             const array_ty = ty.childType(zcu);
   4299             const elem_ty = array_ty.childType(zcu);
   4300             const abi_size = elem_ty.abiSize(zcu);
   4301             const size = array_ty.arrayLenIncludingSentinel(zcu) * abi_size;
   4302             return try cg.constInt(.usize, size);
   4303         },
   4304         .many, .c => unreachable,
   4305     }
   4306 }
   4307 
   4308 fn sliceOrArrayPtr(cg: *CodeGen, operand_id: Id, ty: Type) !Id {
   4309     const zcu = cg.module.zcu;
   4310     if (ty.isSlice(zcu)) {
   4311         const ptr_ty = ty.slicePtrFieldType(zcu);
   4312         return cg.extractField(ptr_ty, operand_id, 0);
   4313     }
   4314     return operand_id;
   4315 }
   4316 
   4317 fn airMemcpy(cg: *CodeGen, inst: Air.Inst.Index) !void {
   4318     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4319     const dest_slice = try cg.resolve(bin_op.lhs);
   4320     const src_slice = try cg.resolve(bin_op.rhs);
   4321     const dest_ty = cg.typeOf(bin_op.lhs);
   4322     const src_ty = cg.typeOf(bin_op.rhs);
   4323     const dest_ptr = try cg.sliceOrArrayPtr(dest_slice, dest_ty);
   4324     const src_ptr = try cg.sliceOrArrayPtr(src_slice, src_ty);
   4325     const len = try cg.sliceOrArrayLen(dest_slice, dest_ty);
   4326     try cg.body.emit(cg.module.gpa, .OpCopyMemorySized, .{
   4327         .target = dest_ptr,
   4328         .source = src_ptr,
   4329         .size = len,
   4330     });
   4331 }
   4332 
   4333 fn airMemmove(cg: *CodeGen, inst: Air.Inst.Index) !void {
   4334     _ = inst;
   4335     return cg.fail("TODO implement airMemcpy for spirv", .{});
   4336 }
   4337 
   4338 fn airSliceField(cg: *CodeGen, inst: Air.Inst.Index, field: u32) !?Id {
   4339     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4340     const field_ty = cg.typeOfIndex(inst);
   4341     const operand_id = try cg.resolve(ty_op.operand);
   4342     return try cg.extractField(field_ty, operand_id, field);
   4343 }
   4344 
   4345 fn airSliceElemPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4346     const zcu = cg.module.zcu;
   4347     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4348     const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
   4349     const slice_ty = cg.typeOf(bin_op.lhs);
   4350     if (!slice_ty.isVolatilePtr(zcu) and cg.liveness.isUnused(inst)) return null;
   4351 
   4352     const slice_id = try cg.resolve(bin_op.lhs);
   4353     const index_id = try cg.resolve(bin_op.rhs);
   4354 
   4355     const ptr_ty = cg.typeOfIndex(inst);
   4356     const ptr_ty_id = try cg.resolveType(ptr_ty, .direct);
   4357 
   4358     const slice_ptr = try cg.extractField(ptr_ty, slice_id, 0);
   4359     return try cg.ptrAccessChain(ptr_ty_id, slice_ptr, index_id, &.{});
   4360 }
   4361 
   4362 fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4363     const zcu = cg.module.zcu;
   4364     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4365     const slice_ty = cg.typeOf(bin_op.lhs);
   4366     if (!slice_ty.isVolatilePtr(zcu) and cg.liveness.isUnused(inst)) return null;
   4367 
   4368     const slice_id = try cg.resolve(bin_op.lhs);
   4369     const index_id = try cg.resolve(bin_op.rhs);
   4370 
   4371     const ptr_ty = slice_ty.slicePtrFieldType(zcu);
   4372     const ptr_ty_id = try cg.resolveType(ptr_ty, .direct);
   4373 
   4374     const slice_ptr = try cg.extractField(ptr_ty, slice_id, 0);
   4375     const elem_ptr = try cg.ptrAccessChain(ptr_ty_id, slice_ptr, index_id, &.{});
   4376     return try cg.load(slice_ty.childType(zcu), elem_ptr, .{ .is_volatile = slice_ty.isVolatilePtr(zcu) });
   4377 }
   4378 
   4379 fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id {
   4380     const zcu = cg.module.zcu;
   4381     // Construct new pointer type for the resulting pointer
   4382     const elem_ty = ptr_ty.elemType2(zcu); // use elemType() so that we get T for *[N]T.
   4383     const elem_ty_id = try cg.resolveType(elem_ty, .indirect);
   4384     const elem_ptr_ty_id = try cg.module.ptrType(elem_ty_id, cg.module.storageClass(ptr_ty.ptrAddressSpace(zcu)));
   4385     if (ptr_ty.isSinglePointer(zcu)) {
   4386         // Pointer-to-array. In this case, the resulting pointer is not of the same type
   4387         // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.
   4388         return try cg.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id});
   4389     } else {
   4390         // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain
   4391         return try cg.ptrAccessChain(elem_ptr_ty_id, ptr_id, index_id, &.{});
   4392     }
   4393 }
   4394 
   4395 fn airPtrElemPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4396     const zcu = cg.module.zcu;
   4397     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4398     const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data;
   4399     const src_ptr_ty = cg.typeOf(bin_op.lhs);
   4400     const elem_ty = src_ptr_ty.childType(zcu);
   4401     const ptr_id = try cg.resolve(bin_op.lhs);
   4402 
   4403     if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   4404         const dst_ptr_ty = cg.typeOfIndex(inst);
   4405         return try cg.bitCast(dst_ptr_ty, src_ptr_ty, ptr_id);
   4406     }
   4407 
   4408     const index_id = try cg.resolve(bin_op.rhs);
   4409     return try cg.ptrElemPtr(src_ptr_ty, ptr_id, index_id);
   4410 }
   4411 
   4412 fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4413     const gpa = cg.module.gpa;
   4414     const zcu = cg.module.zcu;
   4415     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4416     const array_ty = cg.typeOf(bin_op.lhs);
   4417     const elem_ty = array_ty.childType(zcu);
   4418     const array_id = try cg.resolve(bin_op.lhs);
   4419     const index_id = try cg.resolve(bin_op.rhs);
   4420 
   4421     // SPIR-V doesn't have an array indexing function for some damn reason.
   4422     // For now, just generate a temporary and use that.
   4423     // TODO: This backend probably also should use isByRef from llvm...
   4424 
   4425     const is_vector = array_ty.isVector(zcu);
   4426     const elem_repr: Repr = if (is_vector) .direct else .indirect;
   4427     const array_ty_id = try cg.resolveType(array_ty, .direct);
   4428     const elem_ty_id = try cg.resolveType(elem_ty, elem_repr);
   4429     const ptr_array_ty_id = try cg.module.ptrType(array_ty_id, .function);
   4430     const ptr_elem_ty_id = try cg.module.ptrType(elem_ty_id, .function);
   4431 
   4432     const tmp_id = cg.module.allocId();
   4433     try cg.prologue.emit(gpa, .OpVariable, .{
   4434         .id_result_type = ptr_array_ty_id,
   4435         .id_result = tmp_id,
   4436         .storage_class = .function,
   4437     });
   4438 
   4439     try cg.body.emit(gpa, .OpStore, .{
   4440         .pointer = tmp_id,
   4441         .object = array_id,
   4442     });
   4443 
   4444     const elem_ptr_id = try cg.accessChainId(ptr_elem_ty_id, tmp_id, &.{index_id});
   4445 
   4446     const result_id = cg.module.allocId();
   4447     try cg.body.emit(gpa, .OpLoad, .{
   4448         .id_result_type = try cg.resolveType(elem_ty, elem_repr),
   4449         .id_result = result_id,
   4450         .pointer = elem_ptr_id,
   4451     });
   4452 
   4453     if (is_vector) {
   4454         // Result is already in direct representation
   4455         return result_id;
   4456     }
   4457 
   4458     // This is an array type; the elements are stored in indirect representation.
   4459     // We have to convert the type to direct.
   4460 
   4461     return try cg.convertToDirect(elem_ty, result_id);
   4462 }
   4463 
   4464 fn airPtrElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4465     const zcu = cg.module.zcu;
   4466     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4467     const ptr_ty = cg.typeOf(bin_op.lhs);
   4468     const elem_ty = cg.typeOfIndex(inst);
   4469     const ptr_id = try cg.resolve(bin_op.lhs);
   4470     const index_id = try cg.resolve(bin_op.rhs);
   4471     const elem_ptr_id = try cg.ptrElemPtr(ptr_ty, ptr_id, index_id);
   4472     return try cg.load(elem_ty, elem_ptr_id, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) });
   4473 }
   4474 
   4475 fn airSetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) !void {
   4476     const zcu = cg.module.zcu;
   4477     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   4478     const un_ptr_ty = cg.typeOf(bin_op.lhs);
   4479     const un_ty = un_ptr_ty.childType(zcu);
   4480     const layout = cg.unionLayout(un_ty);
   4481 
   4482     if (layout.tag_size == 0) return;
   4483 
   4484     const tag_ty = un_ty.unionTagTypeSafety(zcu).?;
   4485     const tag_ty_id = try cg.resolveType(tag_ty, .indirect);
   4486     const tag_ptr_ty_id = try cg.module.ptrType(tag_ty_id, cg.module.storageClass(un_ptr_ty.ptrAddressSpace(zcu)));
   4487 
   4488     const union_ptr_id = try cg.resolve(bin_op.lhs);
   4489     const new_tag_id = try cg.resolve(bin_op.rhs);
   4490 
   4491     if (!layout.has_payload) {
   4492         try cg.store(tag_ty, union_ptr_id, new_tag_id, .{ .is_volatile = un_ptr_ty.isVolatilePtr(zcu) });
   4493     } else {
   4494         const ptr_id = try cg.accessChain(tag_ptr_ty_id, union_ptr_id, &.{layout.tag_index});
   4495         try cg.store(tag_ty, ptr_id, new_tag_id, .{ .is_volatile = un_ptr_ty.isVolatilePtr(zcu) });
   4496     }
   4497 }
   4498 
   4499 fn airGetUnionTag(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4500     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4501     const un_ty = cg.typeOf(ty_op.operand);
   4502 
   4503     const zcu = cg.module.zcu;
   4504     const layout = cg.unionLayout(un_ty);
   4505     if (layout.tag_size == 0) return null;
   4506 
   4507     const union_handle = try cg.resolve(ty_op.operand);
   4508     if (!layout.has_payload) return union_handle;
   4509 
   4510     const tag_ty = un_ty.unionTagTypeSafety(zcu).?;
   4511     return try cg.extractField(tag_ty, union_handle, layout.tag_index);
   4512 }
   4513 
   4514 fn unionInit(
   4515     cg: *CodeGen,
   4516     ty: Type,
   4517     active_field: u32,
   4518     payload: ?Id,
   4519 ) !Id {
   4520     // To initialize a union, generate a temporary variable with the
   4521     // union type, then get the field pointer and pointer-cast it to the
   4522     // right type to store it. Finally load the entire union.
   4523 
   4524     // Note: The result here is not cached, because it generates runtime code.
   4525 
   4526     const pt = cg.pt;
   4527     const zcu = cg.module.zcu;
   4528     const ip = &zcu.intern_pool;
   4529     const union_ty = zcu.typeToUnion(ty).?;
   4530     const tag_ty: Type = .fromInterned(union_ty.enum_tag_ty);
   4531 
   4532     const layout = cg.unionLayout(ty);
   4533     const payload_ty: Type = .fromInterned(union_ty.field_types.get(ip)[active_field]);
   4534 
   4535     if (union_ty.flagsUnordered(ip).layout == .@"packed") {
   4536         if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   4537             const int_ty = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu)));
   4538             return cg.constInt(int_ty, 0);
   4539         }
   4540 
   4541         assert(payload != null);
   4542         if (payload_ty.isInt(zcu)) {
   4543             if (ty.bitSize(zcu) == payload_ty.bitSize(zcu)) {
   4544                 return cg.bitCast(ty, payload_ty, payload.?);
   4545             }
   4546 
   4547             const trunc = try cg.buildConvert(ty, .{ .ty = payload_ty, .value = .{ .singleton = payload.? } });
   4548             return try trunc.materialize(cg);
   4549         }
   4550 
   4551         const payload_int_ty = try pt.intType(.unsigned, @intCast(payload_ty.bitSize(zcu)));
   4552         const payload_int = if (payload_ty.ip_index == .bool_type)
   4553             try cg.convertToIndirect(payload_ty, payload.?)
   4554         else
   4555             try cg.bitCast(payload_int_ty, payload_ty, payload.?);
   4556         const trunc = try cg.buildConvert(ty, .{ .ty = payload_int_ty, .value = .{ .singleton = payload_int } });
   4557         return try trunc.materialize(cg);
   4558     }
   4559 
   4560     const tag_int = if (layout.tag_size != 0) blk: {
   4561         const tag_val = try pt.enumValueFieldIndex(tag_ty, active_field);
   4562         const tag_int_val = try tag_val.intFromEnum(tag_ty, pt);
   4563         break :blk tag_int_val.toUnsignedInt(zcu);
   4564     } else 0;
   4565 
   4566     if (!layout.has_payload) {
   4567         return try cg.constInt(tag_ty, tag_int);
   4568     }
   4569 
   4570     const ty_id = try cg.resolveType(ty, .indirect);
   4571     const tmp_id = try cg.alloc(ty_id, null);
   4572 
   4573     if (layout.tag_size != 0) {
   4574         const tag_ty_id = try cg.resolveType(tag_ty, .indirect);
   4575         const tag_ptr_ty_id = try cg.module.ptrType(tag_ty_id, .function);
   4576         const ptr_id = try cg.accessChain(tag_ptr_ty_id, tmp_id, &.{@as(u32, @intCast(layout.tag_index))});
   4577         const tag_id = try cg.constInt(tag_ty, tag_int);
   4578         try cg.store(tag_ty, ptr_id, tag_id, .{});
   4579     }
   4580 
   4581     if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   4582         const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect);
   4583         const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, .function);
   4584         const pl_ptr_id = try cg.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});
   4585         const active_pl_ptr_id = if (!layout.payload_ty.eql(payload_ty, zcu)) blk: {
   4586             const payload_ty_id = try cg.resolveType(payload_ty, .indirect);
   4587             const active_pl_ptr_ty_id = try cg.module.ptrType(payload_ty_id, .function);
   4588             const active_pl_ptr_id = cg.module.allocId();
   4589             try cg.body.emit(cg.module.gpa, .OpBitcast, .{
   4590                 .id_result_type = active_pl_ptr_ty_id,
   4591                 .id_result = active_pl_ptr_id,
   4592                 .operand = pl_ptr_id,
   4593             });
   4594             break :blk active_pl_ptr_id;
   4595         } else pl_ptr_id;
   4596 
   4597         try cg.store(payload_ty, active_pl_ptr_id, payload.?, .{});
   4598     } else {
   4599         assert(payload == null);
   4600     }
   4601 
   4602     // Just leave the padding fields uninitialized...
   4603     // TODO: Or should we initialize them with undef explicitly?
   4604 
   4605     return try cg.load(ty, tmp_id, .{});
   4606 }
   4607 
   4608 fn airUnionInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4609     const zcu = cg.module.zcu;
   4610     const ip = &zcu.intern_pool;
   4611     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4612     const extra = cg.air.extraData(Air.UnionInit, ty_pl.payload).data;
   4613     const ty = cg.typeOfIndex(inst);
   4614 
   4615     const union_obj = zcu.typeToUnion(ty).?;
   4616     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[extra.field_index]);
   4617     const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(zcu))
   4618         try cg.resolve(extra.init)
   4619     else
   4620         null;
   4621     return try cg.unionInit(ty, extra.field_index, payload);
   4622 }
   4623 
   4624 fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4625     const pt = cg.pt;
   4626     const zcu = cg.module.zcu;
   4627     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4628     const struct_field = cg.air.extraData(Air.StructField, ty_pl.payload).data;
   4629 
   4630     const object_ty = cg.typeOf(struct_field.struct_operand);
   4631     const object_id = try cg.resolve(struct_field.struct_operand);
   4632     const field_index = struct_field.field_index;
   4633     const field_ty = object_ty.fieldType(field_index, zcu);
   4634 
   4635     if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return null;
   4636 
   4637     switch (object_ty.zigTypeTag(zcu)) {
   4638         .@"struct" => switch (object_ty.containerLayout(zcu)) {
   4639             .@"packed" => {
   4640                 const struct_ty = zcu.typeToPackedStruct(object_ty).?;
   4641                 const struct_backing_int_bits = cg.module.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0";
   4642                 const bit_offset = zcu.structPackedFieldBitOffset(struct_ty, field_index);
   4643                 // We use the same int type the packed struct is backed by, because even though it would
   4644                 // be valid SPIR-V to use an smaller type like u16, some implementations like PoCL will complain.
   4645                 const bit_offset_id = try cg.constInt(object_ty, bit_offset);
   4646                 const signedness = if (field_ty.isInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned;
   4647                 const field_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
   4648                 const field_int_ty = try pt.intType(signedness, field_bit_size);
   4649                 const shift_lhs: Temporary = .{ .ty = object_ty, .value = .{ .singleton = object_id } };
   4650                 const shift = try cg.buildBinary(.OpShiftRightLogical, shift_lhs, .{ .ty = object_ty, .value = .{ .singleton = bit_offset_id } });
   4651                 const mask_id = try cg.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
   4652                 const masked = try cg.buildBinary(.OpBitwiseAnd, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } });
   4653                 const result_id = blk: {
   4654                     if (cg.module.backingIntBits(field_bit_size).@"0" == struct_backing_int_bits)
   4655                         break :blk try cg.bitCast(field_int_ty, object_ty, try masked.materialize(cg));
   4656                     const trunc = try cg.buildConvert(field_int_ty, masked);
   4657                     break :blk try trunc.materialize(cg);
   4658                 };
   4659                 if (field_ty.ip_index == .bool_type) return try cg.convertToDirect(.bool, result_id);
   4660                 if (field_ty.isInt(zcu)) return result_id;
   4661                 return try cg.bitCast(field_ty, field_int_ty, result_id);
   4662             },
   4663             else => return try cg.extractField(field_ty, object_id, field_index),
   4664         },
   4665         .@"union" => switch (object_ty.containerLayout(zcu)) {
   4666             .@"packed" => {
   4667                 const backing_int_ty = try pt.intType(.unsigned, @intCast(object_ty.bitSize(zcu)));
   4668                 const signedness = if (field_ty.isInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned;
   4669                 const field_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
   4670                 const int_ty = try pt.intType(signedness, field_bit_size);
   4671                 const mask_id = try cg.constInt(backing_int_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
   4672                 const masked = try cg.buildBinary(
   4673                     .OpBitwiseAnd,
   4674                     .{ .ty = backing_int_ty, .value = .{ .singleton = object_id } },
   4675                     .{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } },
   4676                 );
   4677                 const result_id = blk: {
   4678                     if (cg.module.backingIntBits(field_bit_size).@"0" == cg.module.backingIntBits(@intCast(backing_int_ty.bitSize(zcu))).@"0")
   4679                         break :blk try cg.bitCast(int_ty, backing_int_ty, try masked.materialize(cg));
   4680                     const trunc = try cg.buildConvert(int_ty, masked);
   4681                     break :blk try trunc.materialize(cg);
   4682                 };
   4683                 if (field_ty.ip_index == .bool_type) return try cg.convertToDirect(.bool, result_id);
   4684                 if (field_ty.isInt(zcu)) return result_id;
   4685                 return try cg.bitCast(field_ty, int_ty, result_id);
   4686             },
   4687             else => {
   4688                 // Store, ptr-elem-ptr, pointer-cast, load
   4689                 const layout = cg.unionLayout(object_ty);
   4690                 assert(layout.has_payload);
   4691 
   4692                 const object_ty_id = try cg.resolveType(object_ty, .indirect);
   4693                 const tmp_id = try cg.alloc(object_ty_id, null);
   4694                 try cg.store(object_ty, tmp_id, object_id, .{});
   4695 
   4696                 const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect);
   4697                 const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, .function);
   4698                 const pl_ptr_id = try cg.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});
   4699 
   4700                 const field_ty_id = try cg.resolveType(field_ty, .indirect);
   4701                 const active_pl_ptr_ty_id = try cg.module.ptrType(field_ty_id, .function);
   4702                 const active_pl_ptr_id = cg.module.allocId();
   4703                 try cg.body.emit(cg.module.gpa, .OpBitcast, .{
   4704                     .id_result_type = active_pl_ptr_ty_id,
   4705                     .id_result = active_pl_ptr_id,
   4706                     .operand = pl_ptr_id,
   4707                 });
   4708                 return try cg.load(field_ty, active_pl_ptr_id, .{});
   4709             },
   4710         },
   4711         else => unreachable,
   4712     }
   4713 }
   4714 
   4715 fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4716     const zcu = cg.module.zcu;
   4717     const target = zcu.getTarget();
   4718     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   4719     const extra = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
   4720 
   4721     const parent_ptr_ty = ty_pl.ty.toType();
   4722     const parent_ty = parent_ptr_ty.childType(zcu);
   4723     const result_ty_id = try cg.resolveType(parent_ptr_ty, .indirect);
   4724 
   4725     const field_ptr = try cg.resolve(extra.field_ptr);
   4726     const field_ptr_ty = cg.typeOf(extra.field_ptr);
   4727     const field_ptr_int = try cg.intFromPtr(field_ptr);
   4728     const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu);
   4729 
   4730     const base_ptr_int = base_ptr_int: {
   4731         if (field_offset == 0) break :base_ptr_int field_ptr_int;
   4732 
   4733         const field_offset_id = try cg.constInt(.usize, field_offset);
   4734         const field_ptr_tmp: Temporary = .init(.usize, field_ptr_int);
   4735         const field_offset_tmp: Temporary = .init(.usize, field_offset_id);
   4736         const result = try cg.buildBinary(.OpISub, field_ptr_tmp, field_offset_tmp);
   4737         break :base_ptr_int try result.materialize(cg);
   4738     };
   4739 
   4740     if (target.os.tag != .opencl) {
   4741         if (field_ptr_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) {
   4742             return cg.fail(
   4743                 "cannot cast integer to pointer with address space '{s}'",
   4744                 .{@tagName(field_ptr_ty.ptrAddressSpace(zcu))},
   4745             );
   4746         }
   4747     }
   4748 
   4749     const base_ptr = cg.module.allocId();
   4750     try cg.body.emit(cg.module.gpa, .OpConvertUToPtr, .{
   4751         .id_result_type = result_ty_id,
   4752         .id_result = base_ptr,
   4753         .integer_value = base_ptr_int,
   4754     });
   4755 
   4756     return base_ptr;
   4757 }
   4758 
   4759 fn structFieldPtr(
   4760     cg: *CodeGen,
   4761     result_ptr_ty: Type,
   4762     object_ptr_ty: Type,
   4763     object_ptr: Id,
   4764     field_index: u32,
   4765 ) !Id {
   4766     const result_ty_id = try cg.resolveType(result_ptr_ty, .direct);
   4767 
   4768     const zcu = cg.module.zcu;
   4769     const object_ty = object_ptr_ty.childType(zcu);
   4770     switch (object_ty.zigTypeTag(zcu)) {
   4771         .pointer => {
   4772             assert(object_ty.isSlice(zcu));
   4773             return cg.accessChain(result_ty_id, object_ptr, &.{field_index});
   4774         },
   4775         .@"struct" => switch (object_ty.containerLayout(zcu)) {
   4776             .@"packed" => return cg.todo("implement field access for packed structs", .{}),
   4777             else => {
   4778                 return try cg.accessChain(result_ty_id, object_ptr, &.{field_index});
   4779             },
   4780         },
   4781         .@"union" => {
   4782             const layout = cg.unionLayout(object_ty);
   4783             if (!layout.has_payload) {
   4784                 // Asked to get a pointer to a zero-sized field. Just lower this
   4785                 // to undefined, there is no reason to make it be a valid pointer.
   4786                 return try cg.module.constUndef(result_ty_id);
   4787             }
   4788 
   4789             const storage_class = cg.module.storageClass(object_ptr_ty.ptrAddressSpace(zcu));
   4790             const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect);
   4791             const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, storage_class);
   4792             const pl_ptr_id = blk: {
   4793                 if (object_ty.containerLayout(zcu) == .@"packed") break :blk object_ptr;
   4794                 break :blk try cg.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index});
   4795             };
   4796 
   4797             const active_pl_ptr_id = cg.module.allocId();
   4798             try cg.body.emit(cg.module.gpa, .OpBitcast, .{
   4799                 .id_result_type = result_ty_id,
   4800                 .id_result = active_pl_ptr_id,
   4801                 .operand = pl_ptr_id,
   4802             });
   4803             return active_pl_ptr_id;
   4804         },
   4805         else => unreachable,
   4806     }
   4807 }
   4808 
   4809 fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, field_index: u32) !?Id {
   4810     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   4811     const struct_ptr = try cg.resolve(ty_op.operand);
   4812     const struct_ptr_ty = cg.typeOf(ty_op.operand);
   4813     const result_ptr_ty = cg.typeOfIndex(inst);
   4814     return try cg.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index);
   4815 }
   4816 
   4817 fn alloc(cg: *CodeGen, ty_id: Id, initializer: ?Id) !Id {
   4818     const ptr_ty_id = try cg.module.ptrType(ty_id, .function);
   4819     const result_id = cg.module.allocId();
   4820     try cg.prologue.emit(cg.module.gpa, .OpVariable, .{
   4821         .id_result_type = ptr_ty_id,
   4822         .id_result = result_id,
   4823         .storage_class = .function,
   4824         .initializer = initializer,
   4825     });
   4826     return result_id;
   4827 }
   4828 
   4829 fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   4830     const zcu = cg.module.zcu;
   4831     const target = zcu.getTarget();
   4832     const ptr_ty = cg.typeOfIndex(inst);
   4833     const child_ty = ptr_ty.childType(zcu);
   4834     const child_ty_id = try cg.resolveType(child_ty, .indirect);
   4835     const ptr_align = ptr_ty.ptrAlignment(zcu);
   4836     const result_id = try cg.alloc(child_ty_id, null);
   4837     if (ptr_align != child_ty.abiAlignment(zcu)) {
   4838         if (target.os.tag != .opencl) return cg.fail("cannot apply alignment to variables", .{});
   4839         try cg.module.decorate(result_id, .{
   4840             .alignment = .{ .alignment = @intCast(ptr_align.toByteUnits().?) },
   4841         });
   4842     }
   4843     return result_id;
   4844 }
   4845 
   4846 fn airArg(cg: *CodeGen) Id {
   4847     defer cg.next_arg_index += 1;
   4848     return cg.args.items[cg.next_arg_index];
   4849 }
   4850 
   4851 /// Given a slice of incoming block connections, returns the block-id of the next
   4852 /// block to jump to. This function emits instructions, so it should be emitted
   4853 /// inside the merge block of the block.
   4854 /// This function should only be called with structured control flow generation.
   4855 fn structuredNextBlock(cg: *CodeGen, incoming: []const ControlFlow.Structured.Block.Incoming) !Id {
   4856     assert(cg.control_flow == .structured);
   4857 
   4858     const result_id = cg.module.allocId();
   4859     const block_id_ty_id = try cg.resolveType(.u32, .direct);
   4860     try cg.body.emitRaw(cg.module.gpa, .OpPhi, @intCast(2 + incoming.len * 2)); // result type + result + variable/parent...
   4861     cg.body.writeOperand(Id, block_id_ty_id);
   4862     cg.body.writeOperand(Id, result_id);
   4863 
   4864     for (incoming) |incoming_block| {
   4865         cg.body.writeOperand(spec.PairIdRefIdRef, .{ incoming_block.next_block, incoming_block.src_label });
   4866     }
   4867 
   4868     return result_id;
   4869 }
   4870 
   4871 /// Jumps to the block with the target block-id. This function must only be called when
   4872 /// terminating a body, there should be no instructions after it.
   4873 /// This function should only be called with structured control flow generation.
   4874 fn structuredBreak(cg: *CodeGen, target_block: Id) !void {
   4875     assert(cg.control_flow == .structured);
   4876 
   4877     const gpa = cg.module.gpa;
   4878     const sblock = cg.control_flow.structured.block_stack.getLast();
   4879     const merge_block = switch (sblock.*) {
   4880         .selection => |*merge| blk: {
   4881             const merge_label = cg.module.allocId();
   4882             try merge.merge_stack.append(gpa, .{
   4883                 .incoming = .{
   4884                     .src_label = cg.block_label,
   4885                     .next_block = target_block,
   4886                 },
   4887                 .merge_block = merge_label,
   4888             });
   4889             break :blk merge_label;
   4890         },
   4891         // Loop blocks do not end in a break. Not through a direct break,
   4892         // and also not through another instruction like cond_br or unreachable (these
   4893         // situations are replaced by `cond_br` in sema, or there is a `block` instruction
   4894         // placed around them).
   4895         .loop => unreachable,
   4896     };
   4897 
   4898     try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_block });
   4899 }
   4900 
   4901 /// Generate a body in a way that exits the body using only structured constructs.
   4902 /// Returns the block-id of the next block to jump to. After this function, a jump
   4903 /// should still be emitted to the block that should follow this structured body.
   4904 /// This function should only be called with structured control flow generation.
   4905 fn genStructuredBody(
   4906     cg: *CodeGen,
   4907     /// This parameter defines the method that this structured body is exited with.
   4908     block_merge_type: union(enum) {
   4909         /// Using selection; early exits from this body are surrounded with
   4910         /// if() statements.
   4911         selection,
   4912         /// Using loops; loops can be early exited by jumping to the merge block at
   4913         /// any time.
   4914         loop: struct {
   4915             merge_label: Id,
   4916             continue_label: Id,
   4917         },
   4918     },
   4919     body: []const Air.Inst.Index,
   4920 ) !Id {
   4921     assert(cg.control_flow == .structured);
   4922 
   4923     const gpa = cg.module.gpa;
   4924 
   4925     var sblock: ControlFlow.Structured.Block = switch (block_merge_type) {
   4926         .loop => |merge| .{ .loop = .{
   4927             .merge_block = merge.merge_label,
   4928         } },
   4929         .selection => .{ .selection = .{} },
   4930     };
   4931     defer sblock.deinit(gpa);
   4932 
   4933     {
   4934         try cg.control_flow.structured.block_stack.append(gpa, &sblock);
   4935         defer _ = cg.control_flow.structured.block_stack.pop();
   4936 
   4937         try cg.genBody(body);
   4938     }
   4939 
   4940     switch (sblock) {
   4941         .selection => |merge| {
   4942             // Now generate the merge block for all merges that
   4943             // still need to be performed.
   4944             const merge_stack = merge.merge_stack.items;
   4945 
   4946             // If no merges on the stack, this block didn't generate any jumps (all paths
   4947             // ended with a return or an unreachable). In that case, we don't need to do
   4948             // any merging.
   4949             if (merge_stack.len == 0) {
   4950                 // We still need to return a value of a next block to jump to.
   4951                 // For example, if we have code like
   4952                 //  if (x) {
   4953                 //    if (y) return else return;
   4954                 //  } else {}
   4955                 // then we still need the outer to have an OpSelectionMerge and consequently
   4956                 // a phi node. In that case we can just return bogus, since we know that its
   4957                 // path will never be taken.
   4958 
   4959                 // Make sure that we are still in a block when exiting the function.
   4960                 // TODO: Can we get rid of that?
   4961                 try cg.beginSpvBlock(cg.module.allocId());
   4962                 const block_id_ty_id = try cg.resolveType(.u32, .direct);
   4963                 return try cg.module.constUndef(block_id_ty_id);
   4964             }
   4965 
   4966             // The top-most merge actually only has a single source, the
   4967             // final jump of the block, or the merge block of a sub-block, cond_br,
   4968             // or loop. Therefore we just need to generate a block with a jump to the
   4969             // next merge block.
   4970             try cg.beginSpvBlock(merge_stack[merge_stack.len - 1].merge_block);
   4971 
   4972             // Now generate a merge ladder for the remaining merges in the stack.
   4973             var incoming: ControlFlow.Structured.Block.Incoming = .{
   4974                 .src_label = cg.block_label,
   4975                 .next_block = merge_stack[merge_stack.len - 1].incoming.next_block,
   4976             };
   4977             var i = merge_stack.len - 1;
   4978             while (i > 0) {
   4979                 i -= 1;
   4980                 const step = merge_stack[i];
   4981 
   4982                 try cg.body.emit(gpa, .OpBranch, .{ .target_label = step.merge_block });
   4983                 try cg.beginSpvBlock(step.merge_block);
   4984                 const next_block = try cg.structuredNextBlock(&.{ incoming, step.incoming });
   4985                 incoming = .{
   4986                     .src_label = step.merge_block,
   4987                     .next_block = next_block,
   4988                 };
   4989             }
   4990 
   4991             return incoming.next_block;
   4992         },
   4993         .loop => |merge| {
   4994             // Close the loop by jumping to the continue label
   4995 
   4996             try cg.body.emit(gpa, .OpBranch, .{ .target_label = block_merge_type.loop.continue_label });
   4997             // For blocks we must simple merge all the incoming blocks to get the next block.
   4998             try cg.beginSpvBlock(merge.merge_block);
   4999             return try cg.structuredNextBlock(merge.merges.items);
   5000         },
   5001     }
   5002 }
   5003 
   5004 fn airBlock(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5005     const inst_datas = cg.air.instructions.items(.data);
   5006     const extra = cg.air.extraData(Air.Block, inst_datas[@intFromEnum(inst)].ty_pl.payload);
   5007     return cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len]));
   5008 }
   5009 
   5010 fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) !?Id {
   5011     // In AIR, a block doesn't really define an entry point like a block, but
   5012     // more like a scope that breaks can jump out of and "return" a value from.
   5013     // This cannot be directly modelled in SPIR-V, so in a block instruction,
   5014     // we're going to split up the current block by first generating the code
   5015     // of the block, then a label, and then generate the rest of the current
   5016     // ir.Block in a different SPIR-V block.
   5017 
   5018     const gpa = cg.module.gpa;
   5019     const zcu = cg.module.zcu;
   5020     const ty = cg.typeOfIndex(inst);
   5021     const have_block_result = ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu);
   5022 
   5023     const cf = switch (cg.control_flow) {
   5024         .structured => |*cf| cf,
   5025         .unstructured => |*cf| {
   5026             var block: ControlFlow.Unstructured.Block = .{};
   5027             defer block.incoming_blocks.deinit(gpa);
   5028 
   5029             // 4 chosen as arbitrary initial capacity.
   5030             try block.incoming_blocks.ensureUnusedCapacity(gpa, 4);
   5031 
   5032             try cf.blocks.putNoClobber(gpa, inst, &block);
   5033             defer assert(cf.blocks.remove(inst));
   5034 
   5035             try cg.genBody(body);
   5036 
   5037             // Only begin a new block if there were actually any breaks towards it.
   5038             if (block.label) |label| {
   5039                 try cg.beginSpvBlock(label);
   5040             }
   5041 
   5042             if (!have_block_result)
   5043                 return null;
   5044 
   5045             assert(block.label != null);
   5046             const result_id = cg.module.allocId();
   5047             const result_type_id = try cg.resolveType(ty, .direct);
   5048 
   5049             try cg.body.emitRaw(
   5050                 gpa,
   5051                 .OpPhi,
   5052                 // result type + result + variable/parent...
   5053                 2 + @as(u16, @intCast(block.incoming_blocks.items.len * 2)),
   5054             );
   5055             cg.body.writeOperand(Id, result_type_id);
   5056             cg.body.writeOperand(Id, result_id);
   5057 
   5058             for (block.incoming_blocks.items) |incoming| {
   5059                 cg.body.writeOperand(
   5060                     spec.PairIdRefIdRef,
   5061                     .{ incoming.break_value_id, incoming.src_label },
   5062                 );
   5063             }
   5064 
   5065             return result_id;
   5066         },
   5067     };
   5068 
   5069     const maybe_block_result_var_id = if (have_block_result) blk: {
   5070         const ty_id = try cg.resolveType(ty, .indirect);
   5071         const block_result_var_id = try cg.alloc(ty_id, null);
   5072         try cf.block_results.putNoClobber(gpa, inst, block_result_var_id);
   5073         break :blk block_result_var_id;
   5074     } else null;
   5075     defer if (have_block_result) assert(cf.block_results.remove(inst));
   5076 
   5077     const next_block = try cg.genStructuredBody(.selection, body);
   5078 
   5079     // When encountering a block instruction, we are always at least in the function's scope,
   5080     // so there always has to be another entry.
   5081     assert(cf.block_stack.items.len > 0);
   5082 
   5083     // Check if the target of the branch was this current block.
   5084     const this_block = try cg.constInt(.u32, @intFromEnum(inst));
   5085     const jump_to_this_block_id = cg.module.allocId();
   5086     const bool_ty_id = try cg.resolveType(.bool, .direct);
   5087     try cg.body.emit(gpa, .OpIEqual, .{
   5088         .id_result_type = bool_ty_id,
   5089         .id_result = jump_to_this_block_id,
   5090         .operand_1 = next_block,
   5091         .operand_2 = this_block,
   5092     });
   5093 
   5094     const sblock = cf.block_stack.getLast();
   5095 
   5096     if (ty.isNoReturn(zcu)) {
   5097         // If this block is noreturn, this instruction is the last of a block,
   5098         // and we must simply jump to the block's merge unconditionally.
   5099         try cg.structuredBreak(next_block);
   5100     } else {
   5101         switch (sblock.*) {
   5102             .selection => |*merge| {
   5103                 // To jump out of a selection block, push a new entry onto its merge stack and
   5104                 // generate a conditional branch to there and to the instructions following this block.
   5105                 const merge_label = cg.module.allocId();
   5106                 const then_label = cg.module.allocId();
   5107                 try cg.body.emit(gpa, .OpSelectionMerge, .{
   5108                     .merge_block = merge_label,
   5109                     .selection_control = .{},
   5110                 });
   5111                 try cg.body.emit(gpa, .OpBranchConditional, .{
   5112                     .condition = jump_to_this_block_id,
   5113                     .true_label = then_label,
   5114                     .false_label = merge_label,
   5115                 });
   5116                 try merge.merge_stack.append(gpa, .{
   5117                     .incoming = .{
   5118                         .src_label = cg.block_label,
   5119                         .next_block = next_block,
   5120                     },
   5121                     .merge_block = merge_label,
   5122                 });
   5123 
   5124                 try cg.beginSpvBlock(then_label);
   5125             },
   5126             .loop => |*merge| {
   5127                 // To jump out of a loop block, generate a conditional that exits the block
   5128                 // to the loop merge if the target ID is not the one of this block.
   5129                 const continue_label = cg.module.allocId();
   5130                 try cg.body.emit(gpa, .OpBranchConditional, .{
   5131                     .condition = jump_to_this_block_id,
   5132                     .true_label = continue_label,
   5133                     .false_label = merge.merge_block,
   5134                 });
   5135                 try merge.merges.append(gpa, .{
   5136                     .src_label = cg.block_label,
   5137                     .next_block = next_block,
   5138                 });
   5139                 try cg.beginSpvBlock(continue_label);
   5140             },
   5141         }
   5142     }
   5143 
   5144     if (maybe_block_result_var_id) |block_result_var_id| {
   5145         return try cg.load(ty, block_result_var_id, .{});
   5146     }
   5147 
   5148     return null;
   5149 }
   5150 
   5151 fn airBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5152     const gpa = cg.module.gpa;
   5153     const zcu = cg.module.zcu;
   5154     const br = cg.air.instructions.items(.data)[@intFromEnum(inst)].br;
   5155     const operand_ty = cg.typeOf(br.operand);
   5156 
   5157     switch (cg.control_flow) {
   5158         .structured => |*cf| {
   5159             if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
   5160                 const operand_id = try cg.resolve(br.operand);
   5161                 const block_result_var_id = cf.block_results.get(br.block_inst).?;
   5162                 try cg.store(operand_ty, block_result_var_id, operand_id, .{});
   5163             }
   5164 
   5165             const next_block = try cg.constInt(.u32, @intFromEnum(br.block_inst));
   5166             try cg.structuredBreak(next_block);
   5167         },
   5168         .unstructured => |cf| {
   5169             const block = cf.blocks.get(br.block_inst).?;
   5170             if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
   5171                 const operand_id = try cg.resolve(br.operand);
   5172                 // block_label should not be undefined here, lest there
   5173                 // is a br or br_void in the function's body.
   5174                 try block.incoming_blocks.append(gpa, .{
   5175                     .src_label = cg.block_label,
   5176                     .break_value_id = operand_id,
   5177                 });
   5178             }
   5179 
   5180             if (block.label == null) {
   5181                 block.label = cg.module.allocId();
   5182             }
   5183 
   5184             try cg.body.emit(gpa, .OpBranch, .{ .target_label = block.label.? });
   5185         },
   5186     }
   5187 }
   5188 
   5189 fn airCondBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5190     const gpa = cg.module.gpa;
   5191     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   5192     const cond_br = cg.air.extraData(Air.CondBr, pl_op.payload);
   5193     const then_body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[cond_br.end..][0..cond_br.data.then_body_len]);
   5194     const else_body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[cond_br.end + then_body.len ..][0..cond_br.data.else_body_len]);
   5195     const condition_id = try cg.resolve(pl_op.operand);
   5196 
   5197     const then_label = cg.module.allocId();
   5198     const else_label = cg.module.allocId();
   5199 
   5200     switch (cg.control_flow) {
   5201         .structured => {
   5202             const merge_label = cg.module.allocId();
   5203 
   5204             try cg.body.emit(gpa, .OpSelectionMerge, .{
   5205                 .merge_block = merge_label,
   5206                 .selection_control = .{},
   5207             });
   5208             try cg.body.emit(gpa, .OpBranchConditional, .{
   5209                 .condition = condition_id,
   5210                 .true_label = then_label,
   5211                 .false_label = else_label,
   5212             });
   5213 
   5214             try cg.beginSpvBlock(then_label);
   5215             const then_next = try cg.genStructuredBody(.selection, then_body);
   5216             const then_incoming: ControlFlow.Structured.Block.Incoming = .{
   5217                 .src_label = cg.block_label,
   5218                 .next_block = then_next,
   5219             };
   5220 
   5221             try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label });
   5222 
   5223             try cg.beginSpvBlock(else_label);
   5224             const else_next = try cg.genStructuredBody(.selection, else_body);
   5225             const else_incoming: ControlFlow.Structured.Block.Incoming = .{
   5226                 .src_label = cg.block_label,
   5227                 .next_block = else_next,
   5228             };
   5229 
   5230             try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label });
   5231 
   5232             try cg.beginSpvBlock(merge_label);
   5233             const next_block = try cg.structuredNextBlock(&.{ then_incoming, else_incoming });
   5234 
   5235             try cg.structuredBreak(next_block);
   5236         },
   5237         .unstructured => {
   5238             try cg.body.emit(gpa, .OpBranchConditional, .{
   5239                 .condition = condition_id,
   5240                 .true_label = then_label,
   5241                 .false_label = else_label,
   5242             });
   5243 
   5244             try cg.beginSpvBlock(then_label);
   5245             try cg.genBody(then_body);
   5246             try cg.beginSpvBlock(else_label);
   5247             try cg.genBody(else_body);
   5248         },
   5249     }
   5250 }
   5251 
   5252 fn airLoop(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5253     const gpa = cg.module.gpa;
   5254     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5255     const loop = cg.air.extraData(Air.Block, ty_pl.payload);
   5256     const body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[loop.end..][0..loop.data.body_len]);
   5257 
   5258     const body_label = cg.module.allocId();
   5259 
   5260     switch (cg.control_flow) {
   5261         .structured => {
   5262             const header_label = cg.module.allocId();
   5263             const merge_label = cg.module.allocId();
   5264             const continue_label = cg.module.allocId();
   5265 
   5266             // The back-edge must point to the loop header, so generate a separate block for the
   5267             // loop header so that we don't accidentally include some instructions from there
   5268             // in the loop.
   5269 
   5270             try cg.body.emit(gpa, .OpBranch, .{ .target_label = header_label });
   5271             try cg.beginSpvBlock(header_label);
   5272 
   5273             // Emit loop header and jump to loop body
   5274             try cg.body.emit(gpa, .OpLoopMerge, .{
   5275                 .merge_block = merge_label,
   5276                 .continue_target = continue_label,
   5277                 .loop_control = .{},
   5278             });
   5279 
   5280             try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label });
   5281 
   5282             try cg.beginSpvBlock(body_label);
   5283 
   5284             const next_block = try cg.genStructuredBody(.{ .loop = .{
   5285                 .merge_label = merge_label,
   5286                 .continue_label = continue_label,
   5287             } }, body);
   5288             try cg.structuredBreak(next_block);
   5289 
   5290             try cg.beginSpvBlock(continue_label);
   5291 
   5292             try cg.body.emit(gpa, .OpBranch, .{ .target_label = header_label });
   5293         },
   5294         .unstructured => {
   5295             try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label });
   5296             try cg.beginSpvBlock(body_label);
   5297             try cg.genBody(body);
   5298 
   5299             try cg.body.emit(gpa, .OpBranch, .{ .target_label = body_label });
   5300         },
   5301     }
   5302 }
   5303 
   5304 fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5305     const zcu = cg.module.zcu;
   5306     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5307     const ptr_ty = cg.typeOf(ty_op.operand);
   5308     const elem_ty = cg.typeOfIndex(inst);
   5309     const operand = try cg.resolve(ty_op.operand);
   5310     if (!ptr_ty.isVolatilePtr(zcu) and cg.liveness.isUnused(inst)) return null;
   5311 
   5312     return try cg.load(elem_ty, operand, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) });
   5313 }
   5314 
   5315 fn airStore(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5316     const zcu = cg.module.zcu;
   5317     const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
   5318     const ptr_ty = cg.typeOf(bin_op.lhs);
   5319     const elem_ty = ptr_ty.childType(zcu);
   5320     const ptr = try cg.resolve(bin_op.lhs);
   5321     const value = try cg.resolve(bin_op.rhs);
   5322 
   5323     try cg.store(elem_ty, ptr, value, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) });
   5324 }
   5325 
   5326 fn airRet(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5327     const gpa = cg.module.gpa;
   5328     const zcu = cg.module.zcu;
   5329     const operand = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   5330     const ret_ty = cg.typeOf(operand);
   5331     if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   5332         const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?;
   5333         if (Type.fromInterned(fn_info.return_type).isError(zcu)) {
   5334             // Functions with an empty error set are emitted with an error code
   5335             // return type and return zero so they can be function pointers coerced
   5336             // to functions that return anyerror.
   5337             const no_err_id = try cg.constInt(.anyerror, 0);
   5338             return try cg.body.emit(gpa, .OpReturnValue, .{ .value = no_err_id });
   5339         } else {
   5340             return try cg.body.emit(gpa, .OpReturn, {});
   5341         }
   5342     }
   5343 
   5344     const operand_id = try cg.resolve(operand);
   5345     try cg.body.emit(gpa, .OpReturnValue, .{ .value = operand_id });
   5346 }
   5347 
   5348 fn airRetLoad(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5349     const gpa = cg.module.gpa;
   5350     const zcu = cg.module.zcu;
   5351     const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   5352     const ptr_ty = cg.typeOf(un_op);
   5353     const ret_ty = ptr_ty.childType(zcu);
   5354 
   5355     if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   5356         const fn_info = zcu.typeToFunc(zcu.navValue(cg.owner_nav).typeOf(zcu)).?;
   5357         if (Type.fromInterned(fn_info.return_type).isError(zcu)) {
   5358             // Functions with an empty error set are emitted with an error code
   5359             // return type and return zero so they can be function pointers coerced
   5360             // to functions that return anyerror.
   5361             const no_err_id = try cg.constInt(.anyerror, 0);
   5362             return try cg.body.emit(gpa, .OpReturnValue, .{ .value = no_err_id });
   5363         } else {
   5364             return try cg.body.emit(gpa, .OpReturn, {});
   5365         }
   5366     }
   5367 
   5368     const ptr = try cg.resolve(un_op);
   5369     const value = try cg.load(ret_ty, ptr, .{ .is_volatile = ptr_ty.isVolatilePtr(zcu) });
   5370     try cg.body.emit(gpa, .OpReturnValue, .{
   5371         .value = value,
   5372     });
   5373 }
   5374 
   5375 fn airTry(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5376     const gpa = cg.module.gpa;
   5377     const zcu = cg.module.zcu;
   5378     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   5379     const err_union_id = try cg.resolve(pl_op.operand);
   5380     const extra = cg.air.extraData(Air.Try, pl_op.payload);
   5381     const body: []const Air.Inst.Index = @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len]);
   5382 
   5383     const err_union_ty = cg.typeOf(pl_op.operand);
   5384     const payload_ty = cg.typeOfIndex(inst);
   5385 
   5386     const bool_ty_id = try cg.resolveType(.bool, .direct);
   5387 
   5388     const eu_layout = cg.errorUnionLayout(payload_ty);
   5389 
   5390     if (!err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
   5391         const err_id = if (eu_layout.payload_has_bits)
   5392             try cg.extractField(.anyerror, err_union_id, eu_layout.errorFieldIndex())
   5393         else
   5394             err_union_id;
   5395 
   5396         const zero_id = try cg.constInt(.anyerror, 0);
   5397         const is_err_id = cg.module.allocId();
   5398         try cg.body.emit(gpa, .OpINotEqual, .{
   5399             .id_result_type = bool_ty_id,
   5400             .id_result = is_err_id,
   5401             .operand_1 = err_id,
   5402             .operand_2 = zero_id,
   5403         });
   5404 
   5405         // When there is an error, we must evaluate `body`. Otherwise we must continue
   5406         // with the current body.
   5407         // Just generate a new block here, then generate a new block inline for the remainder of the body.
   5408 
   5409         const err_block = cg.module.allocId();
   5410         const ok_block = cg.module.allocId();
   5411 
   5412         switch (cg.control_flow) {
   5413             .structured => {
   5414                 // According to AIR documentation, this block is guaranteed
   5415                 // to not break and end in a return instruction. Thus,
   5416                 // for structured control flow, we can just naively use
   5417                 // the ok block as the merge block here.
   5418                 try cg.body.emit(gpa, .OpSelectionMerge, .{
   5419                     .merge_block = ok_block,
   5420                     .selection_control = .{},
   5421                 });
   5422             },
   5423             .unstructured => {},
   5424         }
   5425 
   5426         try cg.body.emit(gpa, .OpBranchConditional, .{
   5427             .condition = is_err_id,
   5428             .true_label = err_block,
   5429             .false_label = ok_block,
   5430         });
   5431 
   5432         try cg.beginSpvBlock(err_block);
   5433         try cg.genBody(body);
   5434 
   5435         try cg.beginSpvBlock(ok_block);
   5436     }
   5437 
   5438     if (!eu_layout.payload_has_bits) {
   5439         return null;
   5440     }
   5441 
   5442     // Now just extract the payload, if required.
   5443     return try cg.extractField(payload_ty, err_union_id, eu_layout.payloadFieldIndex());
   5444 }
   5445 
   5446 fn airErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5447     const zcu = cg.module.zcu;
   5448     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5449     const operand_id = try cg.resolve(ty_op.operand);
   5450     const err_union_ty = cg.typeOf(ty_op.operand);
   5451     const err_ty_id = try cg.resolveType(.anyerror, .direct);
   5452 
   5453     if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
   5454         // No error possible, so just return undefined.
   5455         return try cg.module.constUndef(err_ty_id);
   5456     }
   5457 
   5458     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   5459     const eu_layout = cg.errorUnionLayout(payload_ty);
   5460 
   5461     if (!eu_layout.payload_has_bits) {
   5462         // If no payload, error union is represented by error set.
   5463         return operand_id;
   5464     }
   5465 
   5466     return try cg.extractField(.anyerror, operand_id, eu_layout.errorFieldIndex());
   5467 }
   5468 
   5469 fn airErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5470     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5471     const operand_id = try cg.resolve(ty_op.operand);
   5472     const payload_ty = cg.typeOfIndex(inst);
   5473     const eu_layout = cg.errorUnionLayout(payload_ty);
   5474 
   5475     if (!eu_layout.payload_has_bits) {
   5476         return null; // No error possible.
   5477     }
   5478 
   5479     return try cg.extractField(payload_ty, operand_id, eu_layout.payloadFieldIndex());
   5480 }
   5481 
   5482 fn airWrapErrUnionErr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5483     const zcu = cg.module.zcu;
   5484     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5485     const err_union_ty = cg.typeOfIndex(inst);
   5486     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   5487     const operand_id = try cg.resolve(ty_op.operand);
   5488     const eu_layout = cg.errorUnionLayout(payload_ty);
   5489 
   5490     if (!eu_layout.payload_has_bits) {
   5491         return operand_id;
   5492     }
   5493 
   5494     const payload_ty_id = try cg.resolveType(payload_ty, .indirect);
   5495 
   5496     var members: [2]Id = undefined;
   5497     members[eu_layout.errorFieldIndex()] = operand_id;
   5498     members[eu_layout.payloadFieldIndex()] = try cg.module.constUndef(payload_ty_id);
   5499 
   5500     var types: [2]Type = undefined;
   5501     types[eu_layout.errorFieldIndex()] = .anyerror;
   5502     types[eu_layout.payloadFieldIndex()] = payload_ty;
   5503 
   5504     const err_union_ty_id = try cg.resolveType(err_union_ty, .direct);
   5505     return try cg.constructComposite(err_union_ty_id, &members);
   5506 }
   5507 
   5508 fn airWrapErrUnionPayload(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5509     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5510     const err_union_ty = cg.typeOfIndex(inst);
   5511     const operand_id = try cg.resolve(ty_op.operand);
   5512     const payload_ty = cg.typeOf(ty_op.operand);
   5513     const eu_layout = cg.errorUnionLayout(payload_ty);
   5514 
   5515     if (!eu_layout.payload_has_bits) {
   5516         return try cg.constInt(.anyerror, 0);
   5517     }
   5518 
   5519     var members: [2]Id = undefined;
   5520     members[eu_layout.errorFieldIndex()] = try cg.constInt(.anyerror, 0);
   5521     members[eu_layout.payloadFieldIndex()] = try cg.convertToIndirect(payload_ty, operand_id);
   5522 
   5523     var types: [2]Type = undefined;
   5524     types[eu_layout.errorFieldIndex()] = .anyerror;
   5525     types[eu_layout.payloadFieldIndex()] = payload_ty;
   5526 
   5527     const err_union_ty_id = try cg.resolveType(err_union_ty, .direct);
   5528     return try cg.constructComposite(err_union_ty_id, &members);
   5529 }
   5530 
   5531 fn airIsNull(cg: *CodeGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { is_null, is_non_null }) !?Id {
   5532     const zcu = cg.module.zcu;
   5533     const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   5534     const operand_id = try cg.resolve(un_op);
   5535     const operand_ty = cg.typeOf(un_op);
   5536     const optional_ty = if (is_pointer) operand_ty.childType(zcu) else operand_ty;
   5537     const payload_ty = optional_ty.optionalChild(zcu);
   5538 
   5539     const bool_ty_id = try cg.resolveType(.bool, .direct);
   5540 
   5541     if (optional_ty.optionalReprIsPayload(zcu)) {
   5542         // Pointer payload represents nullability: pointer or slice.
   5543         const loaded_id = if (is_pointer)
   5544             try cg.load(optional_ty, operand_id, .{})
   5545         else
   5546             operand_id;
   5547 
   5548         const ptr_ty = if (payload_ty.isSlice(zcu))
   5549             payload_ty.slicePtrFieldType(zcu)
   5550         else
   5551             payload_ty;
   5552 
   5553         const ptr_id = if (payload_ty.isSlice(zcu))
   5554             try cg.extractField(ptr_ty, loaded_id, 0)
   5555         else
   5556             loaded_id;
   5557 
   5558         const ptr_ty_id = try cg.resolveType(ptr_ty, .direct);
   5559         const null_id = try cg.module.constNull(ptr_ty_id);
   5560         const null_tmp: Temporary = .init(ptr_ty, null_id);
   5561         const ptr: Temporary = .init(ptr_ty, ptr_id);
   5562 
   5563         const op: std.math.CompareOperator = switch (pred) {
   5564             .is_null => .eq,
   5565             .is_non_null => .neq,
   5566         };
   5567         const result = try cg.cmp(op, ptr, null_tmp);
   5568         return try result.materialize(cg);
   5569     }
   5570 
   5571     const is_non_null_id = blk: {
   5572         if (is_pointer) {
   5573             if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   5574                 const storage_class = cg.module.storageClass(operand_ty.ptrAddressSpace(zcu));
   5575                 const bool_indirect_ty_id = try cg.resolveType(.bool, .indirect);
   5576                 const bool_ptr_ty_id = try cg.module.ptrType(bool_indirect_ty_id, storage_class);
   5577                 const tag_ptr_id = try cg.accessChain(bool_ptr_ty_id, operand_id, &.{1});
   5578                 break :blk try cg.load(.bool, tag_ptr_id, .{});
   5579             }
   5580 
   5581             break :blk try cg.load(.bool, operand_id, .{});
   5582         }
   5583 
   5584         break :blk if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu))
   5585             try cg.extractField(.bool, operand_id, 1)
   5586         else
   5587             // Optional representation is bool indicating whether the optional is set
   5588             // Optionals with no payload are represented as an (indirect) bool, so convert
   5589             // it back to the direct bool here.
   5590             try cg.convertToDirect(.bool, operand_id);
   5591     };
   5592 
   5593     return switch (pred) {
   5594         .is_null => blk: {
   5595             // Invert condition
   5596             const result_id = cg.module.allocId();
   5597             try cg.body.emit(cg.module.gpa, .OpLogicalNot, .{
   5598                 .id_result_type = bool_ty_id,
   5599                 .id_result = result_id,
   5600                 .operand = is_non_null_id,
   5601             });
   5602             break :blk result_id;
   5603         },
   5604         .is_non_null => is_non_null_id,
   5605     };
   5606 }
   5607 
   5608 fn airIsErr(cg: *CodeGen, inst: Air.Inst.Index, pred: enum { is_err, is_non_err }) !?Id {
   5609     const zcu = cg.module.zcu;
   5610     const un_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
   5611     const operand_id = try cg.resolve(un_op);
   5612     const err_union_ty = cg.typeOf(un_op);
   5613 
   5614     if (err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) {
   5615         return try cg.constBool(pred == .is_non_err, .direct);
   5616     }
   5617 
   5618     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   5619     const eu_layout = cg.errorUnionLayout(payload_ty);
   5620     const bool_ty_id = try cg.resolveType(.bool, .direct);
   5621 
   5622     const error_id = if (!eu_layout.payload_has_bits)
   5623         operand_id
   5624     else
   5625         try cg.extractField(.anyerror, operand_id, eu_layout.errorFieldIndex());
   5626 
   5627     const result_id = cg.module.allocId();
   5628     switch (pred) {
   5629         inline else => |pred_ct| try cg.body.emit(
   5630             cg.module.gpa,
   5631             switch (pred_ct) {
   5632                 .is_err => .OpINotEqual,
   5633                 .is_non_err => .OpIEqual,
   5634             },
   5635             .{
   5636                 .id_result_type = bool_ty_id,
   5637                 .id_result = result_id,
   5638                 .operand_1 = error_id,
   5639                 .operand_2 = try cg.constInt(.anyerror, 0),
   5640             },
   5641         ),
   5642     }
   5643     return result_id;
   5644 }
   5645 
   5646 fn airUnwrapOptional(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5647     const zcu = cg.module.zcu;
   5648     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5649     const operand_id = try cg.resolve(ty_op.operand);
   5650     const optional_ty = cg.typeOf(ty_op.operand);
   5651     const payload_ty = cg.typeOfIndex(inst);
   5652 
   5653     if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return null;
   5654 
   5655     if (optional_ty.optionalReprIsPayload(zcu)) {
   5656         return operand_id;
   5657     }
   5658 
   5659     return try cg.extractField(payload_ty, operand_id, 0);
   5660 }
   5661 
   5662 fn airUnwrapOptionalPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5663     const zcu = cg.module.zcu;
   5664     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5665     const operand_id = try cg.resolve(ty_op.operand);
   5666     const operand_ty = cg.typeOf(ty_op.operand);
   5667     const optional_ty = operand_ty.childType(zcu);
   5668     const payload_ty = optional_ty.optionalChild(zcu);
   5669     const result_ty = cg.typeOfIndex(inst);
   5670     const result_ty_id = try cg.resolveType(result_ty, .direct);
   5671 
   5672     if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   5673         // There is no payload, but we still need to return a valid pointer.
   5674         // We can just return anything here, so just return a pointer to the operand.
   5675         return try cg.bitCast(result_ty, operand_ty, operand_id);
   5676     }
   5677 
   5678     if (optional_ty.optionalReprIsPayload(zcu)) {
   5679         // They are the same value.
   5680         return try cg.bitCast(result_ty, operand_ty, operand_id);
   5681     }
   5682 
   5683     return try cg.accessChain(result_ty_id, operand_id, &.{0});
   5684 }
   5685 
   5686 fn airWrapOptional(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5687     const zcu = cg.module.zcu;
   5688     const ty_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
   5689     const payload_ty = cg.typeOf(ty_op.operand);
   5690 
   5691     if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
   5692         return try cg.constBool(true, .indirect);
   5693     }
   5694 
   5695     const operand_id = try cg.resolve(ty_op.operand);
   5696 
   5697     const optional_ty = cg.typeOfIndex(inst);
   5698     if (optional_ty.optionalReprIsPayload(zcu)) {
   5699         return operand_id;
   5700     }
   5701 
   5702     const payload_id = try cg.convertToIndirect(payload_ty, operand_id);
   5703     const members = [_]Id{ payload_id, try cg.constBool(true, .indirect) };
   5704     const optional_ty_id = try cg.resolveType(optional_ty, .direct);
   5705     return try cg.constructComposite(optional_ty_id, &members);
   5706 }
   5707 
   5708 fn airSwitchBr(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5709     const gpa = cg.module.gpa;
   5710     const pt = cg.pt;
   5711     const zcu = cg.module.zcu;
   5712     const target = cg.module.zcu.getTarget();
   5713     const switch_br = cg.air.unwrapSwitch(inst);
   5714     const cond_ty = cg.typeOf(switch_br.operand);
   5715     const cond = try cg.resolve(switch_br.operand);
   5716     var cond_indirect = try cg.convertToIndirect(cond_ty, cond);
   5717 
   5718     const cond_words: u32 = switch (cond_ty.zigTypeTag(zcu)) {
   5719         .bool, .error_set => 1,
   5720         .int => blk: {
   5721             const bits = cond_ty.intInfo(zcu).bits;
   5722             const backing_bits, const big_int = cg.module.backingIntBits(bits);
   5723             if (big_int) return cg.todo("implement composite int switch", .{});
   5724             break :blk if (backing_bits <= 32) 1 else 2;
   5725         },
   5726         .@"enum" => blk: {
   5727             const int_ty = cond_ty.intTagType(zcu);
   5728             const int_info = int_ty.intInfo(zcu);
   5729             const backing_bits, const big_int = cg.module.backingIntBits(int_info.bits);
   5730             if (big_int) return cg.todo("implement composite int switch", .{});
   5731             break :blk if (backing_bits <= 32) 1 else 2;
   5732         },
   5733         .pointer => blk: {
   5734             cond_indirect = try cg.intFromPtr(cond_indirect);
   5735             break :blk target.ptrBitWidth() / 32;
   5736         },
   5737         // TODO: Figure out which types apply here, and work around them as we can only do integers.
   5738         else => return cg.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag(zcu))}),
   5739     };
   5740 
   5741     const num_cases = switch_br.cases_len;
   5742 
   5743     // Compute the total number of arms that we need.
   5744     // Zig switches are grouped by condition, so we need to loop through all of them
   5745     const num_conditions = blk: {
   5746         var num_conditions: u32 = 0;
   5747         var it = switch_br.iterateCases();
   5748         while (it.next()) |case| {
   5749             if (case.ranges.len > 0) return cg.todo("switch with ranges", .{});
   5750             num_conditions += @intCast(case.items.len);
   5751         }
   5752         break :blk num_conditions;
   5753     };
   5754 
   5755     // First, pre-allocate the labels for the cases.
   5756     const case_labels = cg.module.allocIds(num_cases);
   5757     // We always need the default case - if zig has none, we will generate unreachable there.
   5758     const default = cg.module.allocId();
   5759 
   5760     const merge_label = switch (cg.control_flow) {
   5761         .structured => cg.module.allocId(),
   5762         .unstructured => null,
   5763     };
   5764 
   5765     if (cg.control_flow == .structured) {
   5766         try cg.body.emit(gpa, .OpSelectionMerge, .{
   5767             .merge_block = merge_label.?,
   5768             .selection_control = .{},
   5769         });
   5770     }
   5771 
   5772     // Emit the instruction before generating the blocks.
   5773     try cg.body.emitRaw(gpa, .OpSwitch, 2 + (cond_words + 1) * num_conditions);
   5774     cg.body.writeOperand(Id, cond_indirect);
   5775     cg.body.writeOperand(Id, default);
   5776 
   5777     // Emit each of the cases
   5778     {
   5779         var it = switch_br.iterateCases();
   5780         while (it.next()) |case| {
   5781             // SPIR-V needs a literal here, which' width depends on the case condition.
   5782             const label = case_labels.at(case.idx);
   5783 
   5784             for (case.items) |item| {
   5785                 const value = (try cg.air.value(item, pt)) orelse unreachable;
   5786                 const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) {
   5787                     .bool, .int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu),
   5788                     .@"enum" => blk: {
   5789                         // TODO: figure out of cond_ty is correct (something with enum literals)
   5790                         break :blk (try value.intFromEnum(cond_ty, pt)).toUnsignedInt(zcu); // TODO: composite integer constants
   5791                     },
   5792                     .error_set => value.getErrorInt(zcu),
   5793                     .pointer => value.toUnsignedInt(zcu),
   5794                     else => unreachable,
   5795                 };
   5796                 const int_lit: spec.LiteralContextDependentNumber = switch (cond_words) {
   5797                     1 => .{ .uint32 = @intCast(int_val) },
   5798                     2 => .{ .uint64 = int_val },
   5799                     else => unreachable,
   5800                 };
   5801                 cg.body.writeOperand(spec.LiteralContextDependentNumber, int_lit);
   5802                 cg.body.writeOperand(Id, label);
   5803             }
   5804         }
   5805     }
   5806 
   5807     var incoming_structured_blocks: std.ArrayList(ControlFlow.Structured.Block.Incoming) = .empty;
   5808     defer incoming_structured_blocks.deinit(gpa);
   5809 
   5810     if (cg.control_flow == .structured) {
   5811         try incoming_structured_blocks.ensureUnusedCapacity(gpa, num_cases + 1);
   5812     }
   5813 
   5814     // Now, finally, we can start emitting each of the cases.
   5815     var it = switch_br.iterateCases();
   5816     while (it.next()) |case| {
   5817         const label = case_labels.at(case.idx);
   5818 
   5819         try cg.beginSpvBlock(label);
   5820 
   5821         switch (cg.control_flow) {
   5822             .structured => {
   5823                 const next_block = try cg.genStructuredBody(.selection, case.body);
   5824                 incoming_structured_blocks.appendAssumeCapacity(.{
   5825                     .src_label = cg.block_label,
   5826                     .next_block = next_block,
   5827                 });
   5828 
   5829                 try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label.? });
   5830             },
   5831             .unstructured => {
   5832                 try cg.genBody(case.body);
   5833             },
   5834         }
   5835     }
   5836 
   5837     const else_body = it.elseBody();
   5838     try cg.beginSpvBlock(default);
   5839     if (else_body.len != 0) {
   5840         switch (cg.control_flow) {
   5841             .structured => {
   5842                 const next_block = try cg.genStructuredBody(.selection, else_body);
   5843                 incoming_structured_blocks.appendAssumeCapacity(.{
   5844                     .src_label = cg.block_label,
   5845                     .next_block = next_block,
   5846                 });
   5847 
   5848                 try cg.body.emit(gpa, .OpBranch, .{ .target_label = merge_label.? });
   5849             },
   5850             .unstructured => {
   5851                 try cg.genBody(else_body);
   5852             },
   5853         }
   5854     } else {
   5855         try cg.body.emit(gpa, .OpUnreachable, {});
   5856     }
   5857 
   5858     if (cg.control_flow == .structured) {
   5859         try cg.beginSpvBlock(merge_label.?);
   5860         const next_block = try cg.structuredNextBlock(incoming_structured_blocks.items);
   5861         try cg.structuredBreak(next_block);
   5862     }
   5863 }
   5864 
   5865 fn airUnreach(cg: *CodeGen) !void {
   5866     try cg.body.emit(cg.module.gpa, .OpUnreachable, {});
   5867 }
   5868 
   5869 fn airDbgStmt(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5870     const zcu = cg.module.zcu;
   5871     const dbg_stmt = cg.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
   5872     const path = zcu.navFileScope(cg.owner_nav).sub_file_path;
   5873 
   5874     if (zcu.comp.config.root_strip) return;
   5875 
   5876     try cg.body.emit(cg.module.gpa, .OpLine, .{
   5877         .file = try cg.module.debugString(path),
   5878         .line = cg.base_line + dbg_stmt.line + 1,
   5879         .column = dbg_stmt.column + 1,
   5880     });
   5881 }
   5882 
   5883 fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5884     const zcu = cg.module.zcu;
   5885     const inst_datas = cg.air.instructions.items(.data);
   5886     const extra = cg.air.extraData(Air.DbgInlineBlock, inst_datas[@intFromEnum(inst)].ty_pl.payload);
   5887     const old_base_line = cg.base_line;
   5888     defer cg.base_line = old_base_line;
   5889     cg.base_line = zcu.navSrcLine(zcu.funcInfo(extra.data.func).owner_nav);
   5890     return cg.lowerBlock(inst, @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.body_len]));
   5891 }
   5892 
   5893 fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void {
   5894     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   5895     const target_id = try cg.resolve(pl_op.operand);
   5896     const name: Air.NullTerminatedString = @enumFromInt(pl_op.payload);
   5897     try cg.module.debugName(target_id, name.toSlice(cg.air));
   5898 }
   5899 
   5900 fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   5901     const gpa = cg.module.gpa;
   5902     const zcu = cg.module.zcu;
   5903     const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
   5904     const extra = cg.air.extraData(Air.Asm, ty_pl.payload);
   5905 
   5906     const is_volatile = extra.data.flags.is_volatile;
   5907     const outputs_len = extra.data.flags.outputs_len;
   5908 
   5909     if (!is_volatile and cg.liveness.isUnused(inst)) return null;
   5910 
   5911     var extra_i: usize = extra.end;
   5912     const outputs: []const Air.Inst.Ref = @ptrCast(cg.air.extra.items[extra_i..][0..outputs_len]);
   5913     extra_i += outputs.len;
   5914     const inputs: []const Air.Inst.Ref = @ptrCast(cg.air.extra.items[extra_i..][0..extra.data.inputs_len]);
   5915     extra_i += inputs.len;
   5916 
   5917     if (outputs.len > 1) {
   5918         return cg.todo("implement inline asm with more than 1 output", .{});
   5919     }
   5920 
   5921     var ass: Assembler = .{ .cg = cg };
   5922     defer ass.deinit();
   5923 
   5924     var output_extra_i = extra_i;
   5925     for (outputs) |output| {
   5926         if (output != .none) {
   5927             return cg.todo("implement inline asm with non-returned output", .{});
   5928         }
   5929         const extra_bytes = std.mem.sliceAsBytes(cg.air.extra.items[extra_i..]);
   5930         const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(cg.air.extra.items[extra_i..]), 0);
   5931         const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   5932         extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5933         // TODO: Record output and use it somewhere.
   5934     }
   5935 
   5936     for (inputs) |input| {
   5937         const extra_bytes = std.mem.sliceAsBytes(cg.air.extra.items[extra_i..]);
   5938         const constraint = std.mem.sliceTo(extra_bytes, 0);
   5939         const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   5940         // This equation accounts for the fact that even if we have exactly 4 bytes
   5941         // for the string, we still use the next u32 for the null terminator.
   5942         extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   5943 
   5944         const input_ty = cg.typeOf(input);
   5945 
   5946         if (std.mem.eql(u8, constraint, "c")) {
   5947             // constant
   5948             const val = (try cg.air.value(input, cg.pt)) orelse {
   5949                 return cg.fail("assembly inputs with 'c' constraint have to be compile-time known", .{});
   5950             };
   5951 
   5952             // TODO: This entire function should be handled a bit better...
   5953             const ip = &zcu.intern_pool;
   5954             switch (ip.indexToKey(val.toIntern())) {
   5955                 .int_type,
   5956                 .ptr_type,
   5957                 .array_type,
   5958                 .vector_type,
   5959                 .opt_type,
   5960                 .anyframe_type,
   5961                 .error_union_type,
   5962                 .simple_type,
   5963                 .struct_type,
   5964                 .union_type,
   5965                 .opaque_type,
   5966                 .enum_type,
   5967                 .func_type,
   5968                 .error_set_type,
   5969                 .inferred_error_set_type,
   5970                 => unreachable, // types, not values
   5971 
   5972                 .undef => return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}),
   5973 
   5974                 .int => try ass.value_map.put(gpa, name, .{ .constant = @intCast(val.toUnsignedInt(zcu)) }),
   5975                 .enum_literal => |str| try ass.value_map.put(gpa, name, .{ .string = str.toSlice(ip) }),
   5976 
   5977                 else => unreachable, // TODO
   5978             }
   5979         } else if (std.mem.eql(u8, constraint, "t")) {
   5980             // type
   5981             if (input_ty.zigTypeTag(zcu) == .type) {
   5982                 // This assembly input is a type instead of a value.
   5983                 // That's fine for now, just make sure to resolve it as such.
   5984                 const val = (try cg.air.value(input, cg.pt)).?;
   5985                 const ty_id = try cg.resolveType(val.toType(), .direct);
   5986                 try ass.value_map.put(gpa, name, .{ .ty = ty_id });
   5987             } else {
   5988                 const ty_id = try cg.resolveType(input_ty, .direct);
   5989                 try ass.value_map.put(gpa, name, .{ .ty = ty_id });
   5990             }
   5991         } else {
   5992             if (input_ty.zigTypeTag(zcu) == .type) {
   5993                 return cg.fail("use the 't' constraint to supply types to SPIR-V inline assembly", .{});
   5994             }
   5995 
   5996             const val_id = try cg.resolve(input);
   5997             try ass.value_map.put(gpa, name, .{ .value = val_id });
   5998         }
   5999     }
   6000 
   6001     // TODO: do something with clobbers
   6002     _ = extra.data.clobbers;
   6003 
   6004     const asm_source = std.mem.sliceAsBytes(cg.air.extra.items[extra_i..])[0..extra.data.source_len];
   6005 
   6006     ass.assemble(asm_source) catch |err| switch (err) {
   6007         error.AssembleFail => {
   6008             // TODO: For now the compiler only supports a single error message per decl,
   6009             // so to translate the possible multiple errors from the assembler, emit
   6010             // them as notes here.
   6011             // TODO: Translate proper error locations.
   6012             assert(ass.errors.items.len != 0);
   6013             assert(cg.error_msg == null);
   6014             const src_loc = zcu.navSrcLoc(cg.owner_nav);
   6015             cg.error_msg = try Zcu.ErrorMsg.create(zcu.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{});
   6016             const notes = try zcu.gpa.alloc(Zcu.ErrorMsg, ass.errors.items.len);
   6017 
   6018             // Sub-scope to prevent `return error.CodegenFail` from running the errdefers.
   6019             {
   6020                 errdefer zcu.gpa.free(notes);
   6021                 var i: usize = 0;
   6022                 errdefer for (notes[0..i]) |*note| {
   6023                     note.deinit(zcu.gpa);
   6024                 };
   6025 
   6026                 while (i < ass.errors.items.len) : (i += 1) {
   6027                     notes[i] = try Zcu.ErrorMsg.init(zcu.gpa, src_loc, "{s}", .{ass.errors.items[i].msg});
   6028                 }
   6029             }
   6030             cg.error_msg.?.notes = notes;
   6031             return error.CodegenFail;
   6032         },
   6033         else => |others| return others,
   6034     };
   6035 
   6036     for (outputs) |output| {
   6037         _ = output;
   6038         const extra_bytes = std.mem.sliceAsBytes(cg.air.extra.items[output_extra_i..]);
   6039         const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(cg.air.extra.items[output_extra_i..]), 0);
   6040         const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   6041         output_extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   6042 
   6043         const result = ass.value_map.get(name) orelse return {
   6044             return cg.fail("invalid asm output '{s}'", .{name});
   6045         };
   6046 
   6047         switch (result) {
   6048             .just_declared, .unresolved_forward_reference => unreachable,
   6049             .ty => return cg.fail("cannot return spir-v type as value from assembly", .{}),
   6050             .value => |ref| return ref,
   6051             .constant, .string => return cg.fail("cannot return constant from assembly", .{}),
   6052         }
   6053 
   6054         // TODO: Multiple results
   6055         // TODO: Check that the output type from assembly is the same as the type actually expected by Zig.
   6056     }
   6057 
   6058     return null;
   6059 }
   6060 
   6061 fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !?Id {
   6062     _ = modifier;
   6063 
   6064     const gpa = cg.module.gpa;
   6065     const zcu = cg.module.zcu;
   6066     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   6067     const extra = cg.air.extraData(Air.Call, pl_op.payload);
   6068     const args: []const Air.Inst.Ref = @ptrCast(cg.air.extra.items[extra.end..][0..extra.data.args_len]);
   6069     const callee_ty = cg.typeOf(pl_op.operand);
   6070     const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) {
   6071         .@"fn" => callee_ty,
   6072         .pointer => return cg.fail("cannot call function pointers", .{}),
   6073         else => unreachable,
   6074     };
   6075     const fn_info = zcu.typeToFunc(zig_fn_ty).?;
   6076     const return_type = fn_info.return_type;
   6077 
   6078     const result_type_id = try cg.resolveFnReturnType(.fromInterned(return_type));
   6079     const result_id = cg.module.allocId();
   6080     const callee_id = try cg.resolve(pl_op.operand);
   6081 
   6082     comptime assert(zig_call_abi_ver == 3);
   6083 
   6084     const scratch_top = cg.id_scratch.items.len;
   6085     defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
   6086     const params = try cg.id_scratch.addManyAsSlice(gpa, args.len);
   6087 
   6088     var n_params: usize = 0;
   6089     for (args) |arg| {
   6090         // Note: resolve() might emit instructions, so we need to call it
   6091         // before starting to emit OpFunctionCall instructions. Hence the
   6092         // temporary params buffer.
   6093         const arg_ty = cg.typeOf(arg);
   6094         if (!arg_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
   6095         const arg_id = try cg.resolve(arg);
   6096 
   6097         params[n_params] = arg_id;
   6098         n_params += 1;
   6099     }
   6100 
   6101     try cg.body.emit(gpa, .OpFunctionCall, .{
   6102         .id_result_type = result_type_id,
   6103         .id_result = result_id,
   6104         .function = callee_id,
   6105         .id_ref_3 = params[0..n_params],
   6106     });
   6107 
   6108     if (cg.liveness.isUnused(inst) or !Type.fromInterned(return_type).hasRuntimeBitsIgnoreComptime(zcu)) {
   6109         return null;
   6110     }
   6111 
   6112     return result_id;
   6113 }
   6114 
   6115 fn builtin3D(
   6116     cg: *CodeGen,
   6117     result_ty: Type,
   6118     builtin: spec.BuiltIn,
   6119     dimension: u32,
   6120     out_of_range_value: anytype,
   6121 ) !Id {
   6122     const gpa = cg.module.gpa;
   6123     if (dimension >= 3) return try cg.constInt(result_ty, out_of_range_value);
   6124     const u32_ty_id = try cg.module.intType(.unsigned, 32);
   6125     const vec_ty_id = try cg.module.vectorType(3, u32_ty_id);
   6126     const ptr_ty_id = try cg.module.ptrType(vec_ty_id, .input);
   6127     const spv_decl_index = try cg.module.builtin(ptr_ty_id, builtin, .input);
   6128     try cg.module.decl_deps.append(gpa, spv_decl_index);
   6129     const ptr_id = cg.module.declPtr(spv_decl_index).result_id;
   6130     const vec_id = cg.module.allocId();
   6131     try cg.body.emit(gpa, .OpLoad, .{
   6132         .id_result_type = vec_ty_id,
   6133         .id_result = vec_id,
   6134         .pointer = ptr_id,
   6135     });
   6136     return try cg.extractVectorComponent(result_ty, vec_id, dimension);
   6137 }
   6138 
   6139 fn airWorkItemId(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   6140     if (cg.liveness.isUnused(inst)) return null;
   6141     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   6142     const dimension = pl_op.payload;
   6143     return try cg.builtin3D(.u32, .local_invocation_id, dimension, 0);
   6144 }
   6145 
   6146 // TODO: this must be an OpConstant/OpSpec but even then the driver crashes.
   6147 fn airWorkGroupSize(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   6148     if (cg.liveness.isUnused(inst)) return null;
   6149     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   6150     const dimension = pl_op.payload;
   6151     return try cg.builtin3D(.u32, .workgroup_size, dimension, 0);
   6152 }
   6153 
   6154 fn airWorkGroupId(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
   6155     if (cg.liveness.isUnused(inst)) return null;
   6156     const pl_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
   6157     const dimension = pl_op.payload;
   6158     return try cg.builtin3D(.u32, .workgroup_id, dimension, 0);
   6159 }
   6160 
   6161 fn typeOf(cg: *CodeGen, inst: Air.Inst.Ref) Type {
   6162     const zcu = cg.module.zcu;
   6163     return cg.air.typeOf(inst, &zcu.intern_pool);
   6164 }
   6165 
   6166 fn typeOfIndex(cg: *CodeGen, inst: Air.Inst.Index) Type {
   6167     const zcu = cg.module.zcu;
   6168     return cg.air.typeOfIndex(inst, &zcu.intern_pool);
   6169 }