From a0eec9ce9e2f4ae40729949957bef98a2513fef7 Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Wed, 19 Feb 2025 00:06:10 +0330 Subject: [PATCH] spirv: replace some unreachables with compile errors --- lib/std/Target/spirv.zig | 42 +++++++++++++++------------ src/Zcu.zig | 8 ++++++ src/codegen/spirv.zig | 56 ++++++++++++++++++++++-------------- src/codegen/spirv/Module.zig | 12 ++++++-- 4 files changed, 76 insertions(+), 42 deletions(-) diff --git a/lib/std/Target/spirv.zig b/lib/std/Target/spirv.zig index 73d57060be..6657bfd971 100644 --- a/lib/std/Target/spirv.zig +++ b/lib/std/Target/spirv.zig @@ -17,6 +17,7 @@ pub const Feature = enum { float64, addresses, matrix, + storage_push_constant16, kernel, generic_pointer, vector16, @@ -35,92 +36,97 @@ pub const all_features = blk: { var result: [len]CpuFeature = undefined; result[@intFromEnum(Feature.v1_0)] = .{ .llvm_name = null, - .description = "SPIR-V version 1.0", + .description = "Enable version 1.0", .dependencies = featureSet(&[_]Feature{}), }; result[@intFromEnum(Feature.v1_1)] = .{ .llvm_name = null, - .description = "SPIR-V version 1.1", + .description = "Enable version 1.1", .dependencies = featureSet(&[_]Feature{.v1_0}), }; result[@intFromEnum(Feature.v1_2)] = .{ .llvm_name = null, - .description = "SPIR-V version 1.2", + .description = "Enable version 1.2", .dependencies = featureSet(&[_]Feature{.v1_1}), }; result[@intFromEnum(Feature.v1_3)] = .{ .llvm_name = null, - .description = "SPIR-V version 1.3", + .description = "Enable version 1.3", .dependencies = featureSet(&[_]Feature{.v1_2}), }; result[@intFromEnum(Feature.v1_4)] = .{ .llvm_name = null, - .description = "SPIR-V version 1.4", + .description = "Enable version 1.4", .dependencies = featureSet(&[_]Feature{.v1_3}), }; result[@intFromEnum(Feature.v1_5)] = .{ .llvm_name = null, - .description = "SPIR-V version 1.5", + .description = "Enable version 1.5", .dependencies = featureSet(&[_]Feature{.v1_4}), }; result[@intFromEnum(Feature.v1_6)] = .{ .llvm_name = null, - .description = "SPIR-V version 1.6", + .description = "Enable version 1.6", .dependencies = featureSet(&[_]Feature{.v1_5}), }; result[@intFromEnum(Feature.int8)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Int8", + .description = "Enable Int8 capability", .dependencies = featureSet(&[_]Feature{.v1_0}), }; result[@intFromEnum(Feature.int16)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Int16", + .description = "Enable Int16 capability", .dependencies = featureSet(&[_]Feature{.v1_0}), }; result[@intFromEnum(Feature.int64)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Int64", + .description = "Enable Int64 capability", .dependencies = featureSet(&[_]Feature{.v1_0}), }; result[@intFromEnum(Feature.float16)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Float16", + .description = "Enable Float16 capability", .dependencies = featureSet(&[_]Feature{.v1_0}), }; result[@intFromEnum(Feature.float64)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Float64", + .description = "Enable Float64 capability", .dependencies = featureSet(&[_]Feature{.v1_0}), }; result[@intFromEnum(Feature.addresses)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Addresses", + .description = "Enable either the Addresses capability or, SPV_KHR_physical_storage_buffer extension and the PhysicalStorageBufferAddresses capability", .dependencies = featureSet(&[_]Feature{.v1_0}), }; result[@intFromEnum(Feature.matrix)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Matrix", + .description = "Enable Matrix capability", .dependencies = featureSet(&[_]Feature{.v1_0}), }; + result[@intFromEnum(Feature.storage_push_constant16)] = .{ + .llvm_name = null, + .description = "Enable SPV_KHR_16bit_storage extension and the StoragePushConstant16 capability", + .dependencies = featureSet(&[_]Feature{.v1_3}), + }; result[@intFromEnum(Feature.kernel)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Kernel", + .description = "Enable Kernel capability", .dependencies = featureSet(&[_]Feature{.v1_0}), }; result[@intFromEnum(Feature.generic_pointer)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability GenericPointer", + .description = "Enable GenericPointer capability", .dependencies = featureSet(&[_]Feature{ .v1_0, .addresses }), }; result[@intFromEnum(Feature.vector16)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Vector16", + .description = "Enable Vector16 capability", .dependencies = featureSet(&[_]Feature{ .v1_0, .kernel }), }; result[@intFromEnum(Feature.shader)] = .{ .llvm_name = null, - .description = "Enable SPIR-V capability Shader", + .description = "Enable Shader capability", .dependencies = featureSet(&[_]Feature{ .v1_0, .matrix }), }; const ti = @typeInfo(Feature); diff --git a/src/Zcu.zig b/src/Zcu.zig index fd0b0dba64..56b1595653 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3463,7 +3463,15 @@ fn clearCachedResolvedReferences(zcu: *Zcu) void { } pub fn errorSetBits(zcu: *const Zcu) u16 { + const target = zcu.getTarget(); + if (zcu.error_limit == 0) return 0; + if (target.cpu.arch == .spirv64) { + if (!std.Target.spirv.featureSetHas(target.cpu.features, .storage_push_constant16)) { + return 32; + } + } + return @as(u16, std.math.log2_int(ErrorInt, zcu.error_limit)) + 1; } diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index ec96c56ae9..de7f3a0699 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1018,7 +1018,7 @@ const NavGen = struct { const comp_ty_id = try self.resolveType(ty, .direct); return try self.constructComposite(comp_ty_id, constituents.items); }, - .tuple_type => unreachable, // TODO + .tuple_type => return self.todo("implement tuple types", .{}), else => unreachable, }, .un => |un| { @@ -1255,6 +1255,7 @@ const NavGen = struct { fn ptrType(self: *NavGen, child_ty: Type, storage_class: StorageClass, child_repr: Repr) !IdRef { const zcu = self.pt.zcu; + const ip = &zcu.intern_pool; const key = .{ child_ty.toIntern(), storage_class, child_repr }; const entry = try self.ptr_types.getOrPut(self.gpa, key); if (entry.found_existing) { @@ -1285,7 +1286,12 @@ const NavGen = struct { } } - try self.spv.decorate(result_id, .{ .ArrayStride = .{ .array_stride = @intCast(child_ty.abiSize(zcu)) } }); + switch (ip.indexToKey(child_ty.toIntern())) { + .func_type, .opaque_type => {}, + else => { + try self.spv.decorate(result_id, .{ .ArrayStride = .{ .array_stride = @intCast(child_ty.abiSize(zcu)) } }); + }, + } } try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ @@ -1704,7 +1710,10 @@ const NavGen = struct { return result_id; }, .@"union" => return try self.resolveUnionType(ty), - .error_set => return try self.resolveType(Type.u16, repr), + .error_set => { + const err_int_ty = try pt.errorIntType(); + return try self.resolveType(err_int_ty, repr); + }, .error_union => { const payload_ty = ty.errorUnionPayload(zcu); const error_ty_id = try self.resolveType(Type.anyerror, .indirect); @@ -2329,7 +2338,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, .opengl => unreachable, // TODO: See above + .vulkan, .opengl => return self.todo("implement fma operation for {s} os", .{@tagName(target.os.tag)}), else => unreachable, }; @@ -2529,12 +2538,12 @@ const NavGen = struct { .vulkan, .opengl => switch (op) { .i_abs => 5, // SAbs .f_abs => 4, // FAbs - .clz => unreachable, // TODO - .ctz => unreachable, // TODO .floor => 8, // Floor .ceil => 9, // Ceil .trunc => 3, // Trunc .round => 1, // Round + .clz, + .ctz, .sqrt, .sin, .cos, @@ -2544,7 +2553,7 @@ const NavGen = struct { .log, .log2, .log10, - => unreachable, // TODO + => return self.todo("implement unary operation '{s}' for {s} os", .{ @tagName(op), @tagName(target.os.tag) }), else => unreachable, }, else => unreachable, @@ -2810,6 +2819,8 @@ const NavGen = struct { /// TODO is to also write out the error as a function call parameter, and to somehow fetch /// the name of an error in the text executor. fn generateTestEntryPoint(self: *NavGen, name: []const u8, spv_test_decl_index: SpvModule.Decl.Index) !void { + const target = self.spv.target; + const anyerror_ty_id = try self.resolveType(Type.anyerror, .direct); const ptr_anyerror_ty = try self.pt.ptrType(.{ .child = Type.anyerror.toIntern(), @@ -2819,13 +2830,13 @@ const NavGen = struct { const spv_decl_index = try self.spv.allocDecl(.func); const kernel_id = self.spv.declPtr(spv_decl_index).result_id; - // for some reason we don't need to decorate the push constant here... - try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index}); + + var decl_deps = std.ArrayList(SpvModule.Decl.Index).init(self.gpa); + defer decl_deps.deinit(); + try decl_deps.append(spv_test_decl_index); const section = &self.spv.sections.functions; - const target = self.spv.target; - const p_error_id = self.spv.allocId(); switch (target.os.tag) { .opencl => { @@ -2904,6 +2915,7 @@ const NavGen = struct { const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr; const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id; + try decl_deps.append(spv_err_decl_index); const zero_id = try self.constInt(Type.u32, 0); // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use @@ -2953,6 +2965,7 @@ const NavGen = struct { else => unreachable, }; + try self.spv.declareDeclDeps(spv_decl_index, decl_deps.items); try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode); } @@ -3372,6 +3385,7 @@ const NavGen = struct { .switch_br => return self.airSwitchBr(inst), .unreach, .trap => return self.airUnreach(), + .dbg_empty_stmt => return, .dbg_stmt => return self.airDbgStmt(inst), .dbg_inline_block => try self.airDbgInlineBlock(inst), .dbg_var_ptr, .dbg_var_val, .dbg_arg_inline => return self.airDbgVar(inst), @@ -3651,6 +3665,7 @@ const NavGen = struct { } fn abs(self: *NavGen, result_ty: Type, value: Temporary) !Temporary { + const zcu = self.pt.zcu; const operand_info = self.arithmeticTypeInfo(value.ty); switch (operand_info.class) { @@ -3658,11 +3673,9 @@ const NavGen = struct { .integer, .strange_integer => { const abs_value = try self.buildUnary(.i_abs, value); - // TODO: We may need to bitcast the result to a uint - // depending on the result type. Do that when - // bitCast is implemented for vectors. - // This is only relevant for Vulkan - assert(self.spv.hasFeature(.kernel)); // TODO + if (value.ty.intInfo(zcu).signedness == .signed and self.spv.hasFeature(.shader)) { + return self.todo("perform bitcast after @abs", .{}); + } return try self.normalize(abs_value, self.arithmeticTypeInfo(result_ty)); }, @@ -3980,8 +3993,6 @@ const NavGen = struct { .float, .bool => unreachable, } - assert(self.spv.hasFeature(.kernel)); // TODO - const count = try self.buildUnary(op, operand); // Result of OpenCL ctz/clz returns operand.ty, and we want result_ty. @@ -4307,7 +4318,8 @@ const NavGen = struct { }, .error_set => { assert(!is_vector); - return try self.cmp(op, lhs.pun(Type.u16), rhs.pun(Type.u16)); + const err_int_ty = try pt.errorIntType(); + return try self.cmp(op, lhs.pun(err_int_ty), rhs.pun(err_int_ty)); }, .pointer => { assert(!is_vector); @@ -4411,7 +4423,7 @@ const NavGen = struct { else => unreachable, }; }, - else => unreachable, + else => |ty| return self.todo("implement cmp operation for '{s}' type", .{@tagName(ty)}), } const info = self.arithmeticTypeInfo(scalar_ty); @@ -5233,13 +5245,13 @@ const NavGen = struct { return self.accessChain(result_ty_id, object_ptr, &.{field_index}); }, .@"struct" => switch (object_ty.containerLayout(zcu)) { - .@"packed" => unreachable, // TODO + .@"packed" => return self.todo("implement field access for packed structs", .{}), else => { return try self.accessChain(result_ty_id, object_ptr, &.{field_index}); }, }, .@"union" => switch (object_ty.containerLayout(zcu)) { - .@"packed" => unreachable, // TODO + .@"packed" => return self.todo("implement field access for packed unions", .{}), else => { const layout = self.unionLayout(object_ty); if (!layout.has_payload) { diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index 317e32c878..30f9b0bc54 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -345,22 +345,30 @@ pub fn finalize(self: *Module, a: Allocator) ![]Word { if (self.target.cpu.features.isEnabled(feature.index)) { const feature_tag: std.Target.spirv.Feature = @enumFromInt(feature.index); switch (feature_tag) { + // Versions .v1_0, .v1_1, .v1_2, .v1_3, .v1_4, .v1_5, .v1_6 => {}, + // Features with no dependencies .int8 => try self.addCapability(.Int8), .int16 => try self.addCapability(.Int16), .int64 => try self.addCapability(.Int64), .float16 => try self.addCapability(.Float16), .float64 => try self.addCapability(.Float64), + .matrix => try self.addCapability(.Matrix), + .storage_push_constant16 => { + try self.addExtension("SPV_KHR_16bit_storage"); + try self.addCapability(.StoragePushConstant16); + }, .addresses => if (self.hasFeature(.shader)) { - try self.addCapability(.PhysicalStorageBufferAddresses); try self.addExtension("SPV_KHR_physical_storage_buffer"); + try self.addCapability(.PhysicalStorageBufferAddresses); } else { try self.addCapability(.Addresses); }, - .matrix => try self.addCapability(.Matrix), + // Kernel .kernel => try self.addCapability(.Kernel), .generic_pointer => try self.addCapability(.GenericPointer), .vector16 => try self.addCapability(.Vector16), + // Shader .shader => try self.addCapability(.Shader), } }