zig

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

blob ded73d6a (195237B) - 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").Value;
     11 const LazySrcLoc = Module.LazySrcLoc;
     12 const Air = @import("../Air.zig");
     13 const Zir = @import("../Zir.zig");
     14 const Liveness = @import("../Liveness.zig");
     15 const InternPool = @import("../InternPool.zig");
     16 
     17 const spec = @import("spirv/spec.zig");
     18 const Opcode = spec.Opcode;
     19 const Word = spec.Word;
     20 const IdRef = spec.IdRef;
     21 const IdResult = spec.IdResult;
     22 const IdResultType = spec.IdResultType;
     23 const StorageClass = spec.StorageClass;
     24 
     25 const SpvModule = @import("spirv/Module.zig");
     26 const CacheRef = SpvModule.CacheRef;
     27 const CacheString = SpvModule.CacheString;
     28 
     29 const SpvSection = @import("spirv/Section.zig");
     30 const SpvAssembler = @import("spirv/Assembler.zig");
     31 
     32 const InstMap = std.AutoHashMapUnmanaged(Air.Inst.Index, IdRef);
     33 
     34 /// We want to store some extra facts about types as mapped from Zig to SPIR-V.
     35 /// This structure is used to keep that extra information, as well as
     36 /// the cached reference to the type.
     37 const SpvTypeInfo = struct {
     38     ty_ref: CacheRef,
     39 };
     40 
     41 const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, SpvTypeInfo);
     42 
     43 const IncomingBlock = struct {
     44     src_label_id: IdRef,
     45     break_value_id: IdRef,
     46 };
     47 
     48 const Block = struct {
     49     label_id: ?IdRef,
     50     incoming_blocks: std.ArrayListUnmanaged(IncomingBlock),
     51 };
     52 
     53 const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block);
     54 
     55 /// This structure holds information that is relevant to the entire compilation,
     56 /// in contrast to `DeclGen`, which only holds relevant information about a
     57 /// single decl.
     58 pub const Object = struct {
     59     /// A general-purpose allocator that can be used for any allocation for this Object.
     60     gpa: Allocator,
     61 
     62     /// the SPIR-V module that represents the final binary.
     63     spv: SpvModule,
     64 
     65     /// The Zig module that this object file is generated for.
     66     /// A map of Zig decl indices to SPIR-V decl indices.
     67     decl_link: std.AutoHashMapUnmanaged(Decl.Index, SpvModule.Decl.Index) = .{},
     68 
     69     /// A map of Zig InternPool indices for anonymous decls to SPIR-V decl indices.
     70     anon_decl_link: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index) = .{},
     71 
     72     /// A map that maps AIR intern pool indices to SPIR-V cache references (which
     73     /// is basically the same thing except for SPIR-V).
     74     /// This map is typically only used for structures that are deemed heavy enough
     75     /// that it is worth to store them here. The SPIR-V module also interns types,
     76     /// and so the main purpose of this map is to avoid recomputation and to
     77     /// cache extra information about the type rather than to aid in validity
     78     /// of the SPIR-V module.
     79     type_map: TypeMap = .{},
     80 
     81     pub fn init(gpa: Allocator) Object {
     82         return .{
     83             .gpa = gpa,
     84             .spv = SpvModule.init(gpa),
     85         };
     86     }
     87 
     88     pub fn deinit(self: *Object) void {
     89         self.spv.deinit();
     90         self.decl_link.deinit(self.gpa);
     91         self.anon_decl_link.deinit(self.gpa);
     92         self.type_map.deinit(self.gpa);
     93     }
     94 
     95     fn genDecl(
     96         self: *Object,
     97         mod: *Module,
     98         decl_index: Decl.Index,
     99         air: Air,
    100         liveness: Liveness,
    101     ) !void {
    102         var decl_gen = DeclGen{
    103             .gpa = self.gpa,
    104             .object = self,
    105             .module = mod,
    106             .spv = &self.spv,
    107             .decl_index = decl_index,
    108             .air = air,
    109             .liveness = liveness,
    110             .type_map = &self.type_map,
    111             .current_block_label_id = undefined,
    112         };
    113         defer decl_gen.deinit();
    114 
    115         decl_gen.genDecl() catch |err| switch (err) {
    116             error.CodegenFail => {
    117                 try mod.failed_decls.put(mod.gpa, decl_index, decl_gen.error_msg.?);
    118             },
    119             else => |other| {
    120                 // There might be an error that happened *after* self.error_msg
    121                 // was already allocated, so be sure to free it.
    122                 if (decl_gen.error_msg) |error_msg| {
    123                     error_msg.deinit(mod.gpa);
    124                 }
    125 
    126                 return other;
    127             },
    128         };
    129     }
    130 
    131     pub fn updateFunc(
    132         self: *Object,
    133         mod: *Module,
    134         func_index: InternPool.Index,
    135         air: Air,
    136         liveness: Liveness,
    137     ) !void {
    138         const decl_index = mod.funcInfo(func_index).owner_decl;
    139         // TODO: Separate types for generating decls and functions?
    140         try self.genDecl(mod, decl_index, air, liveness);
    141     }
    142 
    143     pub fn updateDecl(
    144         self: *Object,
    145         mod: *Module,
    146         decl_index: Decl.Index,
    147     ) !void {
    148         try self.genDecl(mod, decl_index, undefined, undefined);
    149     }
    150 
    151     /// Fetch or allocate a result id for decl index. This function also marks the decl as alive.
    152     /// Note: Function does not actually generate the decl, it just allocates an index.
    153     pub fn resolveDecl(self: *Object, mod: *Module, decl_index: Decl.Index) !SpvModule.Decl.Index {
    154         const decl = mod.declPtr(decl_index);
    155         try mod.markDeclAlive(decl);
    156 
    157         const entry = try self.decl_link.getOrPut(self.gpa, decl_index);
    158         if (!entry.found_existing) {
    159             // TODO: Extern fn?
    160             const kind: SpvModule.DeclKind = if (decl.val.isFuncBody(mod))
    161                 .func
    162             else
    163                 .global;
    164 
    165             entry.value_ptr.* = try self.spv.allocDecl(kind);
    166         }
    167 
    168         return entry.value_ptr.*;
    169     }
    170 };
    171 
    172 /// This structure is used to compile a declaration, and contains all relevant meta-information to deal with that.
    173 const DeclGen = struct {
    174     /// A general-purpose allocator that can be used for any allocations for this DeclGen.
    175     gpa: Allocator,
    176 
    177     /// The object that this decl is generated into.
    178     object: *Object,
    179 
    180     /// The Zig module that we are generating decls for.
    181     module: *Module,
    182 
    183     /// The SPIR-V module that instructions should be emitted into.
    184     /// This is the same as `self.object.spv`, repeated here for brevity.
    185     spv: *SpvModule,
    186 
    187     /// The decl we are currently generating code for.
    188     decl_index: Decl.Index,
    189 
    190     /// The intermediate code of the declaration we are currently generating. Note: If
    191     /// the declaration is not a function, this value will be undefined!
    192     air: Air,
    193 
    194     /// The liveness analysis of the intermediate code for the declaration we are currently generating.
    195     /// Note: If the declaration is not a function, this value will be undefined!
    196     liveness: Liveness,
    197 
    198     /// An array of function argument result-ids. Each index corresponds with the
    199     /// function argument of the same index.
    200     args: std.ArrayListUnmanaged(IdRef) = .{},
    201 
    202     /// A counter to keep track of how many `arg` instructions we've seen yet.
    203     next_arg_index: u32 = 0,
    204 
    205     /// A map keeping track of which instruction generated which result-id.
    206     inst_results: InstMap = .{},
    207 
    208     /// A map that maps AIR intern pool indices to SPIR-V cache references.
    209     /// See Object.type_map
    210     type_map: *TypeMap,
    211 
    212     /// We need to keep track of result ids for block labels, as well as the 'incoming'
    213     /// blocks for a block.
    214     blocks: BlockMap = .{},
    215 
    216     /// The label of the SPIR-V block we are currently generating.
    217     current_block_label_id: IdRef,
    218 
    219     /// The code (prologue and body) for the function we are currently generating code for.
    220     func: SpvModule.Fn = .{},
    221 
    222     /// Stack of the base offsets of the current decl, which is what `dbg_stmt` is relative to.
    223     /// This is a stack to keep track of inline functions.
    224     base_line_stack: std.ArrayListUnmanaged(u32) = .{},
    225 
    226     /// If `gen` returned `Error.CodegenFail`, this contains an explanatory message.
    227     /// Memory is owned by `module.gpa`.
    228     error_msg: ?*Module.ErrorMsg = null,
    229 
    230     /// Possible errors the `genDecl` function may return.
    231     const Error = error{ CodegenFail, OutOfMemory };
    232 
    233     /// This structure is used to return information about a type typically used for
    234     /// arithmetic operations. These types may either be integers, floats, or a vector
    235     /// of these. Most scalar operations also work on vectors, so we can easily represent
    236     /// those as arithmetic types. If the type is a scalar, 'inner type' refers to the
    237     /// scalar type. Otherwise, if its a vector, it refers to the vector's element type.
    238     const ArithmeticTypeInfo = struct {
    239         /// A classification of the inner type.
    240         const Class = enum {
    241             /// A boolean.
    242             bool,
    243 
    244             /// A regular, **native**, integer.
    245             /// This is only returned when the backend supports this int as a native type (when
    246             /// the relevant capability is enabled).
    247             integer,
    248 
    249             /// A regular float. These are all required to be natively supported. Floating points
    250             /// for which the relevant capability is not enabled are not emulated.
    251             float,
    252 
    253             /// An integer of a 'strange' size (which' bit size is not the same as its backing
    254             /// type. **Note**: this may **also** include power-of-2 integers for which the
    255             /// relevant capability is not enabled), but still within the limits of the largest
    256             /// natively supported integer type.
    257             strange_integer,
    258 
    259             /// An integer with more bits than the largest natively supported integer type.
    260             composite_integer,
    261         };
    262 
    263         /// The number of bits in the inner type.
    264         /// This is the actual number of bits of the type, not the size of the backing integer.
    265         bits: u16,
    266 
    267         /// The number of bits required to store the type.
    268         /// For `integer` and `float`, this is equal to `bits`.
    269         /// For `strange_integer` and `bool` this is the size of the backing integer.
    270         /// For `composite_integer` this is 0 (TODO)
    271         backing_bits: u16,
    272 
    273         /// Whether the type is a vector.
    274         is_vector: bool,
    275 
    276         /// Whether the inner type is signed. Only relevant for integers.
    277         signedness: std.builtin.Signedness,
    278 
    279         /// A classification of the inner type. These scenarios
    280         /// will all have to be handled slightly different.
    281         class: Class,
    282     };
    283 
    284     /// Data can be lowered into in two basic representations: indirect, which is when
    285     /// a type is stored in memory, and direct, which is how a type is stored when its
    286     /// a direct SPIR-V value.
    287     const Repr = enum {
    288         /// A SPIR-V value as it would be used in operations.
    289         direct,
    290         /// A SPIR-V value as it is stored in memory.
    291         indirect,
    292     };
    293 
    294     /// Free resources owned by the DeclGen.
    295     pub fn deinit(self: *DeclGen) void {
    296         self.args.deinit(self.gpa);
    297         self.inst_results.deinit(self.gpa);
    298         self.blocks.deinit(self.gpa);
    299         self.func.deinit(self.gpa);
    300         self.base_line_stack.deinit(self.gpa);
    301     }
    302 
    303     /// Return the target which we are currently compiling for.
    304     pub fn getTarget(self: *DeclGen) std.Target {
    305         return self.module.getTarget();
    306     }
    307 
    308     pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
    309         @setCold(true);
    310         const mod = self.module;
    311         const src = LazySrcLoc.nodeOffset(0);
    312         const src_loc = src.toSrcLoc(self.module.declPtr(self.decl_index), mod);
    313         assert(self.error_msg == null);
    314         self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);
    315         return error.CodegenFail;
    316     }
    317 
    318     pub fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
    319         return self.fail("TODO (SPIR-V): " ++ format, args);
    320     }
    321 
    322     /// Fetch the result-id for a previously generated instruction or constant.
    323     fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef {
    324         const mod = self.module;
    325         if (try self.air.value(inst, mod)) |val| {
    326             const ty = self.typeOf(inst);
    327             if (ty.zigTypeTag(mod) == .Fn) {
    328                 const fn_decl_index = switch (mod.intern_pool.indexToKey(val.ip_index)) {
    329                     .extern_func => |extern_func| extern_func.decl,
    330                     .func => |func| func.owner_decl,
    331                     else => unreachable,
    332                 };
    333                 const spv_decl_index = try self.object.resolveDecl(mod, fn_decl_index);
    334                 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
    335                 return self.spv.declPtr(spv_decl_index).result_id;
    336             }
    337 
    338             return try self.constant(ty, val, .direct);
    339         }
    340         const index = Air.refToIndex(inst).?;
    341         return self.inst_results.get(index).?; // Assertion means instruction does not dominate usage.
    342     }
    343 
    344     fn resolveAnonDecl(self: *DeclGen, val: InternPool.Index, storage_class: StorageClass) !IdRef {
    345         // TODO: This cannot be a function at this point, but it should probably be handled anyway.
    346         const spv_decl_index = blk: {
    347             const entry = try self.object.anon_decl_link.getOrPut(self.object.gpa, .{ val, storage_class });
    348             if (entry.found_existing) {
    349                 try self.func.decl_deps.put(self.spv.gpa, entry.value_ptr.*, {});
    350                 return self.spv.declPtr(entry.value_ptr.*).result_id;
    351             }
    352 
    353             const spv_decl_index = try self.spv.allocDecl(.global);
    354             try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
    355             entry.value_ptr.* = spv_decl_index;
    356             break :blk spv_decl_index;
    357         };
    358 
    359         const mod = self.module;
    360         const ty = mod.intern_pool.typeOf(val).toType();
    361         const ty_ref = try self.resolveType(ty, .indirect);
    362         const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class);
    363 
    364         const var_id = self.spv.declPtr(spv_decl_index).result_id;
    365 
    366         const section = &self.spv.sections.types_globals_constants;
    367         try section.emit(self.spv.gpa, .OpVariable, .{
    368             .id_result_type = self.typeId(ptr_ty_ref),
    369             .id_result = var_id,
    370             .storage_class = storage_class,
    371         });
    372 
    373         // TODO: At some point we will be able to generate this all constant here, but then all of
    374         //   constant() will need to be implemented such that it doesn't generate any at-runtime code.
    375         // NOTE: Because this is a global, we really only want to initialize it once. Therefore the
    376         //   constant lowering of this value will need to be deferred to some other function, which
    377         //   is then added to the list of initializers using endGlobal().
    378 
    379         // Save the current state so that we can temporarily generate into a different function.
    380         // TODO: This should probably be made a little more robust.
    381         const func = self.func;
    382         defer self.func = func;
    383         const block_label_id = self.current_block_label_id;
    384         defer self.current_block_label_id = block_label_id;
    385 
    386         self.func = .{};
    387 
    388         // TODO: Merge this with genDecl?
    389         const begin = self.spv.beginGlobal();
    390 
    391         const void_ty_ref = try self.resolveType(Type.void, .direct);
    392         const initializer_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{
    393             .return_type = void_ty_ref,
    394             .parameters = &.{},
    395         } });
    396 
    397         const initializer_id = self.spv.allocId();
    398         try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
    399             .id_result_type = self.typeId(void_ty_ref),
    400             .id_result = initializer_id,
    401             .function_control = .{},
    402             .function_type = self.typeId(initializer_proto_ty_ref),
    403         });
    404         const root_block_id = self.spv.allocId();
    405         try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{
    406             .id_result = root_block_id,
    407         });
    408         self.current_block_label_id = root_block_id;
    409 
    410         const val_id = try self.constant(ty, val.toValue(), .indirect);
    411         try self.func.body.emit(self.spv.gpa, .OpStore, .{
    412             .pointer = var_id,
    413             .object = val_id,
    414         });
    415 
    416         self.spv.endGlobal(spv_decl_index, begin, var_id, initializer_id);
    417         try self.func.body.emit(self.spv.gpa, .OpReturn, {});
    418         try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
    419         try self.spv.addFunction(spv_decl_index, self.func);
    420 
    421         try self.spv.debugNameFmt(var_id, "__anon_{d}", .{@intFromEnum(val)});
    422         try self.spv.debugNameFmt(initializer_id, "initializer of __anon_{d}", .{@intFromEnum(val)});
    423 
    424         return var_id;
    425     }
    426 
    427     /// Start a new SPIR-V block, Emits the label of the new block, and stores which
    428     /// block we are currently generating.
    429     /// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to
    430     /// keep track of the previous block.
    431     fn beginSpvBlock(self: *DeclGen, label_id: IdResult) !void {
    432         try self.func.body.emit(self.spv.gpa, .OpLabel, .{ .id_result = label_id });
    433         self.current_block_label_id = label_id;
    434     }
    435 
    436     /// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need
    437     /// to emulate them in other instructions/types. This function returns, given an integer bit width (signed or unsigned, sign
    438     /// included), the width of the underlying type which represents it, given the enabled features for the current target.
    439     /// If the result is `null`, the largest type the target platform supports natively is not able to perform computations using
    440     /// that size. In this case, multiple elements of the largest type should be used.
    441     /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits.
    442     /// The result is valid to be used with OpTypeInt.
    443     /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).
    444     /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers).
    445     /// TODO: Should the result of this function be cached?
    446     fn backingIntBits(self: *DeclGen, bits: u16) ?u16 {
    447         const target = self.getTarget();
    448 
    449         // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.
    450         assert(bits != 0);
    451 
    452         // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
    453         // 32-bit integers are always supported (see spec, 2.16.1, Data rules).
    454         const ints = [_]struct { bits: u16, feature: ?Target.spirv.Feature }{
    455             .{ .bits = 8, .feature = .Int8 },
    456             .{ .bits = 16, .feature = .Int16 },
    457             .{ .bits = 32, .feature = null },
    458             .{ .bits = 64, .feature = .Int64 },
    459         };
    460 
    461         for (ints) |int| {
    462             const has_feature = if (int.feature) |feature|
    463                 Target.spirv.featureSetHas(target.cpu.features, feature)
    464             else
    465                 true;
    466 
    467             if (bits <= int.bits and has_feature) {
    468                 return int.bits;
    469             }
    470         }
    471 
    472         return null;
    473     }
    474 
    475     /// Return the amount of bits in the largest supported integer type. This is either 32 (always supported), or 64 (if
    476     /// the Int64 capability is enabled).
    477     /// Note: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).
    478     /// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there
    479     /// is no way of knowing whether those are actually supported.
    480     /// TODO: Maybe this should be cached?
    481     fn largestSupportedIntBits(self: *DeclGen) u16 {
    482         const target = self.getTarget();
    483         return if (Target.spirv.featureSetHas(target.cpu.features, .Int64))
    484             64
    485         else
    486             32;
    487     }
    488 
    489     /// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by
    490     /// arrays of largestSupportedIntBits().
    491     /// Asserts `ty` is an integer.
    492     fn isCompositeInt(self: *DeclGen, ty: Type) bool {
    493         return self.backingIntBits(ty) == null;
    494     }
    495 
    496     fn arithmeticTypeInfo(self: *DeclGen, ty: Type) !ArithmeticTypeInfo {
    497         const mod = self.module;
    498         const target = self.getTarget();
    499         return switch (ty.zigTypeTag(mod)) {
    500             .Bool => ArithmeticTypeInfo{
    501                 .bits = 1, // Doesn't matter for this class.
    502                 .backing_bits = self.backingIntBits(1).?,
    503                 .is_vector = false,
    504                 .signedness = .unsigned, // Technically, but doesn't matter for this class.
    505                 .class = .bool,
    506             },
    507             .Float => ArithmeticTypeInfo{
    508                 .bits = ty.floatBits(target),
    509                 .backing_bits = ty.floatBits(target), // TODO: F80?
    510                 .is_vector = false,
    511                 .signedness = .signed, // Technically, but doesn't matter for this class.
    512                 .class = .float,
    513             },
    514             .Int => blk: {
    515                 const int_info = ty.intInfo(mod);
    516                 // TODO: Maybe it's useful to also return this value.
    517                 const maybe_backing_bits = self.backingIntBits(int_info.bits);
    518                 break :blk ArithmeticTypeInfo{
    519                     .bits = int_info.bits,
    520                     .backing_bits = maybe_backing_bits orelse 0,
    521                     .is_vector = false,
    522                     .signedness = int_info.signedness,
    523                     .class = if (maybe_backing_bits) |backing_bits|
    524                         if (backing_bits == int_info.bits)
    525                             ArithmeticTypeInfo.Class.integer
    526                         else
    527                             ArithmeticTypeInfo.Class.strange_integer
    528                     else
    529                         .composite_integer,
    530                 };
    531             },
    532             .Enum => return self.arithmeticTypeInfo(ty.intTagType(mod)),
    533             // As of yet, there is no vector support in the self-hosted compiler.
    534             .Vector => blk: {
    535                 const child_type = ty.childType(mod);
    536                 const child_ty_info = try self.arithmeticTypeInfo(child_type);
    537                 break :blk ArithmeticTypeInfo{
    538                     .bits = child_ty_info.bits,
    539                     .backing_bits = child_ty_info.backing_bits,
    540                     .is_vector = true,
    541                     .signedness = child_ty_info.signedness,
    542                     .class = child_ty_info.class,
    543                 };
    544             },
    545             // TODO: For which types is this the case?
    546             // else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmt(self.module)}),
    547             else => unreachable,
    548         };
    549     }
    550 
    551     /// Emits a bool constant in a particular representation.
    552     fn constBool(self: *DeclGen, value: bool, repr: Repr) !IdRef {
    553         switch (repr) {
    554             .indirect => {
    555                 const int_ty_ref = try self.intType(.unsigned, 1);
    556                 return self.constInt(int_ty_ref, @intFromBool(value));
    557             },
    558             .direct => {
    559                 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
    560                 return self.spv.constBool(bool_ty_ref, value);
    561             },
    562         }
    563     }
    564 
    565     /// Emits an integer constant.
    566     /// This function, unlike SpvModule.constInt, takes care to bitcast
    567     /// the value to an unsigned int first for Kernels.
    568     fn constInt(self: *DeclGen, ty_ref: CacheRef, value: anytype) !IdRef {
    569         if (value < 0) {
    570             const ty = self.spv.cache.lookup(ty_ref).int_type;
    571             // Manually truncate the value so that the resulting value
    572             // fits within the unsigned type.
    573             const bits: u64 = @bitCast(@as(i64, @intCast(value)));
    574             const truncated_bits = if (ty.bits == 64)
    575                 bits
    576             else
    577                 bits & (@as(u64, 1) << @intCast(ty.bits)) - 1;
    578             return try self.spv.constInt(ty_ref, truncated_bits);
    579         } else {
    580             return try self.spv.constInt(ty_ref, value);
    581         }
    582     }
    583 
    584     /// Construct a struct at runtime.
    585     /// result_ty_ref must be a struct type.
    586     /// Constituents should be in `indirect` representation (as the elements of a struct should be).
    587     /// Result is in `direct` representation.
    588     fn constructStruct(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef {
    589         // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which'
    590         // operands are not constant.
    591         // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349
    592         // For now, just initialize the struct by setting the fields manually...
    593         // TODO: Make this OpCompositeConstruct when we can
    594         const ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function);
    595         const ptr_composite_id = self.spv.allocId();
    596         try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
    597             .id_result_type = self.typeId(ptr_ty_ref),
    598             .id_result = ptr_composite_id,
    599             .storage_class = .Function,
    600         });
    601 
    602         const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type;
    603         const member_types = spv_composite_ty.member_types;
    604 
    605         for (constituents, member_types, 0..) |constitent_id, member_ty_ref, index| {
    606             const ptr_member_ty_ref = try self.spv.ptrType(member_ty_ref, .Function);
    607             const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))});
    608             try self.func.body.emit(self.spv.gpa, .OpStore, .{
    609                 .pointer = ptr_id,
    610                 .object = constitent_id,
    611             });
    612         }
    613         const result_id = self.spv.allocId();
    614         try self.func.body.emit(self.spv.gpa, .OpLoad, .{
    615             .id_result_type = self.typeId(result_ty_ref),
    616             .id_result = result_id,
    617             .pointer = ptr_composite_id,
    618         });
    619         return result_id;
    620     }
    621 
    622     /// Construct an array at runtime.
    623     /// result_ty_ref must be an array type.
    624     /// Constituents should be in `indirect` representation (as the elements of an array should be).
    625     /// Result is in `direct` representation.
    626     fn constructArray(self: *DeclGen, result_ty_ref: CacheRef, constituents: []const IdRef) !IdRef {
    627         // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which'
    628         // operands are not constant.
    629         // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349
    630         // For now, just initialize the struct by setting the fields manually...
    631         // TODO: Make this OpCompositeConstruct when we can
    632         // TODO: Make this Function storage type
    633         const ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function);
    634         const ptr_composite_id = self.spv.allocId();
    635         try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
    636             .id_result_type = self.typeId(ptr_ty_ref),
    637             .id_result = ptr_composite_id,
    638             .storage_class = .Function,
    639         });
    640 
    641         const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).array_type;
    642         const elem_ty_ref = spv_composite_ty.element_type;
    643         const ptr_elem_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function);
    644 
    645         for (constituents, 0..) |constitent_id, index| {
    646             const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))});
    647             try self.func.body.emit(self.spv.gpa, .OpStore, .{
    648                 .pointer = ptr_id,
    649                 .object = constitent_id,
    650             });
    651         }
    652         const result_id = self.spv.allocId();
    653         try self.func.body.emit(self.spv.gpa, .OpLoad, .{
    654             .id_result_type = self.typeId(result_ty_ref),
    655             .id_result = result_id,
    656             .pointer = ptr_composite_id,
    657         });
    658         return result_id;
    659     }
    660 
    661     /// This function generates a load for a constant in direct (ie, non-memory) representation.
    662     /// When the constant is simple, it can be generated directly using OpConstant instructions.
    663     /// When the constant is more complicated however, it needs to be constructed using multiple values. This
    664     /// is done by emitting a sequence of instructions that initialize the value.
    665     //
    666     /// This function should only be called during function code generation.
    667     fn constant(self: *DeclGen, ty: Type, arg_val: Value, repr: Repr) !IdRef {
    668         const mod = self.module;
    669         const target = self.getTarget();
    670         const result_ty_ref = try self.resolveType(ty, repr);
    671         const ip = &mod.intern_pool;
    672 
    673         var val = arg_val;
    674         switch (ip.indexToKey(val.toIntern())) {
    675             .runtime_value => |rt| val = rt.val.toValue(),
    676             else => {},
    677         }
    678 
    679         log.debug("constant: ty = {}, val = {}", .{ ty.fmt(mod), val.fmtValue(ty, mod) });
    680         if (val.isUndefDeep(mod)) {
    681             return self.spv.constUndef(result_ty_ref);
    682         }
    683 
    684         switch (ip.indexToKey(val.toIntern())) {
    685             .int_type,
    686             .ptr_type,
    687             .array_type,
    688             .vector_type,
    689             .opt_type,
    690             .anyframe_type,
    691             .error_union_type,
    692             .simple_type,
    693             .struct_type,
    694             .anon_struct_type,
    695             .union_type,
    696             .opaque_type,
    697             .enum_type,
    698             .func_type,
    699             .error_set_type,
    700             .inferred_error_set_type,
    701             => unreachable, // types, not values
    702 
    703             .undef, .runtime_value => unreachable, // handled above
    704 
    705             .variable,
    706             .extern_func,
    707             .func,
    708             .enum_literal,
    709             .empty_enum_value,
    710             => unreachable, // non-runtime values
    711 
    712             .simple_value => |simple_value| switch (simple_value) {
    713                 .undefined,
    714                 .void,
    715                 .null,
    716                 .empty_struct,
    717                 .@"unreachable",
    718                 .generic_poison,
    719                 => unreachable, // non-runtime values
    720 
    721                 .false, .true => return try self.constBool(val.toBool(), repr),
    722             },
    723 
    724             .int => {
    725                 if (ty.isSignedInt(mod)) {
    726                     return try self.constInt(result_ty_ref, val.toSignedInt(mod));
    727                 } else {
    728                     return try self.constInt(result_ty_ref, val.toUnsignedInt(mod));
    729                 }
    730             },
    731             .float => return switch (ty.floatBits(target)) {
    732                 16 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float16 = val.toFloat(f16, mod) } } }),
    733                 32 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float32 = val.toFloat(f32, mod) } } }),
    734                 64 => try self.spv.resolveId(.{ .float = .{ .ty = result_ty_ref, .value = .{ .float64 = val.toFloat(f64, mod) } } }),
    735                 80, 128 => unreachable, // TODO
    736                 else => unreachable,
    737             },
    738             .err => |err| {
    739                 const value = try mod.getErrorValue(err.name);
    740                 return try self.constInt(result_ty_ref, value);
    741             },
    742             .error_union => |error_union| {
    743                 // TODO: Error unions may be constructed with constant instructions if the payload type
    744                 // allows it. For now, just generate it here regardless.
    745                 const err_ty = switch (error_union.val) {
    746                     .err_name => ty.errorUnionSet(mod),
    747                     .payload => Type.err_int,
    748                 };
    749                 const err_val = switch (error_union.val) {
    750                     .err_name => |err_name| (try mod.intern(.{ .err = .{
    751                         .ty = ty.errorUnionSet(mod).toIntern(),
    752                         .name = err_name,
    753                     } })).toValue(),
    754                     .payload => try mod.intValue(Type.err_int, 0),
    755                 };
    756                 const payload_ty = ty.errorUnionPayload(mod);
    757                 const eu_layout = self.errorUnionLayout(payload_ty);
    758                 if (!eu_layout.payload_has_bits) {
    759                     // We use the error type directly as the type.
    760                     return try self.constant(err_ty, err_val, .indirect);
    761                 }
    762 
    763                 const payload_val = switch (error_union.val) {
    764                     .err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }),
    765                     .payload => |payload| payload,
    766                 }.toValue();
    767 
    768                 var constituents: [2]IdRef = undefined;
    769                 if (eu_layout.error_first) {
    770                     constituents[0] = try self.constant(err_ty, err_val, .indirect);
    771                     constituents[1] = try self.constant(payload_ty, payload_val, .indirect);
    772                 } else {
    773                     constituents[0] = try self.constant(payload_ty, payload_val, .indirect);
    774                     constituents[1] = try self.constant(err_ty, err_val, .indirect);
    775                 }
    776 
    777                 return try self.constructStruct(result_ty_ref, &constituents);
    778             },
    779             .enum_tag => {
    780                 const int_val = try val.intFromEnum(ty, mod);
    781                 const int_ty = ty.intTagType(mod);
    782                 return try self.constant(int_ty, int_val, repr);
    783             },
    784             .ptr => |ptr| {
    785                 const ptr_ty = switch (ptr.len) {
    786                     .none => ty,
    787                     else => ty.slicePtrFieldType(mod),
    788                 };
    789                 const ptr_id = try self.constantPtr(ptr_ty, val);
    790                 if (ptr.len == .none) {
    791                     return ptr_id;
    792                 }
    793 
    794                 const len_id = try self.constant(Type.usize, ptr.len.toValue(), .indirect);
    795                 return try self.constructStruct(result_ty_ref, &.{ ptr_id, len_id });
    796             },
    797             .opt => {
    798                 const payload_ty = ty.optionalChild(mod);
    799                 const maybe_payload_val = val.optionalValue(mod);
    800 
    801                 if (!payload_ty.hasRuntimeBits(mod)) {
    802                     return try self.constBool(maybe_payload_val != null, .indirect);
    803                 } else if (ty.optionalReprIsPayload(mod)) {
    804                     // Optional representation is a nullable pointer or slice.
    805                     if (maybe_payload_val) |payload_val| {
    806                         return try self.constant(payload_ty, payload_val, .indirect);
    807                     } else {
    808                         const ptr_ty_ref = try self.resolveType(ty, .indirect);
    809                         return self.spv.constNull(ptr_ty_ref);
    810                     }
    811                 }
    812 
    813                 // Optional representation is a structure.
    814                 // { Payload, Bool }
    815 
    816                 const has_pl_id = try self.constBool(maybe_payload_val != null, .indirect);
    817                 const payload_id = if (maybe_payload_val) |payload_val|
    818                     try self.constant(payload_ty, payload_val, .indirect)
    819                 else
    820                     try self.spv.constUndef(try self.resolveType(payload_ty, .indirect));
    821 
    822                 return try self.constructStruct(result_ty_ref, &.{ payload_id, has_pl_id });
    823             },
    824             .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
    825                 inline .array_type, .vector_type => |array_type, tag| {
    826                     const elem_ty = array_type.child.toType();
    827                     const elem_ty_ref = try self.resolveType(elem_ty, .indirect);
    828 
    829                     const constituents = try self.gpa.alloc(IdRef, @as(u32, @intCast(ty.arrayLenIncludingSentinel(mod))));
    830                     defer self.gpa.free(constituents);
    831 
    832                     switch (aggregate.storage) {
    833                         .bytes => |bytes| {
    834                             // TODO: This is really space inefficient, perhaps there is a better
    835                             // way to do it?
    836                             for (bytes, 0..) |byte, i| {
    837                                 constituents[i] = try self.constInt(elem_ty_ref, byte);
    838                             }
    839                         },
    840                         .elems => |elems| {
    841                             for (0..@as(usize, @intCast(array_type.len))) |i| {
    842                                 constituents[i] = try self.constant(elem_ty, elems[i].toValue(), .indirect);
    843                             }
    844                         },
    845                         .repeated_elem => |elem| {
    846                             const val_id = try self.constant(elem_ty, elem.toValue(), .indirect);
    847                             for (0..@as(usize, @intCast(array_type.len))) |i| {
    848                                 constituents[i] = val_id;
    849                             }
    850                         },
    851                     }
    852 
    853                     switch (tag) {
    854                         inline .array_type => if (array_type.sentinel != .none) {
    855                             constituents[constituents.len - 1] = try self.constant(elem_ty, array_type.sentinel.toValue(), .indirect);
    856                         },
    857                         else => {},
    858                     }
    859 
    860                     return try self.constructArray(result_ty_ref, constituents);
    861                 },
    862                 .struct_type => {
    863                     const struct_type = mod.typeToStruct(ty).?;
    864                     if (struct_type.layout == .Packed) {
    865                         return self.todo("packed struct constants", .{});
    866                     }
    867 
    868                     var constituents = std.ArrayList(IdRef).init(self.gpa);
    869                     defer constituents.deinit();
    870 
    871                     var it = struct_type.iterateRuntimeOrder(ip);
    872                     while (it.next()) |field_index| {
    873                         const field_ty = struct_type.field_types.get(ip)[field_index].toType();
    874                         if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
    875                             // This is a zero-bit field - we only needed it for the alignment.
    876                             continue;
    877                         }
    878 
    879                         // TODO: Padding?
    880                         const field_val = try val.fieldValue(mod, field_index);
    881                         const field_id = try self.constant(field_ty, field_val, .indirect);
    882 
    883                         try constituents.append(field_id);
    884                     }
    885 
    886                     return try self.constructStruct(result_ty_ref, constituents.items);
    887                 },
    888                 .anon_struct_type => unreachable, // TODO
    889                 else => unreachable,
    890             },
    891             .un => |un| {
    892                 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
    893                 const layout = self.unionLayout(ty, active_field);
    894                 const payload = if (layout.active_field_size != 0)
    895                     try self.constant(layout.active_field_ty, un.val.toValue(), .indirect)
    896                 else
    897                     null;
    898 
    899                 return try self.unionInit(ty, active_field, payload);
    900             },
    901             .memoized_call => unreachable,
    902         }
    903     }
    904 
    905     fn constantPtr(self: *DeclGen, ptr_ty: Type, ptr_val: Value) Error!IdRef {
    906         const result_ty_ref = try self.resolveType(ptr_ty, .direct);
    907         const mod = self.module;
    908         switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) {
    909             .decl => |decl| return try self.constantDeclRef(ptr_ty, decl),
    910             .mut_decl => |decl_mut| return try self.constantDeclRef(ptr_ty, decl_mut.decl),
    911             .anon_decl => |anon_decl| return try self.constantAnonDeclRef(ptr_ty, anon_decl),
    912             .int => |int| {
    913                 const ptr_id = self.spv.allocId();
    914                 // TODO: This can probably be an OpSpecConstantOp Bitcast, but
    915                 // that is not implemented by Mesa yet. Therefore, just generate it
    916                 // as a runtime operation.
    917                 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{
    918                     .id_result_type = self.typeId(result_ty_ref),
    919                     .id_result = ptr_id,
    920                     .integer_value = try self.constant(Type.usize, int.toValue(), .direct),
    921                 });
    922                 return ptr_id;
    923             },
    924             .eu_payload => unreachable, // TODO
    925             .opt_payload => unreachable, // TODO
    926             .comptime_field => unreachable,
    927             .elem => |elem_ptr| {
    928                 const parent_ptr_ty = mod.intern_pool.typeOf(elem_ptr.base).toType();
    929                 const parent_ptr_id = try self.constantPtr(parent_ptr_ty, elem_ptr.base.toValue());
    930                 const size_ty_ref = try self.sizeType();
    931                 const index_id = try self.constInt(size_ty_ref, elem_ptr.index);
    932 
    933                 const elem_ptr_id = try self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id);
    934 
    935                 // TODO: Can we consolidate this in ptrElemPtr?
    936                 const elem_ty = parent_ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T.
    937                 const elem_ty_ref = try self.resolveType(elem_ty, .direct);
    938                 const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(parent_ptr_ty.ptrAddressSpace(mod)));
    939 
    940                 if (elem_ptr_ty_ref == result_ty_ref) {
    941                     return elem_ptr_id;
    942                 }
    943                 // This may happen when we have pointer-to-array and the result is
    944                 // another pointer-to-array instead of a pointer-to-element.
    945                 const result_id = self.spv.allocId();
    946                 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
    947                     .id_result_type = self.typeId(result_ty_ref),
    948                     .id_result = result_id,
    949                     .operand = elem_ptr_id,
    950                 });
    951                 return result_id;
    952             },
    953             .field => |field| {
    954                 const base_ptr_ty = mod.intern_pool.typeOf(field.base).toType();
    955                 const base_ptr = try self.constantPtr(base_ptr_ty, field.base.toValue());
    956                 const field_index: u32 = @intCast(field.index);
    957                 return try self.structFieldPtr(ptr_ty, base_ptr_ty, base_ptr, field_index);
    958             },
    959         }
    960     }
    961 
    962     fn constantAnonDeclRef(self: *DeclGen, ty: Type, decl_val: InternPool.Index) !IdRef {
    963         // TODO: Merge this function with constantDeclRef.
    964 
    965         const mod = self.module;
    966         const ip = &mod.intern_pool;
    967         const ty_ref = try self.resolveType(ty, .direct);
    968         const decl_ty = ip.typeOf(decl_val).toType();
    969 
    970         if (decl_val.toValue().getFunction(mod)) |func| {
    971             _ = func;
    972             unreachable; // TODO
    973         } else if (decl_val.toValue().getExternFunc(mod)) |func| {
    974             _ = func;
    975             unreachable;
    976         }
    977 
    978         // const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn;
    979         if (!decl_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) {
    980             // Pointer to nothing - return undefoined
    981             return self.spv.constUndef(ty_ref);
    982         }
    983 
    984         if (decl_ty.zigTypeTag(mod) == .Fn) {
    985             unreachable; // TODO
    986         }
    987 
    988         const final_storage_class = spvStorageClass(ty.ptrAddressSpace(mod));
    989         const actual_storage_class = switch (final_storage_class) {
    990             .Generic => .CrossWorkgroup,
    991             else => |other| other,
    992         };
    993 
    994         const decl_id = try self.resolveAnonDecl(decl_val, actual_storage_class);
    995         const decl_ty_ref = try self.resolveType(decl_ty, .indirect);
    996         const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class);
    997 
    998         const ptr_id = switch (final_storage_class) {
    999             .Generic => blk: {
   1000                 const result_id = self.spv.allocId();
   1001                 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
   1002                     .id_result_type = self.typeId(decl_ptr_ty_ref),
   1003                     .id_result = result_id,
   1004                     .pointer = decl_id,
   1005                 });
   1006                 break :blk result_id;
   1007             },
   1008             else => decl_id,
   1009         };
   1010 
   1011         if (decl_ptr_ty_ref != ty_ref) {
   1012             // Differing pointer types, insert a cast.
   1013             const casted_ptr_id = self.spv.allocId();
   1014             try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
   1015                 .id_result_type = self.typeId(ty_ref),
   1016                 .id_result = casted_ptr_id,
   1017                 .operand = ptr_id,
   1018             });
   1019             return casted_ptr_id;
   1020         } else {
   1021             return ptr_id;
   1022         }
   1023     }
   1024 
   1025     fn constantDeclRef(self: *DeclGen, ty: Type, decl_index: Decl.Index) !IdRef {
   1026         const mod = self.module;
   1027         const ty_ref = try self.resolveType(ty, .direct);
   1028         const ty_id = self.typeId(ty_ref);
   1029         const decl = mod.declPtr(decl_index);
   1030         switch (mod.intern_pool.indexToKey(decl.val.ip_index)) {
   1031             .func => {
   1032                 // TODO: Properly lower function pointers. For now we are going to hack around it and
   1033                 // just generate an empty pointer. Function pointers are represented by a pointer to usize.
   1034                 return try self.spv.constUndef(ty_ref);
   1035             },
   1036             .extern_func => unreachable, // TODO
   1037             else => {},
   1038         }
   1039 
   1040         if (!decl.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) {
   1041             // Pointer to nothing - return undefined.
   1042             return self.spv.constUndef(ty_ref);
   1043         }
   1044 
   1045         const spv_decl_index = try self.object.resolveDecl(mod, decl_index);
   1046 
   1047         const decl_id = self.spv.declPtr(spv_decl_index).result_id;
   1048         try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
   1049 
   1050         const final_storage_class = spvStorageClass(decl.@"addrspace");
   1051 
   1052         const decl_ty_ref = try self.resolveType(decl.ty, .indirect);
   1053         const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class);
   1054 
   1055         const ptr_id = switch (final_storage_class) {
   1056             .Generic => blk: {
   1057                 // Pointer should be Generic, but is actually placed in CrossWorkgroup.
   1058                 const result_id = self.spv.allocId();
   1059                 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
   1060                     .id_result_type = self.typeId(decl_ptr_ty_ref),
   1061                     .id_result = result_id,
   1062                     .pointer = decl_id,
   1063                 });
   1064                 break :blk result_id;
   1065             },
   1066             else => decl_id,
   1067         };
   1068 
   1069         if (decl_ptr_ty_ref != ty_ref) {
   1070             // Differing pointer types, insert a cast.
   1071             const casted_ptr_id = self.spv.allocId();
   1072             try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
   1073                 .id_result_type = ty_id,
   1074                 .id_result = casted_ptr_id,
   1075                 .operand = ptr_id,
   1076             });
   1077             return casted_ptr_id;
   1078         } else {
   1079             return ptr_id;
   1080         }
   1081     }
   1082 
   1083     // Turn a Zig type's name into a cache reference.
   1084     fn resolveTypeName(self: *DeclGen, ty: Type) !CacheString {
   1085         var name = std.ArrayList(u8).init(self.gpa);
   1086         defer name.deinit();
   1087         try ty.print(name.writer(), self.module);
   1088         return try self.spv.resolveString(name.items);
   1089     }
   1090 
   1091     /// Turn a Zig type into a SPIR-V Type, and return its type result-id.
   1092     fn resolveTypeId(self: *DeclGen, ty: Type) !IdResultType {
   1093         const type_ref = try self.resolveType(ty, .direct);
   1094         return self.spv.resultId(type_ref);
   1095     }
   1096 
   1097     fn typeId(self: *DeclGen, ty_ref: CacheRef) IdRef {
   1098         return self.spv.resultId(ty_ref);
   1099     }
   1100 
   1101     /// Create an integer type suitable for storing at least 'bits' bits.
   1102     /// The integer type that is returned by this function is the type that is used to perform
   1103     /// actual operations (as well as store) a Zig type of a particular number of bits. To create
   1104     /// a type with an exact size, use SpvModule.intType.
   1105     fn intType(self: *DeclGen, signedness: std.builtin.Signedness, bits: u16) !CacheRef {
   1106         const backing_bits = self.backingIntBits(bits) orelse {
   1107             // TODO: Integers too big for any native type are represented as "composite integers":
   1108             // An array of largestSupportedIntBits.
   1109             return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits });
   1110         };
   1111         // Kernel only supports unsigned ints.
   1112         // TODO: Only do this with Kernels
   1113         return self.spv.intType(.unsigned, backing_bits);
   1114     }
   1115 
   1116     /// Create an integer type that represents 'usize'.
   1117     fn sizeType(self: *DeclGen) !CacheRef {
   1118         return try self.intType(.unsigned, self.getTarget().ptrBitWidth());
   1119     }
   1120 
   1121     /// Generate a union type, optionally with a known field. If the tag alignment is greater
   1122     /// than that of the payload, a regular union (non-packed, with both tag and payload), will
   1123     /// be generated as follows:
   1124     /// If the active field is known:
   1125     ///  struct {
   1126     ///    tag: TagType,
   1127     ///    payload: ActivePayloadType,
   1128     ///    payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8,
   1129     ///    padding: [padding_size]u8,
   1130     ///  }
   1131     /// If the payload alignment is greater than that of the tag:
   1132     ///  struct {
   1133     ///    payload: ActivePayloadType,
   1134     ///    payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8,
   1135     ///    tag: TagType,
   1136     ///    padding: [padding_size]u8,
   1137     ///  }
   1138     /// If the active payload is unknown, it will default back to the most aligned field. This is
   1139     /// to make sure that the overal struct has the correct alignment in spir-v.
   1140     /// If any of the fields' size is 0, it will be omitted.
   1141     /// NOTE: When the active field is set to something other than the most aligned field, the
   1142     ///   resulting struct will be *underaligned*.
   1143     fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef {
   1144         const mod = self.module;
   1145         const ip = &mod.intern_pool;
   1146         const union_obj = mod.typeToUnion(ty).?;
   1147 
   1148         if (union_obj.getLayout(ip) == .Packed) {
   1149             return self.todo("packed union types", .{});
   1150         }
   1151 
   1152         const layout = self.unionLayout(ty, maybe_active_field);
   1153 
   1154         if (layout.payload_size == 0) {
   1155             // No payload, so represent this as just the tag type.
   1156             return try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect);
   1157         }
   1158 
   1159         // TODO: We need to add the active field to the key, somehow.
   1160         if (maybe_active_field == null) {
   1161             if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
   1162         }
   1163 
   1164         var member_types: [4]CacheRef = undefined;
   1165         var member_names: [4]CacheString = undefined;
   1166 
   1167         const u8_ty_ref = try self.intType(.unsigned, 8); // TODO: What if Int8Type is not enabled?
   1168 
   1169         if (layout.tag_size != 0) {
   1170             const tag_ty_ref = try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect);
   1171             member_types[layout.tag_index] = tag_ty_ref;
   1172             member_names[layout.tag_index] = try self.spv.resolveString("(tag)");
   1173         }
   1174 
   1175         if (layout.active_field_size != 0) {
   1176             const active_payload_ty_ref = try self.resolveType(layout.active_field_ty, .indirect);
   1177             member_types[layout.active_field_index] = active_payload_ty_ref;
   1178             member_names[layout.active_field_index] = try self.spv.resolveString("(payload)");
   1179         }
   1180 
   1181         if (layout.payload_padding_size != 0) {
   1182             const payload_padding_ty_ref = try self.spv.arrayType(@intCast(layout.payload_padding_size), u8_ty_ref);
   1183             member_types[layout.payload_padding_index] = payload_padding_ty_ref;
   1184             member_names[layout.payload_padding_index] = try self.spv.resolveString("(payload padding)");
   1185         }
   1186 
   1187         if (layout.padding_size != 0) {
   1188             const padding_ty_ref = try self.spv.arrayType(@intCast(layout.padding_size), u8_ty_ref);
   1189             member_types[layout.padding_index] = padding_ty_ref;
   1190             member_names[layout.padding_index] = try self.spv.resolveString("(padding)");
   1191         }
   1192 
   1193         const ty_ref = try self.spv.resolve(.{ .struct_type = .{
   1194             .name = try self.resolveTypeName(ty),
   1195             .member_types = member_types[0..layout.total_fields],
   1196             .member_names = member_names[0..layout.total_fields],
   1197         } });
   1198 
   1199         if (maybe_active_field == null) {
   1200             try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
   1201         }
   1202         return ty_ref;
   1203     }
   1204 
   1205     fn resolveFnReturnType(self: *DeclGen, ret_ty: Type) !CacheRef {
   1206         const mod = self.module;
   1207         if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {
   1208             // If the return type is an error set or an error union, then we make this
   1209             // anyerror return type instead, so that it can be coerced into a function
   1210             // pointer type which has anyerror as the return type.
   1211             if (ret_ty.isError(mod)) {
   1212                 return self.resolveType(Type.anyerror, .direct);
   1213             } else {
   1214                 return self.resolveType(Type.void, .direct);
   1215             }
   1216         }
   1217 
   1218         return try self.resolveType(ret_ty, .direct);
   1219     }
   1220 
   1221     /// Turn a Zig type into a SPIR-V Type, and return a reference to it.
   1222     fn resolveType(self: *DeclGen, ty: Type, repr: Repr) Error!CacheRef {
   1223         const mod = self.module;
   1224         const ip = &mod.intern_pool;
   1225         log.debug("resolveType: ty = {}", .{ty.fmt(mod)});
   1226         const target = self.getTarget();
   1227         switch (ty.zigTypeTag(mod)) {
   1228             .NoReturn => {
   1229                 assert(repr == .direct);
   1230                 return try self.spv.resolve(.void_type);
   1231             },
   1232             .Void => switch (repr) {
   1233                 .direct => return try self.spv.resolve(.void_type),
   1234                 // Pointers to void
   1235                 .indirect => return try self.spv.resolve(.{ .opaque_type = .{
   1236                     .name = try self.spv.resolveString("void"),
   1237                 } }),
   1238             },
   1239             .Bool => switch (repr) {
   1240                 .direct => return try self.spv.resolve(.bool_type),
   1241                 .indirect => return try self.intType(.unsigned, 1),
   1242             },
   1243             .Int => {
   1244                 const int_info = ty.intInfo(mod);
   1245                 if (int_info.bits == 0) {
   1246                     // Some times, the backend will be asked to generate a pointer to i0. OpTypeInt
   1247                     // with 0 bits is invalid, so return an opaque type in this case.
   1248                     assert(repr == .indirect);
   1249                     return try self.spv.resolve(.{ .opaque_type = .{
   1250                         .name = try self.spv.resolveString("u0"),
   1251                     } });
   1252                 }
   1253                 return try self.intType(int_info.signedness, int_info.bits);
   1254             },
   1255             .Enum => {
   1256                 const tag_ty = ty.intTagType(mod);
   1257                 return self.resolveType(tag_ty, repr);
   1258             },
   1259             .Float => {
   1260                 // We can (and want) not really emulate floating points with other floating point types like with the integer types,
   1261                 // so if the float is not supported, just return an error.
   1262                 const bits = ty.floatBits(target);
   1263                 const supported = switch (bits) {
   1264                     16 => Target.spirv.featureSetHas(target.cpu.features, .Float16),
   1265                     // 32-bit floats are always supported (see spec, 2.16.1, Data rules).
   1266                     32 => true,
   1267                     64 => Target.spirv.featureSetHas(target.cpu.features, .Float64),
   1268                     else => false,
   1269                 };
   1270 
   1271                 if (!supported) {
   1272                     return self.fail("Floating point width of {} bits is not supported for the current SPIR-V feature set", .{bits});
   1273                 }
   1274 
   1275                 return try self.spv.resolve(.{ .float_type = .{ .bits = bits } });
   1276             },
   1277             .Array => {
   1278                 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
   1279 
   1280                 const elem_ty = ty.childType(mod);
   1281                 const elem_ty_ref = try self.resolveType(elem_ty, .indirect);
   1282                 var total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(mod)) orelse {
   1283                     return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(mod)});
   1284                 };
   1285                 const ty_ref = if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: {
   1286                     // The size of the array would be 0, but that is not allowed in SPIR-V.
   1287                     // This path can be reached when the backend is asked to generate a pointer to
   1288                     // an array of some zero-bit type. This should always be an indirect path.
   1289                     assert(repr == .indirect);
   1290 
   1291                     // We cannot use the child type here, so just use an opaque type.
   1292                     break :blk try self.spv.resolve(.{ .opaque_type = .{
   1293                         .name = try self.spv.resolveString("zero-sized array"),
   1294                     } });
   1295                 } else if (total_len == 0) blk: {
   1296                     // The size of the array would be 0, but that is not allowed in SPIR-V.
   1297                     // This path can be reached for example when there is a slicing of a pointer
   1298                     // that produces a zero-length array. In all cases where this type can be generated,
   1299                     // this should be an indirect path.
   1300                     assert(repr == .indirect);
   1301 
   1302                     // In this case, we have an array of a non-zero sized type. In this case,
   1303                     // generate an array of 1 element instead, so that ptr_elem_ptr instructions
   1304                     // can be lowered to ptrAccessChain instead of manually performing the math.
   1305                     break :blk try self.spv.arrayType(1, elem_ty_ref);
   1306                 } else try self.spv.arrayType(total_len, elem_ty_ref);
   1307 
   1308                 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
   1309                 return ty_ref;
   1310             },
   1311             .Fn => switch (repr) {
   1312                 .direct => {
   1313                     if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
   1314 
   1315                     const fn_info = mod.typeToFunc(ty).?;
   1316                     // TODO: Put this somewhere in Sema.zig
   1317                     if (fn_info.is_var_args)
   1318                         return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
   1319 
   1320                     const param_ty_refs = try self.gpa.alloc(CacheRef, fn_info.param_types.len);
   1321                     defer self.gpa.free(param_ty_refs);
   1322                     var param_index: usize = 0;
   1323                     for (fn_info.param_types.get(ip)) |param_ty_index| {
   1324                         const param_ty = param_ty_index.toType();
   1325                         if (!param_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
   1326 
   1327                         param_ty_refs[param_index] = try self.resolveType(param_ty, .direct);
   1328                         param_index += 1;
   1329                     }
   1330                     const return_ty_ref = try self.resolveFnReturnType(fn_info.return_type.toType());
   1331 
   1332                     const ty_ref = try self.spv.resolve(.{ .function_type = .{
   1333                         .return_type = return_ty_ref,
   1334                         .parameters = param_ty_refs[0..param_index],
   1335                     } });
   1336 
   1337                     try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
   1338                     return ty_ref;
   1339                 },
   1340                 .indirect => {
   1341                     // TODO: Represent function pointers properly.
   1342                     // For now, just use an usize type.
   1343                     return try self.sizeType();
   1344                 },
   1345             },
   1346             .Pointer => {
   1347                 const ptr_info = ty.ptrInfo(mod);
   1348 
   1349                 const storage_class = spvStorageClass(ptr_info.flags.address_space);
   1350                 const child_ty_ref = try self.resolveType(ptr_info.child.toType(), .indirect);
   1351                 const ptr_ty_ref = try self.spv.resolve(.{ .ptr_type = .{
   1352                     .storage_class = storage_class,
   1353                     .child_type = child_ty_ref,
   1354                 } });
   1355                 if (ptr_info.flags.size != .Slice) {
   1356                     return ptr_ty_ref;
   1357                 }
   1358 
   1359                 const size_ty_ref = try self.sizeType();
   1360                 return self.spv.resolve(.{ .struct_type = .{
   1361                     .member_types = &.{ ptr_ty_ref, size_ty_ref },
   1362                     .member_names = &.{
   1363                         try self.spv.resolveString("ptr"),
   1364                         try self.spv.resolveString("len"),
   1365                     },
   1366                 } });
   1367             },
   1368             .Vector => {
   1369                 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
   1370 
   1371                 const elem_ty = ty.childType(mod);
   1372                 const elem_ty_ref = try self.resolveType(elem_ty, .indirect);
   1373 
   1374                 const ty_ref = try self.spv.arrayType(ty.vectorLen(mod), elem_ty_ref);
   1375                 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
   1376                 return ty_ref;
   1377             },
   1378             .Struct => {
   1379                 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
   1380 
   1381                 const struct_type = switch (ip.indexToKey(ty.toIntern())) {
   1382                     .anon_struct_type => |tuple| {
   1383                         const member_types = try self.gpa.alloc(CacheRef, tuple.values.len);
   1384                         defer self.gpa.free(member_types);
   1385 
   1386                         var member_index: usize = 0;
   1387                         for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty, field_val| {
   1388                             if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue;
   1389 
   1390                             member_types[member_index] = try self.resolveType(field_ty.toType(), .indirect);
   1391                             member_index += 1;
   1392                         }
   1393 
   1394                         const ty_ref = try self.spv.resolve(.{ .struct_type = .{
   1395                             .name = try self.resolveTypeName(ty),
   1396                             .member_types = member_types[0..member_index],
   1397                         } });
   1398 
   1399                         try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
   1400                         return ty_ref;
   1401                     },
   1402                     .struct_type => |struct_type| struct_type,
   1403                     else => unreachable,
   1404                 };
   1405 
   1406                 if (struct_type.layout == .Packed) {
   1407                     return try self.resolveType(struct_type.backingIntType(ip).toType(), .direct);
   1408                 }
   1409 
   1410                 var member_types = std.ArrayList(CacheRef).init(self.gpa);
   1411                 defer member_types.deinit();
   1412 
   1413                 var member_names = std.ArrayList(CacheString).init(self.gpa);
   1414                 defer member_names.deinit();
   1415 
   1416                 var it = struct_type.iterateRuntimeOrder(ip);
   1417                 while (it.next()) |field_index| {
   1418                     const field_ty = struct_type.field_types.get(ip)[field_index].toType();
   1419                     if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
   1420                         // This is a zero-bit field - we only needed it for the alignment.
   1421                         continue;
   1422                     }
   1423 
   1424                     const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse
   1425                         try ip.getOrPutStringFmt(mod.gpa, "{d}", .{field_index});
   1426                     try member_types.append(try self.resolveType(field_ty, .indirect));
   1427                     try member_names.append(try self.spv.resolveString(ip.stringToSlice(field_name)));
   1428                 }
   1429 
   1430                 const ty_ref = try self.spv.resolve(.{ .struct_type = .{
   1431                     .name = try self.resolveTypeName(ty),
   1432                     .member_types = member_types.items,
   1433                     .member_names = member_names.items,
   1434                 } });
   1435 
   1436                 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
   1437                 return ty_ref;
   1438             },
   1439             .Optional => {
   1440                 const payload_ty = ty.optionalChild(mod);
   1441                 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
   1442                     // Just use a bool.
   1443                     // Note: Always generate the bool with indirect format, to save on some sanity
   1444                     // Perform the conversion to a direct bool when the field is extracted.
   1445                     return try self.resolveType(Type.bool, .indirect);
   1446                 }
   1447 
   1448                 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
   1449                 if (ty.optionalReprIsPayload(mod)) {
   1450                     // Optional is actually a pointer or a slice.
   1451                     return payload_ty_ref;
   1452                 }
   1453 
   1454                 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
   1455 
   1456                 const bool_ty_ref = try self.resolveType(Type.bool, .indirect);
   1457 
   1458                 const ty_ref = try self.spv.resolve(.{ .struct_type = .{
   1459                     .member_types = &.{ payload_ty_ref, bool_ty_ref },
   1460                     .member_names = &.{
   1461                         try self.spv.resolveString("payload"),
   1462                         try self.spv.resolveString("valid"),
   1463                     },
   1464                 } });
   1465 
   1466                 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
   1467                 return ty_ref;
   1468             },
   1469             .Union => return try self.resolveUnionType(ty, null),
   1470             .ErrorSet => return try self.intType(.unsigned, 16),
   1471             .ErrorUnion => {
   1472                 const payload_ty = ty.errorUnionPayload(mod);
   1473                 const error_ty_ref = try self.resolveType(Type.anyerror, .indirect);
   1474 
   1475                 const eu_layout = self.errorUnionLayout(payload_ty);
   1476                 if (!eu_layout.payload_has_bits) {
   1477                     return error_ty_ref;
   1478                 }
   1479 
   1480                 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
   1481 
   1482                 const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
   1483 
   1484                 var member_types: [2]CacheRef = undefined;
   1485                 var member_names: [2]CacheString = undefined;
   1486                 if (eu_layout.error_first) {
   1487                     // Put the error first
   1488                     member_types = .{ error_ty_ref, payload_ty_ref };
   1489                     member_names = .{
   1490                         try self.spv.resolveString("error"),
   1491                         try self.spv.resolveString("payload"),
   1492                     };
   1493                     // TODO: ABI padding?
   1494                 } else {
   1495                     // Put the payload first.
   1496                     member_types = .{ payload_ty_ref, error_ty_ref };
   1497                     member_names = .{
   1498                         try self.spv.resolveString("payload"),
   1499                         try self.spv.resolveString("error"),
   1500                     };
   1501                     // TODO: ABI padding?
   1502                 }
   1503 
   1504                 const ty_ref = try self.spv.resolve(.{ .struct_type = .{
   1505                     .name = try self.resolveTypeName(ty),
   1506                     .member_types = &member_types,
   1507                     .member_names = &member_names,
   1508                 } });
   1509 
   1510                 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
   1511                 return ty_ref;
   1512             },
   1513             .Opaque => {
   1514                 return try self.spv.resolve(.{
   1515                     .opaque_type = .{
   1516                         .name = .none, // TODO
   1517                     },
   1518                 });
   1519             },
   1520 
   1521             .Null,
   1522             .Undefined,
   1523             .EnumLiteral,
   1524             .ComptimeFloat,
   1525             .ComptimeInt,
   1526             .Type,
   1527             => unreachable, // Must be comptime.
   1528 
   1529             else => |tag| return self.todo("Implement zig type '{}'", .{tag}),
   1530         }
   1531     }
   1532 
   1533     fn spvStorageClass(as: std.builtin.AddressSpace) StorageClass {
   1534         return switch (as) {
   1535             .generic => .Generic,
   1536             .shared => .Workgroup,
   1537             .local => .Private,
   1538             .global => .CrossWorkgroup,
   1539             .constant => .UniformConstant,
   1540             .gs,
   1541             .fs,
   1542             .ss,
   1543             .param,
   1544             .flash,
   1545             .flash1,
   1546             .flash2,
   1547             .flash3,
   1548             .flash4,
   1549             .flash5,
   1550             => unreachable,
   1551         };
   1552     }
   1553 
   1554     const ErrorUnionLayout = struct {
   1555         payload_has_bits: bool,
   1556         error_first: bool,
   1557 
   1558         fn errorFieldIndex(self: @This()) u32 {
   1559             assert(self.payload_has_bits);
   1560             return if (self.error_first) 0 else 1;
   1561         }
   1562 
   1563         fn payloadFieldIndex(self: @This()) u32 {
   1564             assert(self.payload_has_bits);
   1565             return if (self.error_first) 1 else 0;
   1566         }
   1567     };
   1568 
   1569     fn errorUnionLayout(self: *DeclGen, payload_ty: Type) ErrorUnionLayout {
   1570         const mod = self.module;
   1571 
   1572         const error_align = Type.anyerror.abiAlignment(mod);
   1573         const payload_align = payload_ty.abiAlignment(mod);
   1574 
   1575         const error_first = error_align.compare(.gt, payload_align);
   1576         return .{
   1577             .payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(mod),
   1578             .error_first = error_first,
   1579         };
   1580     }
   1581 
   1582     const UnionLayout = struct {
   1583         active_field: u32,
   1584         active_field_ty: Type,
   1585         payload_size: u32,
   1586 
   1587         tag_size: u32,
   1588         tag_index: u32,
   1589         active_field_size: u32,
   1590         active_field_index: u32,
   1591         payload_padding_size: u32,
   1592         payload_padding_index: u32,
   1593         padding_size: u32,
   1594         padding_index: u32,
   1595         total_fields: u32,
   1596     };
   1597 
   1598     fn unionLayout(self: *DeclGen, ty: Type, maybe_active_field: ?usize) UnionLayout {
   1599         const mod = self.module;
   1600         const ip = &mod.intern_pool;
   1601         const layout = ty.unionGetLayout(self.module);
   1602         const union_obj = mod.typeToUnion(ty).?;
   1603 
   1604         const active_field = maybe_active_field orelse layout.most_aligned_field;
   1605         const active_field_ty = union_obj.field_types.get(ip)[active_field].toType();
   1606 
   1607         var union_layout = UnionLayout{
   1608             .active_field = @intCast(active_field),
   1609             .active_field_ty = active_field_ty,
   1610             .payload_size = @intCast(layout.payload_size),
   1611             .tag_size = @intCast(layout.tag_size),
   1612             .tag_index = undefined,
   1613             .active_field_size = undefined,
   1614             .active_field_index = undefined,
   1615             .payload_padding_size = undefined,
   1616             .payload_padding_index = undefined,
   1617             .padding_size = @intCast(layout.padding),
   1618             .padding_index = undefined,
   1619             .total_fields = undefined,
   1620         };
   1621 
   1622         union_layout.active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod))
   1623             @intCast(active_field_ty.abiSize(mod))
   1624         else
   1625             0;
   1626         union_layout.payload_padding_size = @intCast(layout.payload_size - union_layout.active_field_size);
   1627 
   1628         const tag_first = layout.tag_align.compare(.gte, layout.payload_align);
   1629         var field_index: u32 = 0;
   1630 
   1631         if (union_layout.tag_size != 0 and tag_first) {
   1632             union_layout.tag_index = field_index;
   1633             field_index += 1;
   1634         }
   1635 
   1636         if (union_layout.active_field_size != 0) {
   1637             union_layout.active_field_index = field_index;
   1638             field_index += 1;
   1639         }
   1640 
   1641         if (union_layout.payload_padding_size != 0) {
   1642             union_layout.payload_padding_index = field_index;
   1643             field_index += 1;
   1644         }
   1645 
   1646         if (union_layout.tag_size != 0 and !tag_first) {
   1647             union_layout.tag_index = field_index;
   1648             field_index += 1;
   1649         }
   1650 
   1651         if (union_layout.padding_size != 0) {
   1652             union_layout.padding_index = field_index;
   1653             field_index += 1;
   1654         }
   1655 
   1656         union_layout.total_fields = field_index;
   1657 
   1658         return union_layout;
   1659     }
   1660 
   1661     /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure.
   1662     /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry-
   1663     /// points. The test executor will then be able to invoke these to run the tests.
   1664     /// Note that tests are lowered according to std.builtin.TestFn, which is `fn () anyerror!void`.
   1665     /// (anyerror!void has the same layout as anyerror).
   1666     /// Each test declaration generates a function like.
   1667     ///   %anyerror = OpTypeInt 0 16
   1668     ///   %p_anyerror = OpTypePointer CrossWorkgroup %anyerror
   1669     ///   %K = OpTypeFunction %void %p_anyerror
   1670     ///
   1671     ///   %test = OpFunction %void %K
   1672     ///   %p_err = OpFunctionParameter %p_anyerror
   1673     ///   %lbl = OpLabel
   1674     ///   %result = OpFunctionCall %anyerror %func
   1675     ///   OpStore %p_err %result
   1676     ///   OpFunctionEnd
   1677     /// TODO is to also write out the error as a function call parameter, and to somehow fetch
   1678     /// the name of an error in the text executor.
   1679     fn generateTestEntryPoint(self: *DeclGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void {
   1680         const anyerror_ty_ref = try self.resolveType(Type.anyerror, .direct);
   1681         const ptr_anyerror_ty_ref = try self.spv.ptrType(anyerror_ty_ref, .CrossWorkgroup);
   1682         const void_ty_ref = try self.resolveType(Type.void, .direct);
   1683 
   1684         const kernel_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{
   1685             .return_type = void_ty_ref,
   1686             .parameters = &.{ptr_anyerror_ty_ref},
   1687         } });
   1688 
   1689         const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
   1690 
   1691         const spv_decl_index = try self.spv.allocDecl(.func);
   1692         const kernel_id = self.spv.declPtr(spv_decl_index).result_id;
   1693 
   1694         const error_id = self.spv.allocId();
   1695         const p_error_id = self.spv.allocId();
   1696 
   1697         const section = &self.spv.sections.functions;
   1698         try section.emit(self.spv.gpa, .OpFunction, .{
   1699             .id_result_type = self.typeId(void_ty_ref),
   1700             .id_result = kernel_id,
   1701             .function_control = .{},
   1702             .function_type = self.typeId(kernel_proto_ty_ref),
   1703         });
   1704         try section.emit(self.spv.gpa, .OpFunctionParameter, .{
   1705             .id_result_type = self.typeId(ptr_anyerror_ty_ref),
   1706             .id_result = p_error_id,
   1707         });
   1708         try section.emit(self.spv.gpa, .OpLabel, .{
   1709             .id_result = self.spv.allocId(),
   1710         });
   1711         try section.emit(self.spv.gpa, .OpFunctionCall, .{
   1712             .id_result_type = self.typeId(anyerror_ty_ref),
   1713             .id_result = error_id,
   1714             .function = test_id,
   1715         });
   1716         try section.emit(self.spv.gpa, .OpStore, .{
   1717             .pointer = p_error_id,
   1718             .object = error_id,
   1719         });
   1720         try section.emit(self.spv.gpa, .OpReturn, {});
   1721         try section.emit(self.spv.gpa, .OpFunctionEnd, {});
   1722 
   1723         try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index});
   1724 
   1725         // Just generate a quick other name because the intel runtime crashes when the entry-
   1726         // point name is the same as a different OpName.
   1727         const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name});
   1728         defer self.gpa.free(test_name);
   1729         try self.spv.declareEntryPoint(spv_decl_index, test_name);
   1730     }
   1731 
   1732     fn genDecl(self: *DeclGen) !void {
   1733         const mod = self.module;
   1734         const ip = &mod.intern_pool;
   1735         const decl = mod.declPtr(self.decl_index);
   1736         const spv_decl_index = try self.object.resolveDecl(mod, self.decl_index);
   1737 
   1738         const decl_id = self.spv.declPtr(spv_decl_index).result_id;
   1739 
   1740         try self.base_line_stack.append(self.gpa, decl.src_line);
   1741 
   1742         if (decl.val.getFunction(mod)) |_| {
   1743             assert(decl.ty.zigTypeTag(mod) == .Fn);
   1744             const fn_info = mod.typeToFunc(decl.ty).?;
   1745             const return_ty_ref = try self.resolveFnReturnType(fn_info.return_type.toType());
   1746 
   1747             const prototype_id = try self.resolveTypeId(decl.ty);
   1748             try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
   1749                 .id_result_type = self.typeId(return_ty_ref),
   1750                 .id_result = decl_id,
   1751                 .function_control = .{}, // TODO: We can set inline here if the type requires it.
   1752                 .function_type = prototype_id,
   1753             });
   1754 
   1755             try self.args.ensureUnusedCapacity(self.gpa, fn_info.param_types.len);
   1756             for (fn_info.param_types.get(ip)) |param_ty_index| {
   1757                 const param_ty = param_ty_index.toType();
   1758                 if (!param_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
   1759 
   1760                 const param_type_id = try self.resolveTypeId(param_ty);
   1761                 const arg_result_id = self.spv.allocId();
   1762                 try self.func.prologue.emit(self.spv.gpa, .OpFunctionParameter, .{
   1763                     .id_result_type = param_type_id,
   1764                     .id_result = arg_result_id,
   1765                 });
   1766                 self.args.appendAssumeCapacity(arg_result_id);
   1767             }
   1768 
   1769             // TODO: This could probably be done in a better way...
   1770             const root_block_id = self.spv.allocId();
   1771 
   1772             // The root block of a function declaration should appear before OpVariable instructions,
   1773             // so it is generated into the function's prologue.
   1774             try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{
   1775                 .id_result = root_block_id,
   1776             });
   1777             self.current_block_label_id = root_block_id;
   1778 
   1779             const main_body = self.air.getMainBody();
   1780             try self.genBody(main_body);
   1781 
   1782             // Append the actual code into the functions section.
   1783             try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
   1784             try self.spv.addFunction(spv_decl_index, self.func);
   1785 
   1786             const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module));
   1787             try self.spv.debugName(decl_id, fqn);
   1788 
   1789             // Temporarily generate a test kernel declaration if this is a test function.
   1790             if (self.module.test_functions.contains(self.decl_index)) {
   1791                 try self.generateTestEntryPoint(fqn, spv_decl_index);
   1792             }
   1793         } else {
   1794             const init_val = if (decl.val.getVariable(mod)) |payload|
   1795                 payload.init.toValue()
   1796             else
   1797                 decl.val;
   1798 
   1799             if (init_val.ip_index == .unreachable_value) {
   1800                 return self.todo("importing extern variables", .{});
   1801             }
   1802 
   1803             // Currently, initializers for CrossWorkgroup variables is not implemented
   1804             // in Mesa. Therefore we generate an initialization kernel instead.
   1805 
   1806             const void_ty_ref = try self.resolveType(Type.void, .direct);
   1807 
   1808             const initializer_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{
   1809                 .return_type = void_ty_ref,
   1810                 .parameters = &.{},
   1811             } });
   1812 
   1813             // Generate the actual variable for the global...
   1814             const final_storage_class = spvStorageClass(decl.@"addrspace");
   1815             const actual_storage_class = switch (final_storage_class) {
   1816                 .Generic => .CrossWorkgroup,
   1817                 else => final_storage_class,
   1818             };
   1819 
   1820             const ty_ref = try self.resolveType(decl.ty, .indirect);
   1821             const ptr_ty_ref = try self.spv.ptrType(ty_ref, actual_storage_class);
   1822 
   1823             const begin = self.spv.beginGlobal();
   1824             try self.spv.globals.section.emit(self.spv.gpa, .OpVariable, .{
   1825                 .id_result_type = self.typeId(ptr_ty_ref),
   1826                 .id_result = decl_id,
   1827                 .storage_class = actual_storage_class,
   1828             });
   1829 
   1830             // Now emit the instructions that initialize the variable.
   1831             const initializer_id = self.spv.allocId();
   1832             try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
   1833                 .id_result_type = self.typeId(void_ty_ref),
   1834                 .id_result = initializer_id,
   1835                 .function_control = .{},
   1836                 .function_type = self.typeId(initializer_proto_ty_ref),
   1837             });
   1838             const root_block_id = self.spv.allocId();
   1839             try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{
   1840                 .id_result = root_block_id,
   1841             });
   1842             self.current_block_label_id = root_block_id;
   1843 
   1844             const val_id = try self.constant(decl.ty, init_val, .indirect);
   1845             try self.func.body.emit(self.spv.gpa, .OpStore, .{
   1846                 .pointer = decl_id,
   1847                 .object = val_id,
   1848             });
   1849 
   1850             // TODO: We should be able to get rid of this by now...
   1851             self.spv.endGlobal(spv_decl_index, begin, decl_id, initializer_id);
   1852 
   1853             try self.func.body.emit(self.spv.gpa, .OpReturn, {});
   1854             try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
   1855             try self.spv.addFunction(spv_decl_index, self.func);
   1856 
   1857             const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module));
   1858             try self.spv.debugName(decl_id, fqn);
   1859             try self.spv.debugNameFmt(initializer_id, "initializer of {s}", .{fqn});
   1860         }
   1861     }
   1862 
   1863     fn intFromBool(self: *DeclGen, result_ty_ref: CacheRef, condition_id: IdRef) !IdRef {
   1864         const zero_id = try self.constInt(result_ty_ref, 0);
   1865         const one_id = try self.constInt(result_ty_ref, 1);
   1866         const result_id = self.spv.allocId();
   1867         try self.func.body.emit(self.spv.gpa, .OpSelect, .{
   1868             .id_result_type = self.typeId(result_ty_ref),
   1869             .id_result = result_id,
   1870             .condition = condition_id,
   1871             .object_1 = one_id,
   1872             .object_2 = zero_id,
   1873         });
   1874         return result_id;
   1875     }
   1876 
   1877     /// Convert representation from indirect (in memory) to direct (in 'register')
   1878     /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct).
   1879     fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {
   1880         const mod = self.module;
   1881         return switch (ty.zigTypeTag(mod)) {
   1882             .Bool => blk: {
   1883                 const direct_bool_ty_ref = try self.resolveType(ty, .direct);
   1884                 const indirect_bool_ty_ref = try self.resolveType(ty, .indirect);
   1885                 const zero_id = try self.constInt(indirect_bool_ty_ref, 0);
   1886                 const result_id = self.spv.allocId();
   1887                 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
   1888                     .id_result_type = self.typeId(direct_bool_ty_ref),
   1889                     .id_result = result_id,
   1890                     .operand_1 = operand_id,
   1891                     .operand_2 = zero_id,
   1892                 });
   1893                 break :blk result_id;
   1894             },
   1895             else => operand_id,
   1896         };
   1897     }
   1898 
   1899     /// Convert representation from direct (in 'register) to direct (in memory)
   1900     /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect).
   1901     fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {
   1902         const mod = self.module;
   1903         return switch (ty.zigTypeTag(mod)) {
   1904             .Bool => blk: {
   1905                 const indirect_bool_ty_ref = try self.resolveType(ty, .indirect);
   1906                 break :blk self.intFromBool(indirect_bool_ty_ref, operand_id);
   1907             },
   1908             else => operand_id,
   1909         };
   1910     }
   1911 
   1912     fn extractField(self: *DeclGen, result_ty: Type, object: IdRef, field: u32) !IdRef {
   1913         const result_ty_ref = try self.resolveType(result_ty, .indirect);
   1914         const result_id = self.spv.allocId();
   1915         const indexes = [_]u32{field};
   1916         try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{
   1917             .id_result_type = self.typeId(result_ty_ref),
   1918             .id_result = result_id,
   1919             .composite = object,
   1920             .indexes = &indexes,
   1921         });
   1922         // Convert bools; direct structs have their field types as indirect values.
   1923         return try self.convertToDirect(result_ty, result_id);
   1924     }
   1925 
   1926     fn load(self: *DeclGen, value_ty: Type, ptr_id: IdRef, is_volatile: bool) !IdRef {
   1927         const indirect_value_ty_ref = try self.resolveType(value_ty, .indirect);
   1928         const result_id = self.spv.allocId();
   1929         const access = spec.MemoryAccess.Extended{
   1930             .Volatile = is_volatile,
   1931         };
   1932         try self.func.body.emit(self.spv.gpa, .OpLoad, .{
   1933             .id_result_type = self.typeId(indirect_value_ty_ref),
   1934             .id_result = result_id,
   1935             .pointer = ptr_id,
   1936             .memory_access = access,
   1937         });
   1938         return try self.convertToDirect(value_ty, result_id);
   1939     }
   1940 
   1941     fn store(self: *DeclGen, value_ty: Type, ptr_id: IdRef, value_id: IdRef, is_volatile: bool) !void {
   1942         const indirect_value_id = try self.convertToIndirect(value_ty, value_id);
   1943         const access = spec.MemoryAccess.Extended{
   1944             .Volatile = is_volatile,
   1945         };
   1946         try self.func.body.emit(self.spv.gpa, .OpStore, .{
   1947             .pointer = ptr_id,
   1948             .object = indirect_value_id,
   1949             .memory_access = access,
   1950         });
   1951     }
   1952 
   1953     fn genBody(self: *DeclGen, body: []const Air.Inst.Index) Error!void {
   1954         for (body) |inst| {
   1955             try self.genInst(inst);
   1956         }
   1957     }
   1958 
   1959     fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void {
   1960         const mod = self.module;
   1961         const ip = &mod.intern_pool;
   1962         // TODO: remove now-redundant isUnused calls from AIR handler functions
   1963         if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip))
   1964             return;
   1965 
   1966         const air_tags = self.air.instructions.items(.tag);
   1967         const maybe_result_id: ?IdRef = switch (air_tags[inst]) {
   1968             // zig fmt: off
   1969             .add, .add_wrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd, true),
   1970             .sub, .sub_wrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub, true),
   1971             .mul, .mul_wrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul, true),
   1972 
   1973             .div_float,
   1974             .div_float_optimized,
   1975             // TODO: Check that this is the right operation.
   1976             .div_trunc,
   1977             .div_trunc_optimized,
   1978             => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv, false),
   1979             // TODO: Check if this is the right operation
   1980             // TODO: Make airArithOp for rem not emit a mask for the LHS.
   1981             .rem,
   1982             .rem_optimized,
   1983             => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem, false),
   1984 
   1985             .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),
   1986             .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),
   1987 
   1988             .shuffle => try self.airShuffle(inst),
   1989 
   1990             .ptr_add => try self.airPtrAdd(inst),
   1991             .ptr_sub => try self.airPtrSub(inst),
   1992 
   1993             .bit_and  => try self.airBinOpSimple(inst, .OpBitwiseAnd),
   1994             .bit_or   => try self.airBinOpSimple(inst, .OpBitwiseOr),
   1995             .xor      => try self.airBinOpSimple(inst, .OpBitwiseXor),
   1996             .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),
   1997             .bool_or  => try self.airBinOpSimple(inst, .OpLogicalOr),
   1998 
   1999             .shl => try self.airShift(inst, .OpShiftLeftLogical),
   2000 
   2001             .min => try self.airMinMax(inst, .lt),
   2002             .max => try self.airMinMax(inst, .gt),
   2003 
   2004             .bitcast         => try self.airBitCast(inst),
   2005             .intcast, .trunc => try self.airIntCast(inst),
   2006             .int_from_ptr    => try self.airIntFromPtr(inst),
   2007             .float_from_int  => try self.airFloatFromInt(inst),
   2008             .int_from_float  => try self.airIntFromFloat(inst),
   2009             .fpext, .fptrunc => try self.airFloatCast(inst),
   2010             .not             => try self.airNot(inst),
   2011 
   2012             .array_to_slice => try self.airArrayToSlice(inst),
   2013             .slice          => try self.airSlice(inst),
   2014             .aggregate_init => try self.airAggregateInit(inst),
   2015             .memcpy         => return self.airMemcpy(inst),
   2016 
   2017             .slice_ptr      => try self.airSliceField(inst, 0),
   2018             .slice_len      => try self.airSliceField(inst, 1),
   2019             .slice_elem_ptr => try self.airSliceElemPtr(inst),
   2020             .slice_elem_val => try self.airSliceElemVal(inst),
   2021             .ptr_elem_ptr   => try self.airPtrElemPtr(inst),
   2022             .ptr_elem_val   => try self.airPtrElemVal(inst),
   2023             .array_elem_val => try self.airArrayElemVal(inst),
   2024 
   2025             .set_union_tag => return self.airSetUnionTag(inst),
   2026             .get_union_tag => try self.airGetUnionTag(inst),
   2027             .union_init => try self.airUnionInit(inst),
   2028 
   2029             .struct_field_val => try self.airStructFieldVal(inst),
   2030             .field_parent_ptr => try self.airFieldParentPtr(inst),
   2031 
   2032             .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
   2033             .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
   2034             .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
   2035             .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
   2036 
   2037             .cmp_eq     => try self.airCmp(inst, .eq),
   2038             .cmp_neq    => try self.airCmp(inst, .neq),
   2039             .cmp_gt     => try self.airCmp(inst, .gt),
   2040             .cmp_gte    => try self.airCmp(inst, .gte),
   2041             .cmp_lt     => try self.airCmp(inst, .lt),
   2042             .cmp_lte    => try self.airCmp(inst, .lte),
   2043             .cmp_vector => try self.airVectorCmp(inst),
   2044 
   2045             .arg     => self.airArg(),
   2046             .alloc   => try self.airAlloc(inst),
   2047             // TODO: We probably need to have a special implementation of this for the C abi.
   2048             .ret_ptr => try self.airAlloc(inst),
   2049             .block   => try self.airBlock(inst),
   2050 
   2051             .load               => try self.airLoad(inst),
   2052             .store, .store_safe => return self.airStore(inst),
   2053 
   2054             .br             => return self.airBr(inst),
   2055             .breakpoint     => return,
   2056             .cond_br        => return self.airCondBr(inst),
   2057             .loop           => return self.airLoop(inst),
   2058             .ret            => return self.airRet(inst),
   2059             .ret_load       => return self.airRetLoad(inst),
   2060             .@"try"         => try self.airTry(inst),
   2061             .switch_br      => return self.airSwitchBr(inst),
   2062             .unreach, .trap => return self.airUnreach(),
   2063 
   2064             .dbg_stmt                  => return self.airDbgStmt(inst),
   2065             .dbg_inline_begin          => return self.airDbgInlineBegin(inst),
   2066             .dbg_inline_end            => return self.airDbgInlineEnd(inst),
   2067             .dbg_var_ptr, .dbg_var_val => return self.airDbgVar(inst),
   2068             .dbg_block_begin  => return,
   2069             .dbg_block_end    => return,
   2070 
   2071             .unwrap_errunion_err => try self.airErrUnionErr(inst),
   2072             .unwrap_errunion_payload => try self.airErrUnionPayload(inst),
   2073             .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
   2074             .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
   2075 
   2076             .is_null     => try self.airIsNull(inst, .is_null),
   2077             .is_non_null => try self.airIsNull(inst, .is_non_null),
   2078             .is_err      => try self.airIsErr(inst, .is_err),
   2079             .is_non_err  => try self.airIsErr(inst, .is_non_err),
   2080 
   2081             .optional_payload => try self.airUnwrapOptional(inst),
   2082             .wrap_optional    => try self.airWrapOptional(inst),
   2083 
   2084             .assembly => try self.airAssembly(inst),
   2085 
   2086             .call              => try self.airCall(inst, .auto),
   2087             .call_always_tail  => try self.airCall(inst, .always_tail),
   2088             .call_never_tail   => try self.airCall(inst, .never_tail),
   2089             .call_never_inline => try self.airCall(inst, .never_inline),
   2090             // zig fmt: on
   2091 
   2092             else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}),
   2093         };
   2094 
   2095         const result_id = maybe_result_id orelse return;
   2096         try self.inst_results.putNoClobber(self.gpa, inst, result_id);
   2097     }
   2098 
   2099     fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef {
   2100         const mod = self.module;
   2101 
   2102         if (ty.isVector(mod)) {
   2103             const child_ty = ty.childType(mod);
   2104             const vector_len = ty.vectorLen(mod);
   2105 
   2106             var constituents = try self.gpa.alloc(IdRef, vector_len);
   2107             defer self.gpa.free(constituents);
   2108 
   2109             for (constituents, 0..) |*constituent, i| {
   2110                 const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i));
   2111                 const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i));
   2112                 const result_id = try self.binOpSimple(child_ty, lhs_index_id, rhs_index_id, opcode);
   2113                 constituent.* = try self.convertToIndirect(child_ty, result_id);
   2114             }
   2115 
   2116             const result_ty = try self.resolveType(child_ty, .indirect);
   2117             const result_ty_ref = try self.spv.arrayType(vector_len, result_ty);
   2118             return try self.constructArray(result_ty_ref, constituents);
   2119         }
   2120 
   2121         const result_id = self.spv.allocId();
   2122         const result_type_id = try self.resolveTypeId(ty);
   2123         try self.func.body.emit(self.spv.gpa, opcode, .{
   2124             .id_result_type = result_type_id,
   2125             .id_result = result_id,
   2126             .operand_1 = lhs_id,
   2127             .operand_2 = rhs_id,
   2128         });
   2129         return result_id;
   2130     }
   2131 
   2132     fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef {
   2133         if (self.liveness.isUnused(inst)) return null;
   2134 
   2135         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   2136         const lhs_id = try self.resolve(bin_op.lhs);
   2137         const rhs_id = try self.resolve(bin_op.rhs);
   2138         const ty = self.typeOf(bin_op.lhs);
   2139 
   2140         return try self.binOpSimple(ty, lhs_id, rhs_id, opcode);
   2141     }
   2142 
   2143     fn airShift(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef {
   2144         if (self.liveness.isUnused(inst)) return null;
   2145         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   2146         const lhs_id = try self.resolve(bin_op.lhs);
   2147         const rhs_id = try self.resolve(bin_op.rhs);
   2148         const result_type_id = try self.resolveTypeId(self.typeOfIndex(inst));
   2149 
   2150         // the shift and the base must be the same type in SPIR-V, but in Zig the shift is a smaller int.
   2151         const shift_id = self.spv.allocId();
   2152         try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
   2153             .id_result_type = result_type_id,
   2154             .id_result = shift_id,
   2155             .unsigned_value = rhs_id,
   2156         });
   2157 
   2158         const result_id = self.spv.allocId();
   2159         try self.func.body.emit(self.spv.gpa, opcode, .{
   2160             .id_result_type = result_type_id,
   2161             .id_result = result_id,
   2162             .base = lhs_id,
   2163             .shift = shift_id,
   2164         });
   2165         return result_id;
   2166     }
   2167 
   2168     fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef {
   2169         if (self.liveness.isUnused(inst)) return null;
   2170 
   2171         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   2172         const lhs_id = try self.resolve(bin_op.lhs);
   2173         const rhs_id = try self.resolve(bin_op.rhs);
   2174         const result_ty = self.typeOfIndex(inst);
   2175         const result_ty_ref = try self.resolveType(result_ty, .direct);
   2176 
   2177         const info = try self.arithmeticTypeInfo(result_ty);
   2178         // TODO: Use fmin for OpenCL
   2179         const cmp_id = try self.cmp(op, result_ty, lhs_id, rhs_id);
   2180         const selection_id = switch (info.class) {
   2181             .float => blk: {
   2182                 // cmp uses OpFOrd. When we have 0 [<>] nan this returns false,
   2183                 // but we want it to pick lhs. Therefore we also have to check if
   2184                 // rhs is nan. We don't need to care about the result when both
   2185                 // are nan.
   2186                 const rhs_is_nan_id = self.spv.allocId();
   2187                 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
   2188                 try self.func.body.emit(self.spv.gpa, .OpIsNan, .{
   2189                     .id_result_type = self.typeId(bool_ty_ref),
   2190                     .id_result = rhs_is_nan_id,
   2191                     .x = rhs_id,
   2192                 });
   2193                 const float_cmp_id = self.spv.allocId();
   2194                 try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{
   2195                     .id_result_type = self.typeId(bool_ty_ref),
   2196                     .id_result = float_cmp_id,
   2197                     .operand_1 = cmp_id,
   2198                     .operand_2 = rhs_is_nan_id,
   2199                 });
   2200                 break :blk float_cmp_id;
   2201             },
   2202             else => cmp_id,
   2203         };
   2204 
   2205         const result_id = self.spv.allocId();
   2206         try self.func.body.emit(self.spv.gpa, .OpSelect, .{
   2207             .id_result_type = self.typeId(result_ty_ref),
   2208             .id_result = result_id,
   2209             .condition = selection_id,
   2210             .object_1 = lhs_id,
   2211             .object_2 = rhs_id,
   2212         });
   2213         return result_id;
   2214     }
   2215 
   2216     /// This function canonicalizes a "strange" integer value:
   2217     /// For unsigned integers, the value is masked so that only the relevant bits can contain
   2218     /// non-zeros.
   2219     /// For signed integers, the value is also sign extended.
   2220     fn normalizeInt(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef {
   2221         assert(info.class != .composite_integer); // TODO
   2222         if (info.bits == info.backing_bits) {
   2223             return value_id;
   2224         }
   2225 
   2226         switch (info.signedness) {
   2227             .unsigned => {
   2228                 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
   2229                 const result_id = self.spv.allocId();
   2230                 const mask_id = try self.constInt(ty_ref, mask_value);
   2231                 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{
   2232                     .id_result_type = self.typeId(ty_ref),
   2233                     .id_result = result_id,
   2234                     .operand_1 = value_id,
   2235                     .operand_2 = mask_id,
   2236                 });
   2237                 return result_id;
   2238             },
   2239             .signed => {
   2240                 // Shift left and right so that we can copy the sight bit that way.
   2241                 const shift_amt_id = try self.constInt(ty_ref, info.backing_bits - info.bits);
   2242                 const left_id = self.spv.allocId();
   2243                 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{
   2244                     .id_result_type = self.typeId(ty_ref),
   2245                     .id_result = left_id,
   2246                     .base = value_id,
   2247                     .shift = shift_amt_id,
   2248                 });
   2249                 const right_id = self.spv.allocId();
   2250                 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{
   2251                     .id_result_type = self.typeId(ty_ref),
   2252                     .id_result = right_id,
   2253                     .base = left_id,
   2254                     .shift = shift_amt_id,
   2255                 });
   2256                 return right_id;
   2257             },
   2258         }
   2259     }
   2260 
   2261     fn airArithOp(
   2262         self: *DeclGen,
   2263         inst: Air.Inst.Index,
   2264         comptime fop: Opcode,
   2265         comptime sop: Opcode,
   2266         comptime uop: Opcode,
   2267         /// true if this operation holds under modular arithmetic.
   2268         comptime modular: bool,
   2269     ) !?IdRef {
   2270         if (self.liveness.isUnused(inst)) return null;
   2271 
   2272         // LHS and RHS are guaranteed to have the same type, and AIR guarantees
   2273         // the result to be the same as the LHS and RHS, which matches SPIR-V.
   2274         const ty = self.typeOfIndex(inst);
   2275         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   2276         const lhs_id = try self.resolve(bin_op.lhs);
   2277         const rhs_id = try self.resolve(bin_op.rhs);
   2278 
   2279         assert(self.typeOf(bin_op.lhs).eql(ty, self.module));
   2280         assert(self.typeOf(bin_op.rhs).eql(ty, self.module));
   2281 
   2282         return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop, modular);
   2283     }
   2284 
   2285     fn arithOp(
   2286         self: *DeclGen,
   2287         ty: Type,
   2288         lhs_id_: IdRef,
   2289         rhs_id_: IdRef,
   2290         comptime fop: Opcode,
   2291         comptime sop: Opcode,
   2292         comptime uop: Opcode,
   2293         /// true if this operation holds under modular arithmetic.
   2294         comptime modular: bool,
   2295     ) !IdRef {
   2296         var rhs_id = rhs_id_;
   2297         var lhs_id = lhs_id_;
   2298 
   2299         const mod = self.module;
   2300         const result_ty_ref = try self.resolveType(ty, .direct);
   2301 
   2302         if (ty.isVector(mod)) {
   2303             const child_ty = ty.childType(mod);
   2304             const vector_len = ty.vectorLen(mod);
   2305             var constituents = try self.gpa.alloc(IdRef, vector_len);
   2306             defer self.gpa.free(constituents);
   2307 
   2308             for (constituents, 0..) |*constituent, i| {
   2309                 const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i));
   2310                 const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i));
   2311                 constituent.* = try self.arithOp(child_ty, lhs_index_id, rhs_index_id, fop, sop, uop, modular);
   2312             }
   2313 
   2314             return self.constructArray(result_ty_ref, constituents);
   2315         }
   2316 
   2317         // Binary operations are generally applicable to both scalar and vector operations
   2318         // in SPIR-V, but int and float versions of operations require different opcodes.
   2319         const info = try self.arithmeticTypeInfo(ty);
   2320 
   2321         const opcode_index: usize = switch (info.class) {
   2322             .composite_integer => {
   2323                 return self.todo("binary operations for composite integers", .{});
   2324             },
   2325             .strange_integer => blk: {
   2326                 if (!modular) {
   2327                     lhs_id = try self.normalizeInt(result_ty_ref, lhs_id, info);
   2328                     rhs_id = try self.normalizeInt(result_ty_ref, rhs_id, info);
   2329                 }
   2330                 break :blk switch (info.signedness) {
   2331                     .signed => @as(usize, 1),
   2332                     .unsigned => @as(usize, 2),
   2333                 };
   2334             },
   2335             .integer => switch (info.signedness) {
   2336                 .signed => @as(usize, 1),
   2337                 .unsigned => @as(usize, 2),
   2338             },
   2339             .float => 0,
   2340             .bool => unreachable,
   2341         };
   2342 
   2343         const result_id = self.spv.allocId();
   2344         const operands = .{
   2345             .id_result_type = self.typeId(result_ty_ref),
   2346             .id_result = result_id,
   2347             .operand_1 = lhs_id,
   2348             .operand_2 = rhs_id,
   2349         };
   2350 
   2351         switch (opcode_index) {
   2352             0 => try self.func.body.emit(self.spv.gpa, fop, operands),
   2353             1 => try self.func.body.emit(self.spv.gpa, sop, operands),
   2354             2 => try self.func.body.emit(self.spv.gpa, uop, operands),
   2355             else => unreachable,
   2356         }
   2357         // TODO: Trap on overflow? Probably going to be annoying.
   2358         // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
   2359 
   2360         return result_id;
   2361     }
   2362 
   2363     fn airAddSubOverflow(
   2364         self: *DeclGen,
   2365         inst: Air.Inst.Index,
   2366         comptime add: Opcode,
   2367         comptime ucmp: Opcode,
   2368         comptime scmp: Opcode,
   2369     ) !?IdRef {
   2370         if (self.liveness.isUnused(inst)) return null;
   2371 
   2372         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   2373         const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
   2374         const lhs = try self.resolve(extra.lhs);
   2375         const rhs = try self.resolve(extra.rhs);
   2376 
   2377         const operand_ty = self.typeOf(extra.lhs);
   2378         const result_ty = self.typeOfIndex(inst);
   2379 
   2380         const info = try self.arithmeticTypeInfo(operand_ty);
   2381         switch (info.class) {
   2382             .composite_integer => return self.todo("overflow ops for composite integers", .{}),
   2383             .strange_integer => return self.todo("overflow ops for strange integers", .{}),
   2384             .integer => {},
   2385             .float, .bool => unreachable,
   2386         }
   2387 
   2388         // The operand type must be the same as the result type in SPIR-V, which
   2389         // is the same as in Zig.
   2390         const operand_ty_ref = try self.resolveType(operand_ty, .direct);
   2391         const operand_ty_id = self.typeId(operand_ty_ref);
   2392 
   2393         const bool_ty_ref = try self.resolveType(Type.bool, .direct);
   2394 
   2395         const ov_ty = result_ty.structFieldType(1, self.module);
   2396         // Note: result is stored in a struct, so indirect representation.
   2397         const ov_ty_ref = try self.resolveType(ov_ty, .indirect);
   2398 
   2399         // TODO: Operations other than addition.
   2400         const value_id = self.spv.allocId();
   2401         try self.func.body.emit(self.spv.gpa, add, .{
   2402             .id_result_type = operand_ty_id,
   2403             .id_result = value_id,
   2404             .operand_1 = lhs,
   2405             .operand_2 = rhs,
   2406         });
   2407 
   2408         const overflowed_id = switch (info.signedness) {
   2409             .unsigned => blk: {
   2410                 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.
   2411                 // For subtraction the conditions need to be swapped.
   2412                 const overflowed_id = self.spv.allocId();
   2413                 try self.func.body.emit(self.spv.gpa, ucmp, .{
   2414                     .id_result_type = self.typeId(bool_ty_ref),
   2415                     .id_result = overflowed_id,
   2416                     .operand_1 = value_id,
   2417                     .operand_2 = lhs,
   2418                 });
   2419                 break :blk overflowed_id;
   2420             },
   2421             .signed => blk: {
   2422                 // lhs - rhs
   2423                 // For addition, overflow happened if:
   2424                 // - rhs is negative and value > lhs
   2425                 // - rhs is positive and value < lhs
   2426                 // This can be shortened to:
   2427                 //   (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs)
   2428                 // = (rhs < 0) == (value > lhs)
   2429                 // = (rhs < 0) == (lhs < value)
   2430                 // Note that signed overflow is also wrapping in spir-v.
   2431                 // For subtraction, overflow happened if:
   2432                 // - rhs is negative and value < lhs
   2433                 // - rhs is positive and value > lhs
   2434                 // This can be shortened to:
   2435                 //   (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs)
   2436                 // = (rhs < 0) == (value < lhs)
   2437                 // = (rhs < 0) == (lhs > value)
   2438 
   2439                 const rhs_lt_zero_id = self.spv.allocId();
   2440                 const zero_id = try self.constInt(operand_ty_ref, 0);
   2441                 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{
   2442                     .id_result_type = self.typeId(bool_ty_ref),
   2443                     .id_result = rhs_lt_zero_id,
   2444                     .operand_1 = rhs,
   2445                     .operand_2 = zero_id,
   2446                 });
   2447 
   2448                 const value_gt_lhs_id = self.spv.allocId();
   2449                 try self.func.body.emit(self.spv.gpa, scmp, .{
   2450                     .id_result_type = self.typeId(bool_ty_ref),
   2451                     .id_result = value_gt_lhs_id,
   2452                     .operand_1 = lhs,
   2453                     .operand_2 = value_id,
   2454                 });
   2455 
   2456                 const overflowed_id = self.spv.allocId();
   2457                 try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{
   2458                     .id_result_type = self.typeId(bool_ty_ref),
   2459                     .id_result = overflowed_id,
   2460                     .operand_1 = rhs_lt_zero_id,
   2461                     .operand_2 = value_gt_lhs_id,
   2462                 });
   2463                 break :blk overflowed_id;
   2464             },
   2465         };
   2466 
   2467         // Construct the struct that Zig wants as result.
   2468         // The value should already be the correct type.
   2469         const ov_id = try self.intFromBool(ov_ty_ref, overflowed_id);
   2470         const result_ty_ref = try self.resolveType(result_ty, .direct);
   2471         return try self.constructStruct(result_ty_ref, &.{
   2472             value_id,
   2473             ov_id,
   2474         });
   2475     }
   2476 
   2477     fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2478         const mod = self.module;
   2479         if (self.liveness.isUnused(inst)) return null;
   2480         const ty = self.typeOfIndex(inst);
   2481         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   2482         const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
   2483         const a = try self.resolve(extra.a);
   2484         const b = try self.resolve(extra.b);
   2485         const mask = extra.mask.toValue();
   2486         const mask_len = extra.mask_len;
   2487         const a_len = self.typeOf(extra.a).vectorLen(mod);
   2488 
   2489         const result_id = self.spv.allocId();
   2490         const result_type_id = try self.resolveTypeId(ty);
   2491         // Similar to LLVM, SPIR-V uses indices larger than the length of the first vector
   2492         // to index into the second vector.
   2493         try self.func.body.emitRaw(self.spv.gpa, .OpVectorShuffle, 4 + mask_len);
   2494         self.func.body.writeOperand(spec.IdResultType, result_type_id);
   2495         self.func.body.writeOperand(spec.IdResult, result_id);
   2496         self.func.body.writeOperand(spec.IdRef, a);
   2497         self.func.body.writeOperand(spec.IdRef, b);
   2498 
   2499         var i: usize = 0;
   2500         while (i < mask_len) : (i += 1) {
   2501             const elem = try mask.elemValue(mod, i);
   2502             if (elem.isUndef(mod)) {
   2503                 self.func.body.writeOperand(spec.LiteralInteger, 0xFFFF_FFFF);
   2504             } else {
   2505                 const int = elem.toSignedInt(mod);
   2506                 const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int + a_len));
   2507                 self.func.body.writeOperand(spec.LiteralInteger, unsigned);
   2508             }
   2509         }
   2510         return result_id;
   2511     }
   2512 
   2513     fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef {
   2514         const index_ty_ref = try self.intType(.unsigned, 32);
   2515         const ids = try self.gpa.alloc(IdRef, indices.len);
   2516         errdefer self.gpa.free(ids);
   2517         for (indices, ids) |index, *id| {
   2518             id.* = try self.constInt(index_ty_ref, index);
   2519         }
   2520 
   2521         return ids;
   2522     }
   2523 
   2524     fn accessChainId(
   2525         self: *DeclGen,
   2526         result_ty_ref: CacheRef,
   2527         base: IdRef,
   2528         indices: []const IdRef,
   2529     ) !IdRef {
   2530         const result_id = self.spv.allocId();
   2531         try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{
   2532             .id_result_type = self.typeId(result_ty_ref),
   2533             .id_result = result_id,
   2534             .base = base,
   2535             .indexes = indices,
   2536         });
   2537         return result_id;
   2538     }
   2539 
   2540     /// AccessChain is essentially PtrAccessChain with 0 as initial argument. The effective
   2541     /// difference lies in whether the resulting type of the first dereference will be the
   2542     /// same as that of the base pointer, or that of a dereferenced base pointer. AccessChain
   2543     /// is the latter and PtrAccessChain is the former.
   2544     fn accessChain(
   2545         self: *DeclGen,
   2546         result_ty_ref: CacheRef,
   2547         base: IdRef,
   2548         indices: []const u32,
   2549     ) !IdRef {
   2550         const ids = try self.indicesToIds(indices);
   2551         defer self.gpa.free(ids);
   2552         return try self.accessChainId(result_ty_ref, base, ids);
   2553     }
   2554 
   2555     fn ptrAccessChain(
   2556         self: *DeclGen,
   2557         result_ty_ref: CacheRef,
   2558         base: IdRef,
   2559         element: IdRef,
   2560         indices: []const u32,
   2561     ) !IdRef {
   2562         const ids = try self.indicesToIds(indices);
   2563         defer self.gpa.free(ids);
   2564 
   2565         const result_id = self.spv.allocId();
   2566         try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{
   2567             .id_result_type = self.typeId(result_ty_ref),
   2568             .id_result = result_id,
   2569             .base = base,
   2570             .element = element,
   2571             .indexes = ids,
   2572         });
   2573         return result_id;
   2574     }
   2575 
   2576     fn ptrAdd(self: *DeclGen, result_ty: Type, ptr_ty: Type, ptr_id: IdRef, offset_id: IdRef) !IdRef {
   2577         const mod = self.module;
   2578         const result_ty_ref = try self.resolveType(result_ty, .direct);
   2579 
   2580         switch (ptr_ty.ptrSize(mod)) {
   2581             .One => {
   2582                 // Pointer to array
   2583                 // TODO: Is this correct?
   2584                 return try self.accessChainId(result_ty_ref, ptr_id, &.{offset_id});
   2585             },
   2586             .C, .Many => {
   2587                 return try self.ptrAccessChain(result_ty_ref, ptr_id, offset_id, &.{});
   2588             },
   2589             .Slice => {
   2590                 // TODO: This is probably incorrect. A slice should be returned here, though this is what llvm does.
   2591                 const slice_ptr_id = try self.extractField(result_ty, ptr_id, 0);
   2592                 return try self.ptrAccessChain(result_ty_ref, slice_ptr_id, offset_id, &.{});
   2593             },
   2594         }
   2595     }
   2596 
   2597     fn airPtrAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2598         if (self.liveness.isUnused(inst)) return null;
   2599         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   2600         const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   2601         const ptr_id = try self.resolve(bin_op.lhs);
   2602         const offset_id = try self.resolve(bin_op.rhs);
   2603         const ptr_ty = self.typeOf(bin_op.lhs);
   2604         const result_ty = self.typeOfIndex(inst);
   2605 
   2606         return try self.ptrAdd(result_ty, ptr_ty, ptr_id, offset_id);
   2607     }
   2608 
   2609     fn airPtrSub(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2610         if (self.liveness.isUnused(inst)) return null;
   2611         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   2612         const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   2613         const ptr_id = try self.resolve(bin_op.lhs);
   2614         const ptr_ty = self.typeOf(bin_op.lhs);
   2615         const offset_id = try self.resolve(bin_op.rhs);
   2616         const offset_ty = self.typeOf(bin_op.rhs);
   2617         const offset_ty_ref = try self.resolveType(offset_ty, .direct);
   2618         const result_ty = self.typeOfIndex(inst);
   2619 
   2620         const negative_offset_id = self.spv.allocId();
   2621         try self.func.body.emit(self.spv.gpa, .OpSNegate, .{
   2622             .id_result_type = self.typeId(offset_ty_ref),
   2623             .id_result = negative_offset_id,
   2624             .operand = offset_id,
   2625         });
   2626         return try self.ptrAdd(result_ty, ptr_ty, ptr_id, negative_offset_id);
   2627     }
   2628 
   2629     fn cmp(
   2630         self: *DeclGen,
   2631         op: std.math.CompareOperator,
   2632         ty: Type,
   2633         lhs_id: IdRef,
   2634         rhs_id: IdRef,
   2635     ) !IdRef {
   2636         const mod = self.module;
   2637         var cmp_lhs_id = lhs_id;
   2638         var cmp_rhs_id = rhs_id;
   2639         const bool_ty_ref = try self.resolveType(Type.bool, .direct);
   2640         const op_ty = switch (ty.zigTypeTag(mod)) {
   2641             .Int, .Bool, .Float => ty,
   2642             .Enum => ty.intTagType(mod),
   2643             .ErrorSet => Type.u16,
   2644             .Pointer => blk: {
   2645                 // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are
   2646                 // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using
   2647                 // OpConvertPtrToU...
   2648                 cmp_lhs_id = self.spv.allocId();
   2649                 cmp_rhs_id = self.spv.allocId();
   2650 
   2651                 const usize_ty_id = self.typeId(try self.sizeType());
   2652 
   2653                 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{
   2654                     .id_result_type = usize_ty_id,
   2655                     .id_result = cmp_lhs_id,
   2656                     .pointer = lhs_id,
   2657                 });
   2658 
   2659                 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{
   2660                     .id_result_type = usize_ty_id,
   2661                     .id_result = cmp_rhs_id,
   2662                     .pointer = rhs_id,
   2663                 });
   2664 
   2665                 break :blk Type.usize;
   2666             },
   2667             .Optional => {
   2668                 const payload_ty = ty.optionalChild(mod);
   2669                 if (ty.optionalReprIsPayload(mod)) {
   2670                     assert(payload_ty.hasRuntimeBitsIgnoreComptime(mod));
   2671                     assert(!payload_ty.isSlice(mod));
   2672                     return self.cmp(op, payload_ty, lhs_id, rhs_id);
   2673                 }
   2674 
   2675                 const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod))
   2676                     try self.extractField(Type.bool, lhs_id, 1)
   2677                 else
   2678                     try self.convertToDirect(Type.bool, lhs_id);
   2679 
   2680                 const rhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod))
   2681                     try self.extractField(Type.bool, rhs_id, 1)
   2682                 else
   2683                     try self.convertToDirect(Type.bool, rhs_id);
   2684 
   2685                 const valid_cmp_id = try self.cmp(op, Type.bool, lhs_valid_id, rhs_valid_id);
   2686                 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
   2687                     return valid_cmp_id;
   2688                 }
   2689 
   2690                 // TODO: Should we short circuit here? It shouldn't affect correctness, but
   2691                 // perhaps it will generate more efficient code.
   2692 
   2693                 const lhs_pl_id = try self.extractField(payload_ty, lhs_id, 0);
   2694                 const rhs_pl_id = try self.extractField(payload_ty, rhs_id, 0);
   2695 
   2696                 const pl_cmp_id = try self.cmp(op, payload_ty, lhs_pl_id, rhs_pl_id);
   2697 
   2698                 // op == .eq  => lhs_valid == rhs_valid && lhs_pl == rhs_pl
   2699                 // op == .neq => lhs_valid != rhs_valid || lhs_pl != rhs_pl
   2700 
   2701                 const result_id = self.spv.allocId();
   2702                 const args = .{
   2703                     .id_result_type = self.typeId(bool_ty_ref),
   2704                     .id_result = result_id,
   2705                     .operand_1 = valid_cmp_id,
   2706                     .operand_2 = pl_cmp_id,
   2707                 };
   2708                 switch (op) {
   2709                     .eq => try self.func.body.emit(self.spv.gpa, .OpLogicalAnd, args),
   2710                     .neq => try self.func.body.emit(self.spv.gpa, .OpLogicalOr, args),
   2711                     else => unreachable,
   2712                 }
   2713                 return result_id;
   2714             },
   2715             .Vector => {
   2716                 const child_ty = ty.childType(mod);
   2717                 const vector_len = ty.vectorLen(mod);
   2718                 const bool_ty_ref_indirect = try self.resolveType(Type.bool, .indirect);
   2719 
   2720                 var constituents = try self.gpa.alloc(IdRef, vector_len);
   2721                 defer self.gpa.free(constituents);
   2722 
   2723                 for (constituents, 0..) |*constituent, i| {
   2724                     const lhs_index_id = try self.extractField(child_ty, cmp_lhs_id, @intCast(i));
   2725                     const rhs_index_id = try self.extractField(child_ty, cmp_rhs_id, @intCast(i));
   2726                     const result_id = try self.cmp(op, child_ty, lhs_index_id, rhs_index_id);
   2727                     constituent.* = try self.convertToIndirect(Type.bool, result_id);
   2728                 }
   2729 
   2730                 const result_ty_ref = try self.spv.arrayType(vector_len, bool_ty_ref_indirect);
   2731                 return try self.constructArray(result_ty_ref, constituents);
   2732             },
   2733             else => unreachable,
   2734         };
   2735 
   2736         const opcode: Opcode = opcode: {
   2737             const info = try self.arithmeticTypeInfo(op_ty);
   2738             const signedness = switch (info.class) {
   2739                 .composite_integer => {
   2740                     return self.todo("binary operations for composite integers", .{});
   2741                 },
   2742                 .float => break :opcode switch (op) {
   2743                     .eq => .OpFOrdEqual,
   2744                     .neq => .OpFUnordNotEqual,
   2745                     .lt => .OpFOrdLessThan,
   2746                     .lte => .OpFOrdLessThanEqual,
   2747                     .gt => .OpFOrdGreaterThan,
   2748                     .gte => .OpFOrdGreaterThanEqual,
   2749                 },
   2750                 .bool => break :opcode switch (op) {
   2751                     .eq => .OpLogicalEqual,
   2752                     .neq => .OpLogicalNotEqual,
   2753                     else => unreachable,
   2754                 },
   2755                 .strange_integer => sign: {
   2756                     const op_ty_ref = try self.resolveType(op_ty, .direct);
   2757                     // Mask operands before performing comparison.
   2758                     cmp_lhs_id = try self.normalizeInt(op_ty_ref, cmp_lhs_id, info);
   2759                     cmp_rhs_id = try self.normalizeInt(op_ty_ref, cmp_rhs_id, info);
   2760                     break :sign info.signedness;
   2761                 },
   2762                 .integer => info.signedness,
   2763             };
   2764 
   2765             break :opcode switch (signedness) {
   2766                 .unsigned => switch (op) {
   2767                     .eq => .OpIEqual,
   2768                     .neq => .OpINotEqual,
   2769                     .lt => .OpULessThan,
   2770                     .lte => .OpULessThanEqual,
   2771                     .gt => .OpUGreaterThan,
   2772                     .gte => .OpUGreaterThanEqual,
   2773                 },
   2774                 .signed => switch (op) {
   2775                     .eq => .OpIEqual,
   2776                     .neq => .OpINotEqual,
   2777                     .lt => .OpSLessThan,
   2778                     .lte => .OpSLessThanEqual,
   2779                     .gt => .OpSGreaterThan,
   2780                     .gte => .OpSGreaterThanEqual,
   2781                 },
   2782             };
   2783         };
   2784 
   2785         const result_id = self.spv.allocId();
   2786         try self.func.body.emitRaw(self.spv.gpa, opcode, 4);
   2787         self.func.body.writeOperand(spec.IdResultType, self.typeId(bool_ty_ref));
   2788         self.func.body.writeOperand(spec.IdResult, result_id);
   2789         self.func.body.writeOperand(spec.IdResultType, cmp_lhs_id);
   2790         self.func.body.writeOperand(spec.IdResultType, cmp_rhs_id);
   2791         return result_id;
   2792     }
   2793 
   2794     fn airCmp(
   2795         self: *DeclGen,
   2796         inst: Air.Inst.Index,
   2797         comptime op: std.math.CompareOperator,
   2798     ) !?IdRef {
   2799         if (self.liveness.isUnused(inst)) return null;
   2800         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   2801         const lhs_id = try self.resolve(bin_op.lhs);
   2802         const rhs_id = try self.resolve(bin_op.rhs);
   2803         const ty = self.typeOf(bin_op.lhs);
   2804 
   2805         return try self.cmp(op, ty, lhs_id, rhs_id);
   2806     }
   2807 
   2808     fn airVectorCmp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2809         if (self.liveness.isUnused(inst)) return null;
   2810 
   2811         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   2812         const vec_cmp = self.air.extraData(Air.VectorCmp, ty_pl.payload).data;
   2813         const lhs_id = try self.resolve(vec_cmp.lhs);
   2814         const rhs_id = try self.resolve(vec_cmp.rhs);
   2815         const op = vec_cmp.compareOperator();
   2816         const ty = self.typeOf(vec_cmp.lhs);
   2817 
   2818         return try self.cmp(op, ty, lhs_id, rhs_id);
   2819     }
   2820 
   2821     fn bitCast(
   2822         self: *DeclGen,
   2823         dst_ty: Type,
   2824         src_ty: Type,
   2825         src_id: IdRef,
   2826     ) !IdRef {
   2827         const mod = self.module;
   2828         const src_ty_ref = try self.resolveType(src_ty, .direct);
   2829         const dst_ty_ref = try self.resolveType(dst_ty, .direct);
   2830         if (src_ty_ref == dst_ty_ref) {
   2831             return src_id;
   2832         }
   2833 
   2834         // TODO: Some more cases are missing here
   2835         //   See fn bitCast in llvm.zig
   2836 
   2837         if (src_ty.zigTypeTag(mod) == .Int and dst_ty.isPtrAtRuntime(mod)) {
   2838             const result_id = self.spv.allocId();
   2839             try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{
   2840                 .id_result_type = self.typeId(dst_ty_ref),
   2841                 .id_result = result_id,
   2842                 .integer_value = src_id,
   2843             });
   2844             return result_id;
   2845         }
   2846 
   2847         // We can only use OpBitcast for specific conversions: between numerical types, and
   2848         // between pointers. If the resolved spir-v types fall into this category then emit OpBitcast,
   2849         // otherwise use a temporary and perform a pointer cast.
   2850         const src_key = self.spv.cache.lookup(src_ty_ref);
   2851         const dst_key = self.spv.cache.lookup(dst_ty_ref);
   2852 
   2853         if ((src_key.isNumericalType() and dst_key.isNumericalType()) or (src_key == .ptr_type and dst_key == .ptr_type)) {
   2854             const result_id = self.spv.allocId();
   2855             try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
   2856                 .id_result_type = self.typeId(dst_ty_ref),
   2857                 .id_result = result_id,
   2858                 .operand = src_id,
   2859             });
   2860             return result_id;
   2861         }
   2862 
   2863         const src_ptr_ty_ref = try self.spv.ptrType(src_ty_ref, .Function);
   2864         const dst_ptr_ty_ref = try self.spv.ptrType(dst_ty_ref, .Function);
   2865 
   2866         const tmp_id = self.spv.allocId();
   2867         try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
   2868             .id_result_type = self.typeId(src_ptr_ty_ref),
   2869             .id_result = tmp_id,
   2870             .storage_class = .Function,
   2871         });
   2872         try self.store(src_ty, tmp_id, src_id, false);
   2873         const casted_ptr_id = self.spv.allocId();
   2874         try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
   2875             .id_result_type = self.typeId(dst_ptr_ty_ref),
   2876             .id_result = casted_ptr_id,
   2877             .operand = tmp_id,
   2878         });
   2879         return try self.load(dst_ty, casted_ptr_id, false);
   2880     }
   2881 
   2882     fn airBitCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2883         if (self.liveness.isUnused(inst)) return null;
   2884         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2885         const operand_id = try self.resolve(ty_op.operand);
   2886         const operand_ty = self.typeOf(ty_op.operand);
   2887         const result_ty = self.typeOfIndex(inst);
   2888         return try self.bitCast(result_ty, operand_ty, operand_id);
   2889     }
   2890 
   2891     fn airIntCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2892         if (self.liveness.isUnused(inst)) return null;
   2893 
   2894         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2895         const operand_id = try self.resolve(ty_op.operand);
   2896         const src_ty = self.typeOf(ty_op.operand);
   2897         const dst_ty = self.typeOfIndex(inst);
   2898         const src_ty_ref = try self.resolveType(src_ty, .direct);
   2899         const dst_ty_ref = try self.resolveType(dst_ty, .direct);
   2900 
   2901         const src_info = try self.arithmeticTypeInfo(src_ty);
   2902         const dst_info = try self.arithmeticTypeInfo(dst_ty);
   2903 
   2904         // While intcast promises that the value already fits, the upper bits of a
   2905         // strange integer may contain garbage. Therefore, mask/sign extend it before.
   2906         const src_id = try self.normalizeInt(src_ty_ref, operand_id, src_info);
   2907 
   2908         if (src_info.backing_bits == dst_info.backing_bits) {
   2909             return src_id;
   2910         }
   2911 
   2912         const result_id = self.spv.allocId();
   2913         switch (dst_info.signedness) {
   2914             .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{
   2915                 .id_result_type = self.typeId(dst_ty_ref),
   2916                 .id_result = result_id,
   2917                 .signed_value = src_id,
   2918             }),
   2919             .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
   2920                 .id_result_type = self.typeId(dst_ty_ref),
   2921                 .id_result = result_id,
   2922                 .unsigned_value = src_id,
   2923             }),
   2924         }
   2925         return result_id;
   2926     }
   2927 
   2928     fn intFromPtr(self: *DeclGen, operand_id: IdRef) !IdRef {
   2929         const result_type_id = try self.resolveTypeId(Type.usize);
   2930         const result_id = self.spv.allocId();
   2931         try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{
   2932             .id_result_type = result_type_id,
   2933             .id_result = result_id,
   2934             .pointer = operand_id,
   2935         });
   2936         return result_id;
   2937     }
   2938 
   2939     fn airIntFromPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2940         if (self.liveness.isUnused(inst)) return null;
   2941 
   2942         const un_op = self.air.instructions.items(.data)[inst].un_op;
   2943         const operand_id = try self.resolve(un_op);
   2944         return try self.intFromPtr(operand_id);
   2945     }
   2946 
   2947     fn airFloatFromInt(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2948         if (self.liveness.isUnused(inst)) return null;
   2949 
   2950         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2951         const operand_ty = self.typeOf(ty_op.operand);
   2952         const operand_id = try self.resolve(ty_op.operand);
   2953         const operand_info = try self.arithmeticTypeInfo(operand_ty);
   2954         const dest_ty = self.typeOfIndex(inst);
   2955         const dest_ty_id = try self.resolveTypeId(dest_ty);
   2956 
   2957         const result_id = self.spv.allocId();
   2958         switch (operand_info.signedness) {
   2959             .signed => try self.func.body.emit(self.spv.gpa, .OpConvertSToF, .{
   2960                 .id_result_type = dest_ty_id,
   2961                 .id_result = result_id,
   2962                 .signed_value = operand_id,
   2963             }),
   2964             .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertUToF, .{
   2965                 .id_result_type = dest_ty_id,
   2966                 .id_result = result_id,
   2967                 .unsigned_value = operand_id,
   2968             }),
   2969         }
   2970         return result_id;
   2971     }
   2972 
   2973     fn airIntFromFloat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2974         if (self.liveness.isUnused(inst)) return null;
   2975 
   2976         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   2977         const operand_id = try self.resolve(ty_op.operand);
   2978         const dest_ty = self.typeOfIndex(inst);
   2979         const dest_info = try self.arithmeticTypeInfo(dest_ty);
   2980         const dest_ty_id = try self.resolveTypeId(dest_ty);
   2981 
   2982         const result_id = self.spv.allocId();
   2983         switch (dest_info.signedness) {
   2984             .signed => try self.func.body.emit(self.spv.gpa, .OpConvertFToS, .{
   2985                 .id_result_type = dest_ty_id,
   2986                 .id_result = result_id,
   2987                 .float_value = operand_id,
   2988             }),
   2989             .unsigned => try self.func.body.emit(self.spv.gpa, .OpConvertFToU, .{
   2990                 .id_result_type = dest_ty_id,
   2991                 .id_result = result_id,
   2992                 .float_value = operand_id,
   2993             }),
   2994         }
   2995         return result_id;
   2996     }
   2997 
   2998     fn airFloatCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   2999         if (self.liveness.isUnused(inst)) return null;
   3000 
   3001         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3002         const operand_id = try self.resolve(ty_op.operand);
   3003         const dest_ty = self.typeOfIndex(inst);
   3004         const dest_ty_id = try self.resolveTypeId(dest_ty);
   3005 
   3006         const result_id = self.spv.allocId();
   3007         try self.func.body.emit(self.spv.gpa, .OpFConvert, .{
   3008             .id_result_type = dest_ty_id,
   3009             .id_result = result_id,
   3010             .float_value = operand_id,
   3011         });
   3012         return result_id;
   3013     }
   3014 
   3015     fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3016         if (self.liveness.isUnused(inst)) return null;
   3017         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3018         const operand_id = try self.resolve(ty_op.operand);
   3019         const result_ty = self.typeOfIndex(inst);
   3020         const result_ty_id = try self.resolveTypeId(result_ty);
   3021         const info = try self.arithmeticTypeInfo(result_ty);
   3022 
   3023         const result_id = self.spv.allocId();
   3024         switch (info.class) {
   3025             .bool => {
   3026                 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{
   3027                     .id_result_type = result_ty_id,
   3028                     .id_result = result_id,
   3029                     .operand = operand_id,
   3030                 });
   3031             },
   3032             .float => unreachable,
   3033             .composite_integer => unreachable, // TODO
   3034             .strange_integer, .integer => {
   3035                 // Note: strange integer bits will be masked before operations that do not hold under modulo.
   3036                 try self.func.body.emit(self.spv.gpa, .OpNot, .{
   3037                     .id_result_type = result_ty_id,
   3038                     .id_result = result_id,
   3039                     .operand = operand_id,
   3040                 });
   3041             },
   3042         }
   3043 
   3044         return result_id;
   3045     }
   3046 
   3047     fn airArrayToSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3048         if (self.liveness.isUnused(inst)) return null;
   3049 
   3050         const mod = self.module;
   3051         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3052         const array_ptr_ty = self.typeOf(ty_op.operand);
   3053         const array_ty = array_ptr_ty.childType(mod);
   3054         const slice_ty = self.typeOfIndex(inst);
   3055         const elem_ptr_ty = slice_ty.slicePtrFieldType(mod);
   3056 
   3057         const elem_ptr_ty_ref = try self.resolveType(elem_ptr_ty, .direct);
   3058         const slice_ty_ref = try self.resolveType(slice_ty, .direct);
   3059         const size_ty_ref = try self.sizeType();
   3060 
   3061         const array_ptr_id = try self.resolve(ty_op.operand);
   3062         const len_id = try self.constInt(size_ty_ref, array_ty.arrayLen(mod));
   3063 
   3064         const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(mod))
   3065             // Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type.
   3066             try self.bitCast(elem_ptr_ty, array_ptr_ty, array_ptr_id)
   3067         else
   3068             // Convert the pointer-to-array to a pointer to the first element.
   3069             try self.accessChain(elem_ptr_ty_ref, array_ptr_id, &.{0});
   3070 
   3071         return try self.constructStruct(slice_ty_ref, &.{ elem_ptr_id, len_id });
   3072     }
   3073 
   3074     fn airSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3075         if (self.liveness.isUnused(inst)) return null;
   3076 
   3077         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3078         const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   3079         const ptr_id = try self.resolve(bin_op.lhs);
   3080         const len_id = try self.resolve(bin_op.rhs);
   3081         const slice_ty = self.typeOfIndex(inst);
   3082         const slice_ty_ref = try self.resolveType(slice_ty, .direct);
   3083 
   3084         return try self.constructStruct(slice_ty_ref, &.{
   3085             ptr_id, // Note: Type should not need to be converted to direct.
   3086             len_id, // Note: Type should not need to be converted to direct.
   3087         });
   3088     }
   3089 
   3090     fn airAggregateInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3091         if (self.liveness.isUnused(inst)) return null;
   3092 
   3093         const mod = self.module;
   3094         const ip = &mod.intern_pool;
   3095         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3096         const result_ty = self.typeOfIndex(inst);
   3097         const result_ty_ref = try self.resolveType(result_ty, .direct);
   3098         const len: usize = @intCast(result_ty.arrayLen(mod));
   3099         const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]);
   3100 
   3101         switch (result_ty.zigTypeTag(mod)) {
   3102             .Vector => unreachable, // TODO
   3103             .Struct => {
   3104                 if (mod.typeToPackedStruct(result_ty)) |struct_type| {
   3105                     _ = struct_type;
   3106                     unreachable; // TODO
   3107                 }
   3108 
   3109                 const constituents = try self.gpa.alloc(IdRef, elements.len);
   3110                 defer self.gpa.free(constituents);
   3111                 var index: usize = 0;
   3112 
   3113                 switch (ip.indexToKey(result_ty.toIntern())) {
   3114                     .anon_struct_type => |tuple| {
   3115                         for (tuple.types.get(ip), elements, 0..) |field_ty, element, i| {
   3116                             if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue;
   3117                             assert(field_ty.toType().hasRuntimeBits(mod));
   3118 
   3119                             const id = try self.resolve(element);
   3120                             constituents[index] = try self.convertToIndirect(field_ty.toType(), id);
   3121                             index += 1;
   3122                         }
   3123                     },
   3124                     .struct_type => |struct_type| {
   3125                         var it = struct_type.iterateRuntimeOrder(ip);
   3126                         for (elements, 0..) |element, i| {
   3127                             const field_index = it.next().?;
   3128                             if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue;
   3129                             const field_ty = struct_type.field_types.get(ip)[field_index].toType();
   3130                             assert(field_ty.hasRuntimeBitsIgnoreComptime(mod));
   3131 
   3132                             const id = try self.resolve(element);
   3133                             constituents[index] = try self.convertToIndirect(field_ty, id);
   3134                             index += 1;
   3135                         }
   3136                     },
   3137                     else => unreachable,
   3138                 }
   3139 
   3140                 return try self.constructStruct(result_ty_ref, constituents[0..index]);
   3141             },
   3142             .Array => {
   3143                 const array_info = result_ty.arrayInfo(mod);
   3144                 const n_elems: usize = @intCast(result_ty.arrayLenIncludingSentinel(mod));
   3145                 const elem_ids = try self.gpa.alloc(IdRef, n_elems);
   3146                 defer self.gpa.free(elem_ids);
   3147 
   3148                 for (elements, 0..) |element, i| {
   3149                     const id = try self.resolve(element);
   3150                     elem_ids[i] = try self.convertToIndirect(array_info.elem_type, id);
   3151                 }
   3152 
   3153                 if (array_info.sentinel) |sentinel_val| {
   3154                     elem_ids[n_elems - 1] = try self.constant(array_info.elem_type, sentinel_val, .indirect);
   3155                 }
   3156 
   3157                 return try self.constructArray(result_ty_ref, elem_ids);
   3158             },
   3159             else => unreachable,
   3160         }
   3161     }
   3162 
   3163     fn sliceOrArrayLen(self: *DeclGen, operand_id: IdRef, ty: Type) !IdRef {
   3164         const mod = self.module;
   3165         switch (ty.ptrSize(mod)) {
   3166             .Slice => return self.extractField(Type.usize, operand_id, 1),
   3167             .One => {
   3168                 const array_ty = ty.childType(mod);
   3169                 const elem_ty = array_ty.childType(mod);
   3170                 const abi_size = elem_ty.abiSize(mod);
   3171                 const usize_ty_ref = try self.resolveType(Type.usize, .direct);
   3172                 return self.spv.constInt(usize_ty_ref, array_ty.arrayLenIncludingSentinel(mod) * abi_size);
   3173             },
   3174             .Many, .C => unreachable,
   3175         }
   3176     }
   3177 
   3178     fn sliceOrArrayPtr(self: *DeclGen, operand_id: IdRef, ty: Type) !IdRef {
   3179         const mod = self.module;
   3180         if (ty.isSlice(mod)) {
   3181             const ptr_ty = ty.slicePtrFieldType(mod);
   3182             return self.extractField(ptr_ty, operand_id, 0);
   3183         }
   3184         return operand_id;
   3185     }
   3186 
   3187     fn airMemcpy(self: *DeclGen, inst: Air.Inst.Index) !void {
   3188         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   3189         const dest_slice = try self.resolve(bin_op.lhs);
   3190         const src_slice = try self.resolve(bin_op.rhs);
   3191         const dest_ty = self.typeOf(bin_op.lhs);
   3192         const src_ty = self.typeOf(bin_op.rhs);
   3193         const dest_ptr = try self.sliceOrArrayPtr(dest_slice, dest_ty);
   3194         const src_ptr = try self.sliceOrArrayPtr(src_slice, src_ty);
   3195         const len = try self.sliceOrArrayLen(dest_slice, dest_ty);
   3196         try self.func.body.emit(self.spv.gpa, .OpCopyMemorySized, .{
   3197             .target = dest_ptr,
   3198             .source = src_ptr,
   3199             .size = len,
   3200         });
   3201     }
   3202 
   3203     fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef {
   3204         if (self.liveness.isUnused(inst)) return null;
   3205         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3206         const field_ty = self.typeOfIndex(inst);
   3207         const operand_id = try self.resolve(ty_op.operand);
   3208         return try self.extractField(field_ty, operand_id, field);
   3209     }
   3210 
   3211     fn airSliceElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3212         const mod = self.module;
   3213         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3214         const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   3215         const slice_ty = self.typeOf(bin_op.lhs);
   3216         if (!slice_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null;
   3217 
   3218         const slice_id = try self.resolve(bin_op.lhs);
   3219         const index_id = try self.resolve(bin_op.rhs);
   3220 
   3221         const ptr_ty = self.typeOfIndex(inst);
   3222         const ptr_ty_ref = try self.resolveType(ptr_ty, .direct);
   3223 
   3224         const slice_ptr = try self.extractField(ptr_ty, slice_id, 0);
   3225         return try self.ptrAccessChain(ptr_ty_ref, slice_ptr, index_id, &.{});
   3226     }
   3227 
   3228     fn airSliceElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3229         const mod = self.module;
   3230         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   3231         const slice_ty = self.typeOf(bin_op.lhs);
   3232         if (!slice_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null;
   3233 
   3234         const slice_id = try self.resolve(bin_op.lhs);
   3235         const index_id = try self.resolve(bin_op.rhs);
   3236 
   3237         const ptr_ty = slice_ty.slicePtrFieldType(mod);
   3238         const ptr_ty_ref = try self.resolveType(ptr_ty, .direct);
   3239 
   3240         const slice_ptr = try self.extractField(ptr_ty, slice_id, 0);
   3241         const elem_ptr = try self.ptrAccessChain(ptr_ty_ref, slice_ptr, index_id, &.{});
   3242         return try self.load(slice_ty.childType(mod), elem_ptr, slice_ty.isVolatilePtr(mod));
   3243     }
   3244 
   3245     fn ptrElemPtr(self: *DeclGen, ptr_ty: Type, ptr_id: IdRef, index_id: IdRef) !IdRef {
   3246         const mod = self.module;
   3247         // Construct new pointer type for the resulting pointer
   3248         const elem_ty = ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T.
   3249         const elem_ty_ref = try self.resolveType(elem_ty, .direct);
   3250         const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, spvStorageClass(ptr_ty.ptrAddressSpace(mod)));
   3251         if (ptr_ty.isSinglePointer(mod)) {
   3252             // Pointer-to-array. In this case, the resulting pointer is not of the same type
   3253             // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.
   3254             return try self.accessChainId(elem_ptr_ty_ref, ptr_id, &.{index_id});
   3255         } else {
   3256             // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain
   3257             return try self.ptrAccessChain(elem_ptr_ty_ref, ptr_id, index_id, &.{});
   3258         }
   3259     }
   3260 
   3261     fn airPtrElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3262         if (self.liveness.isUnused(inst)) return null;
   3263 
   3264         const mod = self.module;
   3265         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3266         const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
   3267         const src_ptr_ty = self.typeOf(bin_op.lhs);
   3268         const elem_ty = src_ptr_ty.childType(mod);
   3269         const ptr_id = try self.resolve(bin_op.lhs);
   3270 
   3271         if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) {
   3272             const dst_ptr_ty = self.typeOfIndex(inst);
   3273             return try self.bitCast(dst_ptr_ty, src_ptr_ty, ptr_id);
   3274         }
   3275 
   3276         const index_id = try self.resolve(bin_op.rhs);
   3277         return try self.ptrElemPtr(src_ptr_ty, ptr_id, index_id);
   3278     }
   3279 
   3280     fn airArrayElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3281         if (self.liveness.isUnused(inst)) return null;
   3282 
   3283         const mod = self.module;
   3284         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   3285         const array_ty = self.typeOf(bin_op.lhs);
   3286         const array_ty_ref = try self.resolveType(array_ty, .direct);
   3287         const elem_ty = array_ty.childType(mod);
   3288         const elem_ty_ref = try self.resolveType(elem_ty, .indirect);
   3289         const array_id = try self.resolve(bin_op.lhs);
   3290         const index_id = try self.resolve(bin_op.rhs);
   3291 
   3292         // SPIR-V doesn't have an array indexing function for some damn reason.
   3293         // For now, just generate a temporary and use that.
   3294         // TODO: This backend probably also should use isByRef from llvm...
   3295 
   3296         const array_ptr_ty_ref = try self.spv.ptrType(array_ty_ref, .Function);
   3297         const elem_ptr_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function);
   3298 
   3299         const tmp_id = self.spv.allocId();
   3300         try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
   3301             .id_result_type = self.typeId(array_ptr_ty_ref),
   3302             .id_result = tmp_id,
   3303             .storage_class = .Function,
   3304         });
   3305         try self.func.body.emit(self.spv.gpa, .OpStore, .{
   3306             .pointer = tmp_id,
   3307             .object = array_id,
   3308         });
   3309 
   3310         const elem_ptr_id = try self.accessChainId(elem_ptr_ty_ref, tmp_id, &.{index_id});
   3311         return try self.load(elem_ty, elem_ptr_id, false);
   3312     }
   3313 
   3314     fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3315         if (self.liveness.isUnused(inst)) return null;
   3316 
   3317         const mod = self.module;
   3318         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   3319         const ptr_ty = self.typeOf(bin_op.lhs);
   3320         const elem_ty = self.typeOfIndex(inst);
   3321         const ptr_id = try self.resolve(bin_op.lhs);
   3322         const index_id = try self.resolve(bin_op.rhs);
   3323         const elem_ptr_id = try self.ptrElemPtr(ptr_ty, ptr_id, index_id);
   3324         return try self.load(elem_ty, elem_ptr_id, ptr_ty.isVolatilePtr(mod));
   3325     }
   3326 
   3327     fn airSetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !void {
   3328         const mod = self.module;
   3329         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   3330         const un_ptr_ty = self.typeOf(bin_op.lhs);
   3331         const un_ty = un_ptr_ty.childType(mod);
   3332         const layout = self.unionLayout(un_ty, null);
   3333 
   3334         if (layout.tag_size == 0) return;
   3335 
   3336         const tag_ty = un_ty.unionTagTypeSafety(mod).?;
   3337         const tag_ty_ref = try self.resolveType(tag_ty, .indirect);
   3338         const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, spvStorageClass(un_ptr_ty.ptrAddressSpace(mod)));
   3339 
   3340         const union_ptr_id = try self.resolve(bin_op.lhs);
   3341         const new_tag_id = try self.resolve(bin_op.rhs);
   3342 
   3343         if (layout.payload_size == 0) {
   3344             try self.store(tag_ty, union_ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod));
   3345         } else {
   3346             const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index});
   3347             try self.store(tag_ty, ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod));
   3348         }
   3349     }
   3350 
   3351     fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3352         if (self.liveness.isUnused(inst)) return null;
   3353 
   3354         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3355         const un_ty = self.typeOf(ty_op.operand);
   3356 
   3357         const mod = self.module;
   3358         const layout = self.unionLayout(un_ty, null);
   3359         if (layout.tag_size == 0) return null;
   3360 
   3361         const union_handle = try self.resolve(ty_op.operand);
   3362         if (layout.payload_size == 0) return union_handle;
   3363 
   3364         const tag_ty = un_ty.unionTagTypeSafety(mod).?;
   3365         return try self.extractField(tag_ty, union_handle, layout.tag_index);
   3366     }
   3367 
   3368     fn unionInit(
   3369         self: *DeclGen,
   3370         ty: Type,
   3371         active_field: u32,
   3372         payload: ?IdRef,
   3373     ) !IdRef {
   3374         // To initialize a union, generate a temporary variable with the
   3375         // type that has the right field active, then pointer-cast and store
   3376         // the active field, and finally load and return the entire union.
   3377 
   3378         const mod = self.module;
   3379         const ip = &mod.intern_pool;
   3380         const union_ty = mod.typeToUnion(ty).?;
   3381 
   3382         if (union_ty.getLayout(ip) == .Packed) {
   3383             unreachable; // TODO
   3384         }
   3385 
   3386         const maybe_tag_ty = ty.unionTagTypeSafety(mod);
   3387         const layout = self.unionLayout(ty, active_field);
   3388 
   3389         const tag_int = if (layout.tag_size != 0) blk: {
   3390             const tag_ty = maybe_tag_ty.?;
   3391             const union_field_name = union_ty.field_names.get(ip)[active_field];
   3392             const enum_field_index = tag_ty.enumFieldIndex(union_field_name, mod).?;
   3393             const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index);
   3394             const tag_int_val = try tag_val.intFromEnum(tag_ty, mod);
   3395             break :blk tag_int_val.toUnsignedInt(mod);
   3396         } else 0;
   3397 
   3398         if (layout.payload_size == 0) {
   3399             const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct);
   3400             return try self.constInt(tag_ty_ref, tag_int);
   3401         }
   3402 
   3403         const un_active_ty_ref = try self.resolveUnionType(ty, active_field);
   3404         const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function);
   3405         const un_general_ty_ref = try self.resolveType(ty, .direct);
   3406         const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function);
   3407 
   3408         const tmp_id = self.spv.allocId();
   3409         try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
   3410             .id_result_type = self.typeId(un_active_ptr_ty_ref),
   3411             .id_result = tmp_id,
   3412             .storage_class = .Function,
   3413         });
   3414 
   3415         if (layout.tag_size != 0) {
   3416             const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct);
   3417             const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function);
   3418             const ptr_id = try self.accessChain(tag_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.tag_index))});
   3419             const tag_id = try self.constInt(tag_ty_ref, tag_int);
   3420             try self.func.body.emit(self.spv.gpa, .OpStore, .{
   3421                 .pointer = ptr_id,
   3422                 .object = tag_id,
   3423             });
   3424         }
   3425 
   3426         if (layout.active_field_size != 0) {
   3427             const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect);
   3428             const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function);
   3429             const ptr_id = try self.accessChain(active_field_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.active_field_index))});
   3430             try self.func.body.emit(self.spv.gpa, .OpStore, .{
   3431                 .pointer = ptr_id,
   3432                 .object = payload.?,
   3433             });
   3434         } else {
   3435             assert(payload == null);
   3436         }
   3437 
   3438         // Just leave the padding fields uninitialized...
   3439         // TODO: Or should we initialize them with undef explicitly?
   3440 
   3441         // Now cast the pointer and load it as the 'generic' union type.
   3442 
   3443         const casted_var_id = self.spv.allocId();
   3444         try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
   3445             .id_result_type = self.typeId(un_general_ptr_ty_ref),
   3446             .id_result = casted_var_id,
   3447             .operand = tmp_id,
   3448         });
   3449 
   3450         const result_id = self.spv.allocId();
   3451         try self.func.body.emit(self.spv.gpa, .OpLoad, .{
   3452             .id_result_type = self.typeId(un_general_ty_ref),
   3453             .id_result = result_id,
   3454             .pointer = casted_var_id,
   3455         });
   3456 
   3457         return result_id;
   3458     }
   3459 
   3460     fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3461         if (self.liveness.isUnused(inst)) return null;
   3462 
   3463         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3464         const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
   3465         const ty = self.typeOfIndex(inst);
   3466         const layout = self.unionLayout(ty, extra.field_index);
   3467 
   3468         const payload = if (layout.active_field_size != 0)
   3469             try self.resolve(extra.init)
   3470         else
   3471             null;
   3472         return try self.unionInit(ty, extra.field_index, payload);
   3473     }
   3474 
   3475     fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3476         if (self.liveness.isUnused(inst)) return null;
   3477 
   3478         const mod = self.module;
   3479         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3480         const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
   3481 
   3482         const object_ty = self.typeOf(struct_field.struct_operand);
   3483         const object_id = try self.resolve(struct_field.struct_operand);
   3484         const field_index = struct_field.field_index;
   3485         const field_ty = object_ty.structFieldType(field_index, mod);
   3486 
   3487         if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) return null;
   3488 
   3489         switch (object_ty.zigTypeTag(mod)) {
   3490             .Struct => switch (object_ty.containerLayout(mod)) {
   3491                 .Packed => unreachable, // TODO
   3492                 else => return try self.extractField(field_ty, object_id, field_index),
   3493             },
   3494             .Union => switch (object_ty.containerLayout(mod)) {
   3495                 .Packed => unreachable, // TODO
   3496                 else => {
   3497                     // Store, pointer-cast, load
   3498                     const un_general_ty_ref = try self.resolveType(object_ty, .indirect);
   3499                     const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function);
   3500                     const un_active_ty_ref = try self.resolveUnionType(object_ty, field_index);
   3501                     const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function);
   3502                     const field_ty_ref = try self.resolveType(field_ty, .indirect);
   3503                     const field_ptr_ty_ref = try self.spv.ptrType(field_ty_ref, .Function);
   3504 
   3505                     const tmp_id = self.spv.allocId();
   3506                     try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
   3507                         .id_result_type = self.typeId(un_general_ptr_ty_ref),
   3508                         .id_result = tmp_id,
   3509                         .storage_class = .Function,
   3510                     });
   3511                     try self.store(object_ty, tmp_id, object_id, false);
   3512                     const casted_tmp_id = self.spv.allocId();
   3513                     try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
   3514                         .id_result_type = self.typeId(un_active_ptr_ty_ref),
   3515                         .id_result = casted_tmp_id,
   3516                         .operand = tmp_id,
   3517                     });
   3518                     const layout = self.unionLayout(object_ty, field_index);
   3519                     const field_ptr_id = try self.accessChain(field_ptr_ty_ref, casted_tmp_id, &.{layout.active_field_index});
   3520                     return try self.load(field_ty, field_ptr_id, false);
   3521                 },
   3522             },
   3523             else => unreachable,
   3524         }
   3525     }
   3526 
   3527     fn airFieldParentPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3528         const mod = self.module;
   3529         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3530         const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
   3531 
   3532         const parent_ty = self.air.getRefType(ty_pl.ty).childType(mod);
   3533         const res_ty = try self.resolveType(self.air.getRefType(ty_pl.ty), .indirect);
   3534         const usize_ty = Type.usize;
   3535         const usize_ty_ref = try self.resolveType(usize_ty, .direct);
   3536 
   3537         const field_ptr = try self.resolve(extra.field_ptr);
   3538         const field_ptr_int = try self.intFromPtr(field_ptr);
   3539         const field_offset = parent_ty.structFieldOffset(extra.field_index, mod);
   3540 
   3541         const base_ptr_int = base_ptr_int: {
   3542             if (field_offset == 0) break :base_ptr_int field_ptr_int;
   3543 
   3544             const field_offset_id = try self.constInt(usize_ty_ref, field_offset);
   3545             break :base_ptr_int try self.binOpSimple(usize_ty, field_ptr_int, field_offset_id, .OpISub);
   3546         };
   3547 
   3548         const base_ptr = self.spv.allocId();
   3549         try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{
   3550             .id_result_type = self.spv.resultId(res_ty),
   3551             .id_result = base_ptr,
   3552             .integer_value = base_ptr_int,
   3553         });
   3554 
   3555         return base_ptr;
   3556     }
   3557 
   3558     fn structFieldPtr(
   3559         self: *DeclGen,
   3560         result_ptr_ty: Type,
   3561         object_ptr_ty: Type,
   3562         object_ptr: IdRef,
   3563         field_index: u32,
   3564     ) !IdRef {
   3565         const result_ty_ref = try self.resolveType(result_ptr_ty, .direct);
   3566 
   3567         const mod = self.module;
   3568         const object_ty = object_ptr_ty.childType(mod);
   3569         switch (object_ty.zigTypeTag(mod)) {
   3570             .Struct => switch (object_ty.containerLayout(mod)) {
   3571                 .Packed => unreachable, // TODO
   3572                 else => {
   3573                     return try self.accessChain(result_ty_ref, object_ptr, &.{field_index});
   3574                 },
   3575             },
   3576             .Union => switch (object_ty.containerLayout(mod)) {
   3577                 .Packed => unreachable, // TODO
   3578                 else => {
   3579                     const storage_class = spvStorageClass(object_ptr_ty.ptrAddressSpace(mod));
   3580                     const un_active_ty_ref = try self.resolveUnionType(object_ty, field_index);
   3581                     const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, storage_class);
   3582 
   3583                     const casted_id = self.spv.allocId();
   3584                     try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
   3585                         .id_result_type = self.typeId(un_active_ptr_ty_ref),
   3586                         .id_result = casted_id,
   3587                         .operand = object_ptr,
   3588                     });
   3589                     const layout = self.unionLayout(object_ty, field_index);
   3590                     return try self.accessChain(result_ty_ref, casted_id, &.{layout.active_field_index});
   3591                 },
   3592             },
   3593             else => unreachable,
   3594         }
   3595     }
   3596 
   3597     fn airStructFieldPtrIndex(self: *DeclGen, inst: Air.Inst.Index, field_index: u32) !?IdRef {
   3598         if (self.liveness.isUnused(inst)) return null;
   3599         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3600         const struct_ptr = try self.resolve(ty_op.operand);
   3601         const struct_ptr_ty = self.typeOf(ty_op.operand);
   3602         const result_ptr_ty = self.typeOfIndex(inst);
   3603         return try self.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index);
   3604     }
   3605 
   3606     /// We cannot use an OpVariable directly in an OpSpecConstantOp, but we can
   3607     /// after we insert a dummy AccessChain...
   3608     /// TODO: Get rid of this
   3609     fn makePointerConstant(
   3610         self: *DeclGen,
   3611         section: *SpvSection,
   3612         ptr_ty_ref: CacheRef,
   3613         ptr_id: IdRef,
   3614     ) !IdRef {
   3615         const result_id = self.spv.allocId();
   3616         try section.emitSpecConstantOp(self.spv.gpa, .OpInBoundsAccessChain, .{
   3617             .id_result_type = self.typeId(ptr_ty_ref),
   3618             .id_result = result_id,
   3619             .base = ptr_id,
   3620         });
   3621         return result_id;
   3622     }
   3623 
   3624     // Allocate a function-local variable, with possible initializer.
   3625     // This function returns a pointer to a variable of type `ty_ref`,
   3626     // which is in the Generic address space. The variable is actually
   3627     // placed in the Function address space.
   3628     fn alloc(
   3629         self: *DeclGen,
   3630         ty_ref: CacheRef,
   3631         initializer: ?IdRef,
   3632     ) !IdRef {
   3633         const fn_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Function);
   3634         const general_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic);
   3635 
   3636         // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to
   3637         // directly generate them into func.prologue instead of the body.
   3638         const var_id = self.spv.allocId();
   3639         try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
   3640             .id_result_type = self.typeId(fn_ptr_ty_ref),
   3641             .id_result = var_id,
   3642             .storage_class = .Function,
   3643             .initializer = initializer,
   3644         });
   3645 
   3646         // Convert to a generic pointer
   3647         const result_id = self.spv.allocId();
   3648         try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
   3649             .id_result_type = self.typeId(general_ptr_ty_ref),
   3650             .id_result = result_id,
   3651             .pointer = var_id,
   3652         });
   3653         return result_id;
   3654     }
   3655 
   3656     fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3657         if (self.liveness.isUnused(inst)) return null;
   3658         const mod = self.module;
   3659         const ptr_ty = self.typeOfIndex(inst);
   3660         assert(ptr_ty.ptrAddressSpace(mod) == .generic);
   3661         const child_ty = ptr_ty.childType(mod);
   3662         const child_ty_ref = try self.resolveType(child_ty, .indirect);
   3663         return try self.alloc(child_ty_ref, null);
   3664     }
   3665 
   3666     fn airArg(self: *DeclGen) IdRef {
   3667         defer self.next_arg_index += 1;
   3668         return self.args.items[self.next_arg_index];
   3669     }
   3670 
   3671     fn airBlock(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3672         // In AIR, a block doesn't really define an entry point like a block, but
   3673         // more like a scope that breaks can jump out of and "return" a value from.
   3674         // This cannot be directly modelled in SPIR-V, so in a block instruction,
   3675         // we're going to split up the current block by first generating the code
   3676         // of the block, then a label, and then generate the rest of the current
   3677         // ir.Block in a different SPIR-V block.
   3678 
   3679         const mod = self.module;
   3680         const ty = self.typeOfIndex(inst);
   3681         const inst_datas = self.air.instructions.items(.data);
   3682         const extra = self.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
   3683         const body = self.air.extra[extra.end..][0..extra.data.body_len];
   3684         const have_block_result = ty.isFnOrHasRuntimeBitsIgnoreComptime(mod);
   3685 
   3686         // 4 chosen as arbitrary initial capacity.
   3687         var block = Block{
   3688             // Label id is lazily allocated if needed.
   3689             .label_id = null,
   3690             .incoming_blocks = try std.ArrayListUnmanaged(IncomingBlock).initCapacity(self.gpa, 4),
   3691         };
   3692         defer block.incoming_blocks.deinit(self.gpa);
   3693 
   3694         try self.blocks.putNoClobber(self.gpa, inst, &block);
   3695         defer assert(self.blocks.remove(inst));
   3696 
   3697         try self.genBody(body);
   3698 
   3699         // Only begin a new block if there were actually any breaks towards it.
   3700         if (block.label_id) |label_id| {
   3701             try self.beginSpvBlock(label_id);
   3702         }
   3703 
   3704         if (!have_block_result)
   3705             return null;
   3706 
   3707         assert(block.label_id != null);
   3708         const result_id = self.spv.allocId();
   3709         const result_type_id = try self.resolveTypeId(ty);
   3710 
   3711         try self.func.body.emitRaw(self.spv.gpa, .OpPhi, 2 + @as(u16, @intCast(block.incoming_blocks.items.len * 2))); // result type + result + variable/parent...
   3712         self.func.body.writeOperand(spec.IdResultType, result_type_id);
   3713         self.func.body.writeOperand(spec.IdRef, result_id);
   3714 
   3715         for (block.incoming_blocks.items) |incoming| {
   3716             self.func.body.writeOperand(spec.PairIdRefIdRef, .{ incoming.break_value_id, incoming.src_label_id });
   3717         }
   3718 
   3719         return result_id;
   3720     }
   3721 
   3722     fn airBr(self: *DeclGen, inst: Air.Inst.Index) !void {
   3723         const br = self.air.instructions.items(.data)[inst].br;
   3724         const operand_ty = self.typeOf(br.operand);
   3725         const block = self.blocks.get(br.block_inst).?;
   3726 
   3727         const mod = self.module;
   3728         if (operand_ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) {
   3729             const operand_id = try self.resolve(br.operand);
   3730             // current_block_label_id should not be undefined here, lest there is a br or br_void in the function's body.
   3731             try block.incoming_blocks.append(self.gpa, .{
   3732                 .src_label_id = self.current_block_label_id,
   3733                 .break_value_id = operand_id,
   3734             });
   3735         }
   3736 
   3737         if (block.label_id == null) {
   3738             block.label_id = self.spv.allocId();
   3739         }
   3740 
   3741         try self.func.body.emit(self.spv.gpa, .OpBranch, .{ .target_label = block.label_id.? });
   3742     }
   3743 
   3744     fn airCondBr(self: *DeclGen, inst: Air.Inst.Index) !void {
   3745         const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   3746         const cond_br = self.air.extraData(Air.CondBr, pl_op.payload);
   3747         const then_body = self.air.extra[cond_br.end..][0..cond_br.data.then_body_len];
   3748         const else_body = self.air.extra[cond_br.end + then_body.len ..][0..cond_br.data.else_body_len];
   3749         const condition_id = try self.resolve(pl_op.operand);
   3750 
   3751         // These will always generate a new SPIR-V block, since they are ir.Body and not ir.Block.
   3752         const then_label_id = self.spv.allocId();
   3753         const else_label_id = self.spv.allocId();
   3754 
   3755         // TODO: We can generate OpSelectionMerge here if we know the target block that both of these will resolve to,
   3756         // but i don't know if those will always resolve to the same block.
   3757 
   3758         try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{
   3759             .condition = condition_id,
   3760             .true_label = then_label_id,
   3761             .false_label = else_label_id,
   3762         });
   3763 
   3764         try self.beginSpvBlock(then_label_id);
   3765         try self.genBody(then_body);
   3766         try self.beginSpvBlock(else_label_id);
   3767         try self.genBody(else_body);
   3768     }
   3769 
   3770     fn airLoad(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3771         const mod = self.module;
   3772         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3773         const ptr_ty = self.typeOf(ty_op.operand);
   3774         const elem_ty = self.typeOfIndex(inst);
   3775         const operand = try self.resolve(ty_op.operand);
   3776         if (!ptr_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) return null;
   3777 
   3778         return try self.load(elem_ty, operand, ptr_ty.isVolatilePtr(mod));
   3779     }
   3780 
   3781     fn airStore(self: *DeclGen, inst: Air.Inst.Index) !void {
   3782         const bin_op = self.air.instructions.items(.data)[inst].bin_op;
   3783         const ptr_ty = self.typeOf(bin_op.lhs);
   3784         const elem_ty = ptr_ty.childType(self.module);
   3785         const ptr = try self.resolve(bin_op.lhs);
   3786         const value = try self.resolve(bin_op.rhs);
   3787 
   3788         try self.store(elem_ty, ptr, value, ptr_ty.isVolatilePtr(self.module));
   3789     }
   3790 
   3791     fn airLoop(self: *DeclGen, inst: Air.Inst.Index) !void {
   3792         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   3793         const loop = self.air.extraData(Air.Block, ty_pl.payload);
   3794         const body = self.air.extra[loop.end..][0..loop.data.body_len];
   3795         const loop_label_id = self.spv.allocId();
   3796 
   3797         // Jump to the loop entry point
   3798         try self.func.body.emit(self.spv.gpa, .OpBranch, .{ .target_label = loop_label_id });
   3799 
   3800         // TODO: Look into OpLoopMerge.
   3801         try self.beginSpvBlock(loop_label_id);
   3802         try self.genBody(body);
   3803 
   3804         try self.func.body.emit(self.spv.gpa, .OpBranch, .{ .target_label = loop_label_id });
   3805     }
   3806 
   3807     fn airRet(self: *DeclGen, inst: Air.Inst.Index) !void {
   3808         const operand = self.air.instructions.items(.data)[inst].un_op;
   3809         const ret_ty = self.typeOf(operand);
   3810         const mod = self.module;
   3811         if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {
   3812             const decl = mod.declPtr(self.decl_index);
   3813             const fn_info = mod.typeToFunc(decl.ty).?;
   3814             if (fn_info.return_type.toType().isError(mod)) {
   3815                 // Functions with an empty error set are emitted with an error code
   3816                 // return type and return zero so they can be function pointers coerced
   3817                 // to functions that return anyerror.
   3818                 const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
   3819                 const no_err_id = try self.constInt(err_ty_ref, 0);
   3820                 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id });
   3821             } else {
   3822                 return try self.func.body.emit(self.spv.gpa, .OpReturn, {});
   3823             }
   3824         }
   3825 
   3826         const operand_id = try self.resolve(operand);
   3827         try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = operand_id });
   3828     }
   3829 
   3830     fn airRetLoad(self: *DeclGen, inst: Air.Inst.Index) !void {
   3831         const mod = self.module;
   3832         const un_op = self.air.instructions.items(.data)[inst].un_op;
   3833         const ptr_ty = self.typeOf(un_op);
   3834         const ret_ty = ptr_ty.childType(mod);
   3835 
   3836         if (!ret_ty.hasRuntimeBitsIgnoreComptime(mod)) {
   3837             const decl = mod.declPtr(self.decl_index);
   3838             const fn_info = mod.typeToFunc(decl.ty).?;
   3839             if (fn_info.return_type.toType().isError(mod)) {
   3840                 // Functions with an empty error set are emitted with an error code
   3841                 // return type and return zero so they can be function pointers coerced
   3842                 // to functions that return anyerror.
   3843                 const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
   3844                 const no_err_id = try self.constInt(err_ty_ref, 0);
   3845                 return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id });
   3846             } else {
   3847                 return try self.func.body.emit(self.spv.gpa, .OpReturn, {});
   3848             }
   3849         }
   3850 
   3851         const ptr = try self.resolve(un_op);
   3852         const value = try self.load(ret_ty, ptr, ptr_ty.isVolatilePtr(mod));
   3853         try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{
   3854             .value = value,
   3855         });
   3856     }
   3857 
   3858     fn airTry(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3859         const mod = self.module;
   3860         const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   3861         const err_union_id = try self.resolve(pl_op.operand);
   3862         const extra = self.air.extraData(Air.Try, pl_op.payload);
   3863         const body = self.air.extra[extra.end..][0..extra.data.body_len];
   3864 
   3865         const err_union_ty = self.typeOf(pl_op.operand);
   3866         const payload_ty = self.typeOfIndex(inst);
   3867 
   3868         const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
   3869         const bool_ty_ref = try self.resolveType(Type.bool, .direct);
   3870 
   3871         const eu_layout = self.errorUnionLayout(payload_ty);
   3872 
   3873         if (!err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) {
   3874             const err_id = if (eu_layout.payload_has_bits)
   3875                 try self.extractField(Type.anyerror, err_union_id, eu_layout.errorFieldIndex())
   3876             else
   3877                 err_union_id;
   3878 
   3879             const zero_id = try self.constInt(err_ty_ref, 0);
   3880             const is_err_id = self.spv.allocId();
   3881             try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
   3882                 .id_result_type = self.typeId(bool_ty_ref),
   3883                 .id_result = is_err_id,
   3884                 .operand_1 = err_id,
   3885                 .operand_2 = zero_id,
   3886             });
   3887 
   3888             // When there is an error, we must evaluate `body`. Otherwise we must continue
   3889             // with the current body.
   3890             // Just generate a new block here, then generate a new block inline for the remainder of the body.
   3891 
   3892             const err_block = self.spv.allocId();
   3893             const ok_block = self.spv.allocId();
   3894 
   3895             // TODO: Merge block
   3896             try self.func.body.emit(self.spv.gpa, .OpBranchConditional, .{
   3897                 .condition = is_err_id,
   3898                 .true_label = err_block,
   3899                 .false_label = ok_block,
   3900             });
   3901 
   3902             try self.beginSpvBlock(err_block);
   3903             try self.genBody(body);
   3904 
   3905             try self.beginSpvBlock(ok_block);
   3906             // Now just extract the payload, if required.
   3907         }
   3908         if (self.liveness.isUnused(inst)) {
   3909             return null;
   3910         }
   3911         if (!eu_layout.payload_has_bits) {
   3912             return null;
   3913         }
   3914 
   3915         return try self.extractField(payload_ty, err_union_id, eu_layout.payloadFieldIndex());
   3916     }
   3917 
   3918     fn airErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3919         if (self.liveness.isUnused(inst)) return null;
   3920 
   3921         const mod = self.module;
   3922         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3923         const operand_id = try self.resolve(ty_op.operand);
   3924         const err_union_ty = self.typeOf(ty_op.operand);
   3925         const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
   3926 
   3927         if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) {
   3928             // No error possible, so just return undefined.
   3929             return try self.spv.constUndef(err_ty_ref);
   3930         }
   3931 
   3932         const payload_ty = err_union_ty.errorUnionPayload(mod);
   3933         const eu_layout = self.errorUnionLayout(payload_ty);
   3934 
   3935         if (!eu_layout.payload_has_bits) {
   3936             // If no payload, error union is represented by error set.
   3937             return operand_id;
   3938         }
   3939 
   3940         return try self.extractField(Type.anyerror, operand_id, eu_layout.errorFieldIndex());
   3941     }
   3942 
   3943     fn airErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3944         if (self.liveness.isUnused(inst)) return null;
   3945 
   3946         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3947         const operand_id = try self.resolve(ty_op.operand);
   3948         const payload_ty = self.typeOfIndex(inst);
   3949         const eu_layout = self.errorUnionLayout(payload_ty);
   3950 
   3951         if (!eu_layout.payload_has_bits) {
   3952             return null; // No error possible.
   3953         }
   3954 
   3955         return try self.extractField(payload_ty, operand_id, eu_layout.payloadFieldIndex());
   3956     }
   3957 
   3958     fn airWrapErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3959         if (self.liveness.isUnused(inst)) return null;
   3960 
   3961         const mod = self.module;
   3962         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3963         const err_union_ty = self.typeOfIndex(inst);
   3964         const payload_ty = err_union_ty.errorUnionPayload(mod);
   3965         const operand_id = try self.resolve(ty_op.operand);
   3966         const eu_layout = self.errorUnionLayout(payload_ty);
   3967 
   3968         if (!eu_layout.payload_has_bits) {
   3969             return operand_id;
   3970         }
   3971 
   3972         const payload_ty_ref = try self.resolveType(payload_ty, .indirect);
   3973 
   3974         var members: [2]IdRef = undefined;
   3975         members[eu_layout.errorFieldIndex()] = operand_id;
   3976         members[eu_layout.payloadFieldIndex()] = try self.spv.constUndef(payload_ty_ref);
   3977 
   3978         const err_union_ty_ref = try self.resolveType(err_union_ty, .direct);
   3979         return try self.constructStruct(err_union_ty_ref, &members);
   3980     }
   3981 
   3982     fn airWrapErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   3983         if (self.liveness.isUnused(inst)) return null;
   3984 
   3985         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   3986         const err_union_ty = self.typeOfIndex(inst);
   3987         const operand_id = try self.resolve(ty_op.operand);
   3988         const payload_ty = self.typeOf(ty_op.operand);
   3989         const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
   3990         const eu_layout = self.errorUnionLayout(payload_ty);
   3991 
   3992         if (!eu_layout.payload_has_bits) {
   3993             return try self.constInt(err_ty_ref, 0);
   3994         }
   3995 
   3996         var members: [2]IdRef = undefined;
   3997         members[eu_layout.errorFieldIndex()] = try self.constInt(err_ty_ref, 0);
   3998         members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id);
   3999 
   4000         const err_union_ty_ref = try self.resolveType(err_union_ty, .direct);
   4001         return try self.constructStruct(err_union_ty_ref, &members);
   4002     }
   4003 
   4004     fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_null, is_non_null }) !?IdRef {
   4005         if (self.liveness.isUnused(inst)) return null;
   4006 
   4007         const mod = self.module;
   4008         const un_op = self.air.instructions.items(.data)[inst].un_op;
   4009         const operand_id = try self.resolve(un_op);
   4010         const optional_ty = self.typeOf(un_op);
   4011 
   4012         const payload_ty = optional_ty.optionalChild(mod);
   4013 
   4014         const bool_ty_ref = try self.resolveType(Type.bool, .direct);
   4015 
   4016         if (optional_ty.optionalReprIsPayload(mod)) {
   4017             // Pointer payload represents nullability: pointer or slice.
   4018 
   4019             const ptr_ty = if (payload_ty.isSlice(mod))
   4020                 payload_ty.slicePtrFieldType(mod)
   4021             else
   4022                 payload_ty;
   4023 
   4024             const ptr_id = if (payload_ty.isSlice(mod))
   4025                 try self.extractField(ptr_ty, operand_id, 0)
   4026             else
   4027                 operand_id;
   4028 
   4029             const payload_ty_ref = try self.resolveType(ptr_ty, .direct);
   4030             const null_id = try self.spv.constNull(payload_ty_ref);
   4031             const op: std.math.CompareOperator = switch (pred) {
   4032                 .is_null => .eq,
   4033                 .is_non_null => .neq,
   4034             };
   4035             return try self.cmp(op, ptr_ty, ptr_id, null_id);
   4036         }
   4037 
   4038         const is_non_null_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod))
   4039             try self.extractField(Type.bool, operand_id, 1)
   4040         else
   4041             // Optional representation is bool indicating whether the optional is set
   4042             // Optionals with no payload are represented as an (indirect) bool, so convert
   4043             // it back to the direct bool here.
   4044             try self.convertToDirect(Type.bool, operand_id);
   4045 
   4046         return switch (pred) {
   4047             .is_null => blk: {
   4048                 // Invert condition
   4049                 const result_id = self.spv.allocId();
   4050                 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{
   4051                     .id_result_type = self.typeId(bool_ty_ref),
   4052                     .id_result = result_id,
   4053                     .operand = is_non_null_id,
   4054                 });
   4055                 break :blk result_id;
   4056             },
   4057             .is_non_null => is_non_null_id,
   4058         };
   4059     }
   4060 
   4061     fn airIsErr(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_err, is_non_err }) !?IdRef {
   4062         if (self.liveness.isUnused(inst)) return null;
   4063 
   4064         const mod = self.module;
   4065         const un_op = self.air.instructions.items(.data)[inst].un_op;
   4066         const operand_id = try self.resolve(un_op);
   4067         const err_union_ty = self.typeOf(un_op);
   4068 
   4069         if (err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod)) {
   4070             return try self.constBool(pred == .is_non_err, .direct);
   4071         }
   4072 
   4073         const payload_ty = err_union_ty.errorUnionPayload(mod);
   4074         const eu_layout = self.errorUnionLayout(payload_ty);
   4075         const bool_ty_ref = try self.resolveType(Type.bool, .direct);
   4076         const err_ty_ref = try self.resolveType(Type.anyerror, .direct);
   4077 
   4078         const error_id = if (!eu_layout.payload_has_bits)
   4079             operand_id
   4080         else
   4081             try self.extractField(Type.anyerror, operand_id, eu_layout.errorFieldIndex());
   4082 
   4083         const result_id = self.spv.allocId();
   4084         const operands = .{
   4085             .id_result_type = self.typeId(bool_ty_ref),
   4086             .id_result = result_id,
   4087             .operand_1 = error_id,
   4088             .operand_2 = try self.constInt(err_ty_ref, 0),
   4089         };
   4090         switch (pred) {
   4091             .is_err => try self.func.body.emit(self.spv.gpa, .OpINotEqual, operands),
   4092             .is_non_err => try self.func.body.emit(self.spv.gpa, .OpIEqual, operands),
   4093         }
   4094         return result_id;
   4095     }
   4096 
   4097     fn airUnwrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   4098         if (self.liveness.isUnused(inst)) return null;
   4099 
   4100         const mod = self.module;
   4101         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   4102         const operand_id = try self.resolve(ty_op.operand);
   4103         const optional_ty = self.typeOf(ty_op.operand);
   4104         const payload_ty = self.typeOfIndex(inst);
   4105 
   4106         if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) return null;
   4107 
   4108         if (optional_ty.optionalReprIsPayload(mod)) {
   4109             return operand_id;
   4110         }
   4111 
   4112         return try self.extractField(payload_ty, operand_id, 0);
   4113     }
   4114 
   4115     fn airWrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   4116         if (self.liveness.isUnused(inst)) return null;
   4117 
   4118         const mod = self.module;
   4119         const ty_op = self.air.instructions.items(.data)[inst].ty_op;
   4120         const payload_ty = self.typeOf(ty_op.operand);
   4121 
   4122         if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
   4123             return try self.constBool(true, .indirect);
   4124         }
   4125 
   4126         const operand_id = try self.resolve(ty_op.operand);
   4127 
   4128         const optional_ty = self.typeOfIndex(inst);
   4129         if (optional_ty.optionalReprIsPayload(mod)) {
   4130             return operand_id;
   4131         }
   4132 
   4133         const optional_ty_ref = try self.resolveType(optional_ty, .direct);
   4134         const payload_id = try self.convertToIndirect(payload_ty, operand_id);
   4135         const members = [_]IdRef{ payload_id, try self.constBool(true, .indirect) };
   4136         return try self.constructStruct(optional_ty_ref, &members);
   4137     }
   4138 
   4139     fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void {
   4140         const mod = self.module;
   4141         const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   4142         const cond_ty = self.typeOf(pl_op.operand);
   4143         const cond = try self.resolve(pl_op.operand);
   4144         const cond_indirect = try self.convertToIndirect(cond_ty, cond);
   4145         const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
   4146 
   4147         const cond_words: u32 = switch (cond_ty.zigTypeTag(mod)) {
   4148             .Bool => 1,
   4149             .Int => blk: {
   4150                 const bits = cond_ty.intInfo(mod).bits;
   4151                 const backing_bits = self.backingIntBits(bits) orelse {
   4152                     return self.todo("implement composite int switch", .{});
   4153                 };
   4154                 break :blk if (backing_bits <= 32) @as(u32, 1) else 2;
   4155             },
   4156             .Enum => blk: {
   4157                 const int_ty = cond_ty.intTagType(mod);
   4158                 const int_info = int_ty.intInfo(mod);
   4159                 const backing_bits = self.backingIntBits(int_info.bits) orelse {
   4160                     return self.todo("implement composite int switch", .{});
   4161                 };
   4162                 break :blk if (backing_bits <= 32) @as(u32, 1) else 2;
   4163             },
   4164             .ErrorSet => 1,
   4165             else => return self.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag(mod))}), // TODO: Figure out which types apply here, and work around them as we can only do integers.
   4166         };
   4167 
   4168         const num_cases = switch_br.data.cases_len;
   4169 
   4170         // Compute the total number of arms that we need.
   4171         // Zig switches are grouped by condition, so we need to loop through all of them
   4172         const num_conditions = blk: {
   4173             var extra_index: usize = switch_br.end;
   4174             var case_i: u32 = 0;
   4175             var num_conditions: u32 = 0;
   4176             while (case_i < num_cases) : (case_i += 1) {
   4177                 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
   4178                 const case_body = self.air.extra[case.end + case.data.items_len ..][0..case.data.body_len];
   4179                 extra_index = case.end + case.data.items_len + case_body.len;
   4180                 num_conditions += case.data.items_len;
   4181             }
   4182             break :blk num_conditions;
   4183         };
   4184 
   4185         // First, pre-allocate the labels for the cases.
   4186         const first_case_label = self.spv.allocIds(num_cases);
   4187         // We always need the default case - if zig has none, we will generate unreachable there.
   4188         const default = self.spv.allocId();
   4189 
   4190         // Emit the instruction before generating the blocks.
   4191         try self.func.body.emitRaw(self.spv.gpa, .OpSwitch, 2 + (cond_words + 1) * num_conditions);
   4192         self.func.body.writeOperand(IdRef, cond_indirect);
   4193         self.func.body.writeOperand(IdRef, default);
   4194 
   4195         // Emit each of the cases
   4196         {
   4197             var extra_index: usize = switch_br.end;
   4198             var case_i: u32 = 0;
   4199             while (case_i < num_cases) : (case_i += 1) {
   4200                 // SPIR-V needs a literal here, which' width depends on the case condition.
   4201                 const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
   4202                 const items = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[case.end..][0..case.data.items_len]));
   4203                 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
   4204                 extra_index = case.end + case.data.items_len + case_body.len;
   4205 
   4206                 const label = IdRef{ .id = first_case_label.id + case_i };
   4207 
   4208                 for (items) |item| {
   4209                     const value = (try self.air.value(item, mod)) orelse {
   4210                         return self.todo("switch on runtime value???", .{});
   4211                     };
   4212                     const int_val = switch (cond_ty.zigTypeTag(mod)) {
   4213                         .Bool, .Int => if (cond_ty.isSignedInt(mod)) @as(u64, @bitCast(value.toSignedInt(mod))) else value.toUnsignedInt(mod),
   4214                         .Enum => blk: {
   4215                             // TODO: figure out of cond_ty is correct (something with enum literals)
   4216                             break :blk (try value.intFromEnum(cond_ty, mod)).toUnsignedInt(mod); // TODO: composite integer constants
   4217                         },
   4218                         .ErrorSet => value.getErrorInt(mod),
   4219                         else => unreachable,
   4220                     };
   4221                     const int_lit: spec.LiteralContextDependentNumber = switch (cond_words) {
   4222                         1 => .{ .uint32 = @as(u32, @intCast(int_val)) },
   4223                         2 => .{ .uint64 = int_val },
   4224                         else => unreachable,
   4225                     };
   4226                     self.func.body.writeOperand(spec.LiteralContextDependentNumber, int_lit);
   4227                     self.func.body.writeOperand(IdRef, label);
   4228                 }
   4229             }
   4230         }
   4231 
   4232         // Now, finally, we can start emitting each of the cases.
   4233         var extra_index: usize = switch_br.end;
   4234         var case_i: u32 = 0;
   4235         while (case_i < num_cases) : (case_i += 1) {
   4236             const case = self.air.extraData(Air.SwitchBr.Case, extra_index);
   4237             const items = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[case.end..][0..case.data.items_len]));
   4238             const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
   4239             extra_index = case.end + case.data.items_len + case_body.len;
   4240 
   4241             const label = IdResult{ .id = first_case_label.id + case_i };
   4242 
   4243             try self.beginSpvBlock(label);
   4244             try self.genBody(case_body);
   4245         }
   4246 
   4247         const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len];
   4248         try self.beginSpvBlock(default);
   4249         if (else_body.len != 0) {
   4250             try self.genBody(else_body);
   4251         } else {
   4252             try self.func.body.emit(self.spv.gpa, .OpUnreachable, {});
   4253         }
   4254     }
   4255 
   4256     fn airUnreach(self: *DeclGen) !void {
   4257         try self.func.body.emit(self.spv.gpa, .OpUnreachable, {});
   4258     }
   4259 
   4260     fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {
   4261         const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
   4262         const mod = self.module;
   4263         const decl = mod.declPtr(self.decl_index);
   4264         const path = decl.getFileScope(mod).sub_file_path;
   4265         const src_fname_id = try self.spv.resolveSourceFileName(path);
   4266         const base_line = self.base_line_stack.getLast();
   4267         try self.func.body.emit(self.spv.gpa, .OpLine, .{
   4268             .file = src_fname_id,
   4269             .line = base_line + dbg_stmt.line + 1,
   4270             .column = dbg_stmt.column + 1,
   4271         });
   4272     }
   4273 
   4274     fn airDbgInlineBegin(self: *DeclGen, inst: Air.Inst.Index) !void {
   4275         const mod = self.module;
   4276         const fn_ty = self.air.instructions.items(.data)[inst].ty_fn;
   4277         const decl_index = mod.funcInfo(fn_ty.func).owner_decl;
   4278         const decl = mod.declPtr(decl_index);
   4279         try self.base_line_stack.append(self.gpa, decl.src_line);
   4280     }
   4281 
   4282     fn airDbgInlineEnd(self: *DeclGen, inst: Air.Inst.Index) !void {
   4283         _ = inst;
   4284         _ = self.base_line_stack.pop();
   4285     }
   4286 
   4287     fn airDbgVar(self: *DeclGen, inst: Air.Inst.Index) !void {
   4288         const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   4289         const target_id = try self.resolve(pl_op.operand);
   4290         const name = self.air.nullTerminatedString(pl_op.payload);
   4291         try self.spv.debugName(target_id, name);
   4292     }
   4293 
   4294     fn airAssembly(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
   4295         const mod = self.module;
   4296         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
   4297         const extra = self.air.extraData(Air.Asm, ty_pl.payload);
   4298 
   4299         const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0;
   4300         const clobbers_len = @as(u31, @truncate(extra.data.flags));
   4301 
   4302         if (!is_volatile and self.liveness.isUnused(inst)) return null;
   4303 
   4304         var extra_i: usize = extra.end;
   4305         const outputs = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len]));
   4306         extra_i += outputs.len;
   4307         const inputs = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[extra_i..][0..extra.data.inputs_len]));
   4308         extra_i += inputs.len;
   4309 
   4310         if (outputs.len > 1) {
   4311             return self.todo("implement inline asm with more than 1 output", .{});
   4312         }
   4313 
   4314         var output_extra_i = extra_i;
   4315         for (outputs) |output| {
   4316             if (output != .none) {
   4317                 return self.todo("implement inline asm with non-returned output", .{});
   4318             }
   4319             const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
   4320             const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   4321             const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   4322             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   4323             // TODO: Record output and use it somewhere.
   4324         }
   4325 
   4326         var input_extra_i = extra_i;
   4327         for (inputs) |input| {
   4328             const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
   4329             const constraint = std.mem.sliceTo(extra_bytes, 0);
   4330             const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   4331             // This equation accounts for the fact that even if we have exactly 4 bytes
   4332             // for the string, we still use the next u32 for the null terminator.
   4333             extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   4334             // TODO: Record input and use it somewhere.
   4335             _ = input;
   4336         }
   4337 
   4338         {
   4339             var clobber_i: u32 = 0;
   4340             while (clobber_i < clobbers_len) : (clobber_i += 1) {
   4341                 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
   4342                 extra_i += clobber.len / 4 + 1;
   4343                 // TODO: Record clobber and use it somewhere.
   4344             }
   4345         }
   4346 
   4347         const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len];
   4348 
   4349         var as = SpvAssembler{
   4350             .gpa = self.gpa,
   4351             .src = asm_source,
   4352             .spv = self.spv,
   4353             .func = &self.func,
   4354         };
   4355         defer as.deinit();
   4356 
   4357         for (inputs) |input| {
   4358             const extra_bytes = std.mem.sliceAsBytes(self.air.extra[input_extra_i..]);
   4359             const constraint = std.mem.sliceTo(extra_bytes, 0);
   4360             const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   4361             // This equation accounts for the fact that even if we have exactly 4 bytes
   4362             // for the string, we still use the next u32 for the null terminator.
   4363             input_extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   4364 
   4365             const value = try self.resolve(input);
   4366             try as.value_map.put(as.gpa, name, .{ .value = value });
   4367         }
   4368 
   4369         as.assemble() catch |err| switch (err) {
   4370             error.AssembleFail => {
   4371                 // TODO: For now the compiler only supports a single error message per decl,
   4372                 // so to translate the possible multiple errors from the assembler, emit
   4373                 // them as notes here.
   4374                 // TODO: Translate proper error locations.
   4375                 assert(as.errors.items.len != 0);
   4376                 assert(self.error_msg == null);
   4377                 const loc = LazySrcLoc.nodeOffset(0);
   4378                 const src_loc = loc.toSrcLoc(self.module.declPtr(self.decl_index), mod);
   4379                 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{});
   4380                 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len);
   4381 
   4382                 // Sub-scope to prevent `return error.CodegenFail` from running the errdefers.
   4383                 {
   4384                     errdefer self.module.gpa.free(notes);
   4385                     var i: usize = 0;
   4386                     errdefer for (notes[0..i]) |*note| {
   4387                         note.deinit(self.module.gpa);
   4388                     };
   4389 
   4390                     while (i < as.errors.items.len) : (i += 1) {
   4391                         notes[i] = try Module.ErrorMsg.init(self.module.gpa, src_loc, "{s}", .{as.errors.items[i].msg});
   4392                     }
   4393                 }
   4394                 self.error_msg.?.notes = notes;
   4395                 return error.CodegenFail;
   4396             },
   4397             else => |others| return others,
   4398         };
   4399 
   4400         for (outputs) |output| {
   4401             _ = output;
   4402             const extra_bytes = std.mem.sliceAsBytes(self.air.extra[output_extra_i..]);
   4403             const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[output_extra_i..]), 0);
   4404             const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
   4405             output_extra_i += (constraint.len + name.len + (2 + 3)) / 4;
   4406 
   4407             const result = as.value_map.get(name) orelse return {
   4408                 return self.fail("invalid asm output '{s}'", .{name});
   4409             };
   4410 
   4411             switch (result) {
   4412                 .just_declared, .unresolved_forward_reference => unreachable,
   4413                 .ty => return self.fail("cannot return spir-v type as value from assembly", .{}),
   4414                 .value => |ref| return ref,
   4415             }
   4416 
   4417             // TODO: Multiple results
   4418         }
   4419 
   4420         return null;
   4421     }
   4422 
   4423     fn airCall(self: *DeclGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !?IdRef {
   4424         _ = modifier;
   4425 
   4426         const mod = self.module;
   4427         const pl_op = self.air.instructions.items(.data)[inst].pl_op;
   4428         const extra = self.air.extraData(Air.Call, pl_op.payload);
   4429         const args = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]));
   4430         const callee_ty = self.typeOf(pl_op.operand);
   4431         const zig_fn_ty = switch (callee_ty.zigTypeTag(mod)) {
   4432             .Fn => callee_ty,
   4433             .Pointer => return self.fail("cannot call function pointers", .{}),
   4434             else => unreachable,
   4435         };
   4436         const fn_info = mod.typeToFunc(zig_fn_ty).?;
   4437         const return_type = fn_info.return_type;
   4438 
   4439         const result_type_ref = try self.resolveFnReturnType(return_type.toType());
   4440         const result_id = self.spv.allocId();
   4441         const callee_id = try self.resolve(pl_op.operand);
   4442 
   4443         const params = try self.gpa.alloc(spec.IdRef, args.len);
   4444         defer self.gpa.free(params);
   4445 
   4446         var n_params: usize = 0;
   4447         for (args) |arg| {
   4448             // Note: resolve() might emit instructions, so we need to call it
   4449             // before starting to emit OpFunctionCall instructions. Hence the
   4450             // temporary params buffer.
   4451             const arg_ty = self.typeOf(arg);
   4452             if (!arg_ty.hasRuntimeBitsIgnoreComptime(mod)) continue;
   4453             const arg_id = try self.resolve(arg);
   4454 
   4455             params[n_params] = arg_id;
   4456             n_params += 1;
   4457         }
   4458 
   4459         try self.func.body.emit(self.spv.gpa, .OpFunctionCall, .{
   4460             .id_result_type = self.typeId(result_type_ref),
   4461             .id_result = result_id,
   4462             .function = callee_id,
   4463             .id_ref_3 = params[0..n_params],
   4464         });
   4465 
   4466         if (return_type == .noreturn_type) {
   4467             try self.func.body.emit(self.spv.gpa, .OpUnreachable, {});
   4468         }
   4469 
   4470         if (self.liveness.isUnused(inst) or !return_type.toType().hasRuntimeBitsIgnoreComptime(mod)) {
   4471             return null;
   4472         }
   4473 
   4474         return result_id;
   4475     }
   4476 
   4477     fn typeOf(self: *DeclGen, inst: Air.Inst.Ref) Type {
   4478         const mod = self.module;
   4479         return self.air.typeOf(inst, &mod.intern_pool);
   4480     }
   4481 
   4482     fn typeOfIndex(self: *DeclGen, inst: Air.Inst.Index) Type {
   4483         const mod = self.module;
   4484         return self.air.typeOfIndex(inst, &mod.intern_pool);
   4485     }
   4486 };