From 85169bbba24d7e7592a24de5af6743b34bfe5961 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Fri, 14 Feb 2025 20:28:36 +0330 Subject: [PATCH] spirv: respect cpu features --- src/codegen/spirv.zig | 153 +++++++++++++---------------------- src/codegen/spirv/Module.zig | 40 ++++++--- src/link/SpirV.zig | 92 +++++++++++---------- 3 files changed, 132 insertions(+), 153 deletions(-) diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 8c4b0478de..58182ead0b 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -176,10 +176,10 @@ pub const Object = struct { push_constant_ptr: SpvModule.Decl.Index, } = null, - pub fn init(gpa: Allocator) Object { + pub fn init(gpa: Allocator, target: std.Target) Object { return .{ .gpa = gpa, - .spv = SpvModule.init(gpa), + .spv = SpvModule.init(gpa, target), }; } @@ -412,11 +412,6 @@ const NavGen = struct { self.func.deinit(self.gpa); } - /// Return the target which we are currently compiling for. - pub fn getTarget(self: *NavGen) std.Target { - return self.pt.zcu.getTarget(); - } - pub fn fail(self: *NavGen, comptime format: []const u8, args: anytype) Error { @branchHint(.cold); const zcu = self.pt.zcu; @@ -431,12 +426,12 @@ const NavGen = struct { } /// This imports the "default" extended instruction set for the target - /// For OpenCL, OpenCL.std.100. For Vulkan, GLSL.std.450. + /// For OpenCL, OpenCL.std.100. For Vulkan and OpenGL, GLSL.std.450. fn importExtendedSet(self: *NavGen) !IdResult { - const target = self.getTarget(); + const target = self.spv.target; return switch (target.os.tag) { .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"), - .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"), + .vulkan, .opengl => try self.spv.importInstructionSet(.@"GLSL.std.450"), else => unreachable, }; } @@ -546,14 +541,10 @@ const NavGen = struct { } fn addFunctionDep(self: *NavGen, decl_index: SpvModule.Decl.Index, storage_class: StorageClass) !void { - const target = self.getTarget(); - if (target.os.tag == .vulkan) { - // Shader entry point dependencies must be variables with Input or Output storage class - switch (storage_class) { - .Input, .Output => { - try self.func.decl_deps.put(self.spv.gpa, decl_index, {}); - }, - else => {}, + if (self.spv.version.minor < 4) { + // Before version 1.4, the interface’s storage classes are limited to the Input and Output + if (storage_class == .Input or storage_class == .Output) { + try self.func.decl_deps.put(self.spv.gpa, decl_index, {}); } } else { try self.func.decl_deps.put(self.spv.gpa, decl_index, {}); @@ -561,11 +552,7 @@ const NavGen = struct { } fn castToGeneric(self: *NavGen, type_id: IdRef, ptr_id: IdRef) !IdRef { - const target = self.getTarget(); - - if (target.os.tag == .vulkan) { - return ptr_id; - } else { + if (self.spv.hasFeature(.Kernel)) { const result_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ .id_result_type = type_id, @@ -574,6 +561,8 @@ const NavGen = struct { }); return result_id; } + + return ptr_id; } /// Start a new SPIR-V block, Emits the label of the new block, and stores which @@ -596,8 +585,6 @@ const NavGen = struct { /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers). /// TODO: Should the result of this function be cached? fn backingIntBits(self: *NavGen, bits: u16) ?u16 { - const target = self.getTarget(); - // The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function. assert(bits != 0); @@ -611,14 +598,8 @@ const NavGen = struct { }; for (ints) |int| { - const has_feature = if (int.feature) |feature| - Target.spirv.featureSetHas(target.cpu.features, feature) - else - true; - - if (bits <= int.bits and has_feature) { - return int.bits; - } + const has_feature = if (int.feature) |feature| self.spv.hasFeature(feature) else true; + if (bits <= int.bits and has_feature) return int.bits; } return null; @@ -631,11 +612,7 @@ const NavGen = struct { /// is no way of knowing whether those are actually supported. /// TODO: Maybe this should be cached? fn largestSupportedIntBits(self: *NavGen) u16 { - const target = self.getTarget(); - return if (Target.spirv.featureSetHas(target.cpu.features, .Int64)) - 64 - else - 32; + return if (self.spv.hasFeature(.Int64)) 64 else 32; } /// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by @@ -648,7 +625,6 @@ const NavGen = struct { /// Checks whether the type can be directly translated to SPIR-V vectors fn isSpvVector(self: *NavGen, ty: Type) bool { const zcu = self.pt.zcu; - const target = self.getTarget(); if (ty.zigTypeTag(zcu) != .vector) return false; // TODO: This check must be expanded for types that can be represented @@ -664,17 +640,19 @@ const NavGen = struct { } const elem_ty = ty.childType(zcu); - const len = ty.vectorLen(zcu); - const is_scalar = elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type; - const spirv_len = len > 1 and len <= 4; - const opencl_len = if (target.os.tag == .opencl) (len == 8 or len == 16) else false; - return is_scalar and (spirv_len or opencl_len); + + if (elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type) { + if (len > 1 and len <= 4) return true; + if (self.spv.hasFeature(.Vector16)) return (len == 8 or len == 16); + } + + return false; } fn arithmeticTypeInfo(self: *NavGen, ty: Type) ArithmeticTypeInfo { const zcu = self.pt.zcu; - const target = self.getTarget(); + const target = self.spv.target; var scalar_ty = ty.scalarType(zcu); if (scalar_ty.zigTypeTag(zcu) == .@"enum") { scalar_ty = scalar_ty.intTagType(zcu); @@ -791,7 +769,7 @@ const NavGen = struct { /// ty must be an aggregate type. fn constructCompositeSplat(self: *NavGen, ty: Type, constituent: IdRef) !IdRef { const zcu = self.pt.zcu; - const n = ty.arrayLen(zcu); + const n: usize = @intCast(ty.arrayLen(zcu)); const constituents = try self.gpa.alloc(IdRef, n); defer self.gpa.free(constituents); @@ -817,7 +795,7 @@ const NavGen = struct { const pt = self.pt; const zcu = pt.zcu; - const target = self.getTarget(); + const target = self.spv.target; const result_ty_id = try self.resolveType(ty, repr); const ip = &zcu.intern_pool; @@ -1263,11 +1241,11 @@ const NavGen = struct { }; // Kernel only supports unsigned ints. - if (self.getTarget().os.tag == .vulkan) { - return self.spv.intType(signedness, backing_bits); + if (self.spv.hasFeature(.Kernel)) { + return self.spv.intType(.unsigned, backing_bits); } - return self.spv.intType(.unsigned, backing_bits); + return self.spv.intType(signedness, backing_bits); } fn arrayType(self: *NavGen, len: u32, child_ty: IdRef) !IdRef { @@ -1436,7 +1414,7 @@ const NavGen = struct { const zcu = pt.zcu; const ip = &zcu.intern_pool; log.debug("resolveType: ty = {}", .{ty.fmt(pt)}); - const target = self.getTarget(); + const target = self.spv.target; const section = &self.spv.sections.types_globals_constants; @@ -1533,7 +1511,7 @@ const NavGen = struct { return try self.arrayType(1, elem_ty_id); } else { const result_id = try self.arrayType(total_len, elem_ty_id); - if (target.os.tag == .vulkan) { + if (self.spv.hasFeature(.Shader)) { try self.spv.decorate(result_id, .{ .ArrayStride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)), } }); @@ -1667,7 +1645,7 @@ const NavGen = struct { continue; } - if (target.os.tag == .vulkan) { + if (self.spv.hasFeature(.Shader)) { try self.spv.decorateMember(result_id, index, .{ .Offset = .{ .byte_offset = @intCast(ty.structFieldOffset(field_index, zcu)), } }); @@ -1769,20 +1747,11 @@ const NavGen = struct { } fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass { - const target = self.getTarget(); return switch (as) { - .generic => switch (target.os.tag) { - .vulkan => .Function, - .opencl => .Generic, - else => unreachable, - }, + .generic => if (self.spv.hasFeature(.GenericPointer)) .Generic else .Function, .shared => .Workgroup, .local => .Function, - .global => switch (target.os.tag) { - .opencl => .CrossWorkgroup, - .vulkan => .PhysicalStorageBuffer, - else => unreachable, - }, + .global => if (self.spv.hasFeature(.Shader)) .PhysicalStorageBuffer else .CrossWorkgroup, .constant => .UniformConstant, .push_constant => .PushConstant, .input => .Input, @@ -2326,7 +2295,7 @@ const NavGen = struct { } fn buildFma(self: *NavGen, a: Temporary, b: Temporary, c: Temporary) !Temporary { - const target = self.getTarget(); + const target = self.spv.target; const v = self.vectorization(.{ a, b, c }); const ops = v.operations(); @@ -2348,7 +2317,7 @@ const NavGen = struct { // NOTE: Vulkan's FMA instruction does *NOT* produce the right values! // its precision guarantees do NOT match zigs and it does NOT match OpenCLs! // it needs to be emulated! - .vulkan => unreachable, // TODO: See above + .vulkan, .opengl => unreachable, // TODO: See above else => unreachable, }; @@ -2485,14 +2454,14 @@ const NavGen = struct { }; fn buildUnary(self: *NavGen, op: UnaryOp, operand: Temporary) !Temporary { - const target = self.getTarget(); + const target = self.spv.target; const v = blk: { const v = self.vectorization(.{operand}); break :blk switch (op) { // TODO: These instructions don't seem to be working // properly for LLVM-based backends on OpenCL for 8- and // 16-component vectors. - .i_abs => if (target.os.tag == .opencl and v.components() >= 8) v.unroll() else v, + .i_abs => if (self.spv.hasFeature(.Vector16) and v.components() >= 8) v.unroll() else v, else => v, }; }; @@ -2545,7 +2514,7 @@ const NavGen = struct { // Note: We'll need to check these for floating point accuracy // Vulkan does not put tight requirements on these, for correction // we might want to emulate them at some point. - .vulkan => switch (op) { + .vulkan, .opengl => switch (op) { .i_abs => 5, // SAbs .f_abs => 4, // FAbs .clz => unreachable, // TODO @@ -2615,7 +2584,7 @@ const NavGen = struct { }; fn buildBinary(self: *NavGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary { - const target = self.getTarget(); + const target = self.spv.target; const v = self.vectorization(.{ lhs, rhs }); const ops = v.operations(); @@ -2674,7 +2643,7 @@ const NavGen = struct { .u_min => 159, // u_min else => unreachable, }, - .vulkan => switch (op) { + .vulkan, .opengl => switch (op) { .f_max => 40, // FMax .s_max => 42, // SMax .u_max => 41, // UMax @@ -2713,7 +2682,7 @@ const NavGen = struct { ) !struct { Temporary, Temporary } { const pt = self.pt; const zcu = pt.zcu; - const target = self.getTarget(); + const target = self.spv.target; const ip = &zcu.intern_pool; const v = lhs.vectorization(self).unify(rhs.vectorization(self)); @@ -2756,7 +2725,7 @@ const NavGen = struct { }); } }, - .vulkan => { + .vulkan, .opengl => { // Operations return a struct{T, T} // where T is maybe vectorized. const op_result_ty: Type = .fromInterned(try ip.getTupleType(zcu.gpa, pt.tid, .{ @@ -2843,7 +2812,7 @@ const NavGen = struct { const section = &self.spv.sections.functions; - const target = self.getTarget(); + const target = self.spv.target; const p_error_id = self.spv.allocId(); switch (target.os.tag) { @@ -2866,7 +2835,7 @@ const NavGen = struct { .id_result = self.spv.allocId(), }); }, - .vulkan => { + .vulkan, .opengl => { const ptr_ptr_anyerror_ty_id = self.spv.allocId(); try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ .id_result = ptr_ptr_anyerror_ty_id, @@ -2967,7 +2936,7 @@ const NavGen = struct { defer self.gpa.free(test_name); const execution_mode: spec.ExecutionModel = switch (target.os.tag) { - .vulkan => .GLCompute, + .vulkan, .opengl => .GLCompute, .opencl => .Kernel, else => unreachable, }; @@ -3670,7 +3639,6 @@ const NavGen = struct { } fn abs(self: *NavGen, result_ty: Type, value: Temporary) !Temporary { - const target = self.getTarget(); const operand_info = self.arithmeticTypeInfo(value.ty); switch (operand_info.class) { @@ -3682,7 +3650,7 @@ const NavGen = struct { // depending on the result type. Do that when // bitCast is implemented for vectors. // This is only relevant for Vulkan - assert(target.os.tag != .vulkan); // TODO + assert(self.spv.hasFeature(.Kernel)); // TODO return try self.normalize(abs_value, self.arithmeticTypeInfo(result_ty)); }, @@ -3756,7 +3724,6 @@ const NavGen = struct { } fn airMulOverflow(self: *NavGen, inst: Air.Inst.Index) !?IdRef { - const target = self.getTarget(); const pt = self.pt; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -3780,7 +3747,7 @@ const NavGen = struct { // - Additionally, if info.bits != 32, we'll have to check the high bits // of the result too. - const largest_int_bits: u16 = if (Target.spirv.featureSetHas(target.cpu.features, .Int64)) 64 else 32; + const largest_int_bits = self.largestSupportedIntBits(); // If non-null, the number of bits that the multiplication should be performed in. If // null, we have to use wide multiplication. const maybe_op_ty_bits: ?u16 = switch (info.bits) { @@ -3989,7 +3956,6 @@ const NavGen = struct { if (self.liveness.isUnused(inst)) return null; const zcu = self.pt.zcu; - const target = self.getTarget(); const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try self.temporary(ty_op.operand); @@ -4002,10 +3968,7 @@ const NavGen = struct { .float, .bool => unreachable, } - switch (target.os.tag) { - .vulkan => unreachable, // TODO - else => {}, - } + assert(self.spv.hasFeature(.Kernel)); // TODO const count = try self.buildUnary(op, operand); @@ -4241,23 +4204,22 @@ const NavGen = struct { defer self.gpa.free(ids); const result_id = self.spv.allocId(); - const target = self.getTarget(); - switch (target.os.tag) { - .opencl => try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{ + if (self.spv.hasFeature(.Kernel)) { + try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{ .id_result_type = result_ty_id, .id_result = result_id, .base = base, .element = element, .indexes = ids, - }), - .vulkan => try self.func.body.emit(self.spv.gpa, .OpPtrAccessChain, .{ + }); + } else { + try self.func.body.emit(self.spv.gpa, .OpPtrAccessChain, .{ .id_result_type = result_ty_id, .id_result = result_id, .base = base, .element = element, .indexes = ids, - }), - else => unreachable, + }); } return result_id; } @@ -5328,10 +5290,7 @@ const NavGen = struct { .initializer = options.initializer, }); - const target = self.getTarget(); - if (target.os.tag == .vulkan) { - return var_id; - } + if (self.spv.hasFeature(.Shader)) return var_id; switch (options.storage_class) { .Generic => { @@ -6204,7 +6163,7 @@ const NavGen = struct { fn airSwitchBr(self: *NavGen, inst: Air.Inst.Index) !void { const pt = self.pt; const zcu = pt.zcu; - const target = self.getTarget(); + const target = self.spv.target; const switch_br = self.air.unwrapSwitch(inst); const cond_ty = self.typeOf(switch_br.operand); const cond = try self.resolve(switch_br.operand); diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index 8e7f5ad4bc..6d5b0afccc 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -118,6 +118,12 @@ gpa: Allocator, /// Arena for things that need to live for the length of this program. arena: std.heap.ArenaAllocator, +/// Target info +target: std.Target, + +/// The target SPIR-V version +version: spec.Version, + /// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module". sections: struct { /// Capability instructions @@ -196,10 +202,23 @@ entry_points: std.ArrayListUnmanaged(EntryPoint) = .empty, /// The list of extended instruction sets that should be imported. extended_instruction_set: std.AutoHashMapUnmanaged(spec.InstructionSet, IdRef) = .empty, -pub fn init(gpa: Allocator) Module { +pub fn init(gpa: Allocator, target: std.Target) Module { + const version_minor: u8 = blk: { + // Prefer higher versions + if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_6)) break :blk 6; + if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_5)) break :blk 5; + if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_4)) break :blk 4; + if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_3)) break :blk 3; + if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_2)) break :blk 2; + if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_1)) break :blk 1; + break :blk 0; + }; + return .{ .gpa = gpa, .arena = std.heap.ArenaAllocator.init(gpa), + .target = target, + .version = .{ .major = 1, .minor = version_minor }, .next_result_id = 1, // 0 is an invalid SPIR-V result id, so start counting at 1. }; } @@ -263,6 +282,10 @@ pub fn idBound(self: Module) Word { return self.next_result_id; } +pub fn hasFeature(self: *Module, feature: std.Target.spirv.Feature) bool { + return std.Target.spirv.featureSetHas(self.target.cpu.features, feature); +} + fn addEntryPointDeps( self: *Module, decl_index: Decl.Index, @@ -315,7 +338,7 @@ fn entryPoints(self: *Module) !Section { return entry_points; } -pub fn finalize(self: *Module, a: Allocator, target: std.Target) ![]Word { +pub fn finalize(self: *Module, a: Allocator) ![]Word { // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction" // TODO: Audit calls to allocId() in this function to make it idempotent. @@ -324,16 +347,7 @@ pub fn finalize(self: *Module, a: Allocator, target: std.Target) ![]Word { const header = [_]Word{ spec.magic_number, - // TODO: From cpu features - spec.Version.toWord(.{ - .major = 1, - .minor = switch (target.os.tag) { - // Emit SPIR-V 1.3 for now. This is the highest version that Vulkan 1.1 supports. - .vulkan => 3, - // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports. - else => 4, - }, - }), + self.version.toWord(), spec.zig_generator_id, self.idBound(), 0, // Schema (currently reserved for future use) @@ -342,7 +356,7 @@ pub fn finalize(self: *Module, a: Allocator, target: std.Target) ![]Word { var source = Section{}; defer source.deinit(self.gpa); try self.sections.debug_strings.emit(self.gpa, .OpSource, .{ - .source_language = .Unknown, + .source_language = .Zig, .version = 0, // We cannot emit these because the Khronos translator does not parse this instruction // correctly. diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index a5b9615c5e..7e2f2b1114 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -75,7 +75,7 @@ pub fn createEmpty( .disable_lld_caching = options.disable_lld_caching, .build_id = options.build_id, }, - .object = codegen.Object.init(gpa), + .object = codegen.Object.init(gpa, comp.getTarget()), }; errdefer self.deinit(); @@ -172,7 +172,7 @@ pub fn updateExports( const spv_decl_index = try self.object.resolveNav(zcu, nav_index); const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu); const execution_model: spec.ExecutionModel = switch (target.os.tag) { - .vulkan => switch (cc) { + .vulkan, .opengl => switch (cc) { .spirv_vertex => .Vertex, .spirv_fragment => .Fragment, .spirv_kernel => .GLCompute, @@ -231,10 +231,9 @@ pub fn flushModule( const spv = &self.object.spv; const diags = &comp.link_diags; const gpa = comp.gpa; - const target = comp.getTarget(); - try writeCapabilities(spv, target); - try writeMemoryModel(spv, target); + try writeCapabilities(spv); + try writeMemoryModel(spv); // We need to export the list of error names somewhere so that we can pretty-print them in the // executor. This is not really an important thing though, so we can just dump it in any old @@ -269,7 +268,7 @@ pub fn flushModule( .extension = error_info.items, }); - const module = try spv.finalize(arena, target); + const module = try spv.finalize(arena); errdefer arena.free(module); const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) { @@ -299,56 +298,63 @@ fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: std.Progress return binary.finalize(a); } -fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { - const gpa = spv.gpa; - // TODO: Integrate with a hypothetical feature system - const caps: []const spec.Capability = switch (target.os.tag) { - .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .Vector16, .GenericPointer }, - .vulkan => &.{ .Shader, .PhysicalStorageBufferAddresses, .Int8, .Int16, .Int64, .Float64, .Float16, .VariablePointers, .VariablePointersStorageBuffer }, - else => unreachable, - }; +fn writeCapabilities(spv: *SpvModule) !void { + var caps: std.ArrayList(spec.Capability) = .init(spv.gpa); + var extensions: std.ArrayList([]const u8) = .init(spv.gpa); + defer { + caps.deinit(); + extensions.deinit(); + } - for (caps) |cap| { - try spv.sections.capabilities.emit(gpa, .OpCapability, .{ + // Currently all spirv target features name are mapped to a Capability or an Extension. + // Except for versions which we ignore. + for (std.Target.spirv.all_features, 0..) |_, i| { + if (spv.target.cpu.features.isEnabled(@intCast(i))) { + const feature: std.Target.spirv.Feature = @enumFromInt(i); + const name = @tagName(feature); + if (std.meta.stringToEnum(spec.Capability, name)) |cap| { + try caps.append(cap); + } else if (std.mem.startsWith(u8, name, "SPV_")) { + try extensions.append(name); + } + } + } + + for (caps.items) |cap| { + try spv.sections.capabilities.emit(spv.gpa, .OpCapability, .{ .capability = cap, }); } - switch (target.os.tag) { - .vulkan => { - try spv.sections.extensions.emit(gpa, .OpExtension, .{ - .name = "SPV_KHR_physical_storage_buffer", - }); - }, - else => {}, + for (extensions.items) |ext| { + try spv.sections.extensions.emit(spv.gpa, .OpExtension, .{ .name = ext }); } } -fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { - const gpa = spv.gpa; +fn writeMemoryModel(spv: *SpvModule) !void { + const addressing_model: spec.AddressingModel = blk: { + if (spv.hasFeature(.Shader)) { + break :blk switch (spv.target.cpu.arch) { + .spirv32 => .Logical, // TODO: I don't think this will ever be implemented. + .spirv64 => .PhysicalStorageBuffer64, + else => unreachable, + }; + } else if (spv.hasFeature(.Kernel)) { + break :blk switch (spv.target.cpu.arch) { + .spirv32 => .Physical32, + .spirv64 => .Physical64, + else => unreachable, + }; + } - const addressing_model: spec.AddressingModel = switch (target.os.tag) { - .opencl => switch (target.cpu.arch) { - .spirv32 => .Physical32, - .spirv64 => .Physical64, - else => unreachable, - }, - .opengl, .vulkan => switch (target.cpu.arch) { - .spirv32 => .Logical, // TODO: I don't think this will ever be implemented. - .spirv64 => .PhysicalStorageBuffer64, - else => unreachable, - }, - else => unreachable, + unreachable; }; - - const memory_model: spec.MemoryModel = switch (target.os.tag) { + const memory_model: spec.MemoryModel = switch (spv.target.os.tag) { .opencl => .OpenCL, - .opengl => .GLSL450, - .vulkan => .GLSL450, + .vulkan, .opengl => .GLSL450, else => unreachable, }; - - try spv.sections.memory_model.emit(gpa, .OpMemoryModel, .{ + try spv.sections.memory_model.emit(spv.gpa, .OpMemoryModel, .{ .addressing_model = addressing_model, .memory_model = memory_model, });