zig

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

blob ed04ee47 (254316B) - Raw


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