From 9b42bc1ce5a1d688aa2167a068a3b75d69888c12 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 20 Oct 2024 16:51:15 +0200 Subject: [PATCH 1/7] spirv: start.zig support for vulkan * Use builtin.zig_backend instead of builtin.cpu.arch, the latter does not yet compile under VK. * Don't call regular _start for either opencl or vulkan. We might even want to disable these completely. --- lib/std/start.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/std/start.zig b/lib/std/start.zig index a70d9e609e..c5664cbf0f 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -19,8 +19,7 @@ pub const simplified_logic = builtin.zig_backend == .stage2_aarch64 or builtin.zig_backend == .stage2_arm or builtin.zig_backend == .stage2_sparc64 or - builtin.cpu.arch == .spirv32 or - builtin.cpu.arch == .spirv64; + builtin.zig_backend == .stage2_spirv64; comptime { // No matter what, we import the root file, so that any export, test, comptime @@ -37,7 +36,7 @@ comptime { if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) { @export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" }); } - } else if (builtin.os.tag == .opencl) { + } else if (builtin.os.tag == .opencl or builtin.os.tag == .vulkan) { if (@hasDecl(root, "main")) @export(&spirvMain2, .{ .name = "main" }); } else { From 6de456c179d81b9118ecd26eb9d7c90213bac313 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 20 Oct 2024 16:53:53 +0200 Subject: [PATCH 2/7] spirv: fix up calling conventions for vulkan * Fragment and Vertex CCs are only valid for SPIR-V when running under Vulkan. * Emit GLCompute instead of Kernel for SPIR-V kernels. --- src/Zcu.zig | 7 ++----- src/codegen/spirv.zig | 22 +++++++++++++--------- src/link/SpirV.zig | 43 +++++++++++++++++++++++++------------------ 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/Zcu.zig b/src/Zcu.zig index d86fee6365..aa8610b7fd 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -3639,11 +3639,8 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu else => false, }, .stage2_spirv64 => switch (cc) { - .spirv_device, - .spirv_kernel, - .spirv_fragment, - .spirv_vertex, - => true, + .spirv_device, .spirv_kernel => true, + .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan, else => false, }, }; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 279b7d5056..da527c9ac8 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1640,13 +1640,18 @@ const NavGen = struct { comptime assert(zig_call_abi_ver == 3); switch (fn_info.cc) { - .auto, .spirv_kernel, .spirv_fragment, .spirv_vertex => {}, - else => @panic("TODO"), + .auto, + .spirv_kernel, + .spirv_fragment, + .spirv_vertex, + .spirv_device, + => {}, + else => unreachable, } - // TODO: Put this somewhere in Sema.zig - if (fn_info.is_var_args) - return self.fail("VarArgs functions are unsupported for SPIR-V", .{}); + // Guaranteed by callConvSupportsVarArgs, there are nog SPIR-V CCs which support + // varargs. + assert(!fn_info.is_var_args); // Note: Logic is different from functionType(). const param_ty_ids = try self.gpa.alloc(IdRef, fn_info.param_types.len); @@ -2969,11 +2974,10 @@ const NavGen = struct { try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ .id_result_type = return_ty_id, .id_result = result_id, - .function_control = switch (fn_info.cc) { - .@"inline" => .{ .Inline = true }, - else => .{}, - }, .function_type = prototype_ty_id, + // Note: the backend will never be asked to generate an inline function + // (this is handled in sema), so we don't need to set function_control here. + .function_control = .{}, }); comptime assert(zig_call_abi_ver == 3); diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index c6b0752aee..59ca27d48e 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -161,28 +161,35 @@ pub fn updateExports( }, }; const nav_ty = ip.getNav(nav_index).typeOf(ip); + const target = zcu.getTarget(); if (ip.isFunctionType(nav_ty)) { - const target = zcu.getTarget(); const spv_decl_index = try self.object.resolveNav(zcu, nav_index); - const execution_model = switch (Type.fromInterned(nav_ty).fnCallingConvention(zcu)) { - .spirv_vertex => spec.ExecutionModel.Vertex, - .spirv_fragment => spec.ExecutionModel.Fragment, - .spirv_kernel => spec.ExecutionModel.Kernel, + const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu); + const execution_model: spec.ExecutionModel = switch (target.os.tag) { + .vulkan => switch (cc) { + .spirv_vertex => .Vertex, + .spirv_fragment => .Fragment, + .spirv_kernel => .GLCompute, + // TODO: We should integrate with the Linkage capability and export this function + .spirv_device => return, + else => unreachable, + }, + .opencl => switch (cc) { + .spirv_kernel => .Kernel, + // TODO: We should integrate with the Linkage capability and export this function + .spirv_device => return, + else => unreachable, + }, else => unreachable, }; - const is_vulkan = target.os.tag == .vulkan; - if ((!is_vulkan and execution_model == .Kernel) or - (is_vulkan and (execution_model == .Fragment or execution_model == .Vertex))) - { - for (export_indices) |export_idx| { - const exp = zcu.all_exports.items[export_idx]; - try self.object.spv.declareEntryPoint( - spv_decl_index, - exp.opts.name.toSlice(ip), - execution_model, - ); - } + for (export_indices) |export_idx| { + const exp = zcu.all_exports.items[export_idx]; + try self.object.spv.declareEntryPoint( + spv_decl_index, + exp.opts.name.toSlice(ip), + execution_model, + ); } } @@ -258,7 +265,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => |other| { - log.err("error while linking: {s}\n", .{@errorName(other)}); + log.err("error while linking: {s}", .{@errorName(other)}); return error.FlushFailure; }, }; From 7c6923136718aab50fc20a327e47c0c23517dda2 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 20 Oct 2024 16:59:32 +0200 Subject: [PATCH 3/7] spirv: use PhysicalStorageBuffer64 for global pointers under vk We can use real pointers with this storage class!! --- src/codegen/spirv.zig | 9 +++++++-- src/link/SpirV.zig | 30 +++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index da527c9ac8..b0253ff2cf 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1843,11 +1843,16 @@ const NavGen = struct { return switch (as) { .generic => switch (target.os.tag) { .vulkan => .Private, - else => .Generic, + .opencl => .Generic, + else => unreachable, }, .shared => .Workgroup, .local => .Private, - .global => .CrossWorkgroup, + .global => switch (target.os.tag) { + .opencl => .CrossWorkgroup, + .vulkan => .PhysicalStorageBuffer, + else => unreachable, + }, .constant => .UniformConstant, .input => .Input, .output => .Output, diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 59ca27d48e..045bd05d84 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -296,9 +296,8 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { // 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 }, - .opengl => &.{.Shader}, - .vulkan => &.{ .Shader, .VariablePointersStorageBuffer, .Int8, .Int16, .Int64, .Float64, .Float16 }, - else => unreachable, // TODO + .vulkan => &.{ .Shader, .PhysicalStorageBufferAddresses, .StoragePushConstant16, .Int8, .Int16, .Int64, .Float64, .Float16 }, + else => unreachable, }; for (caps) |cap| { @@ -306,19 +305,32 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { .capability = cap, }); } + + switch (target.os.tag) { + .vulkan => { + try spv.sections.extensions.emit(gpa, .OpExtension, .{ + .name = "SPV_KHR_physical_storage_buffer", + }); + }, + else => {}, + } } fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { const gpa = spv.gpa; - const addressing_model = switch (target.os.tag) { + const addressing_model: spec.AddressingModel = switch (target.os.tag) { .opencl => switch (target.cpu.arch) { - .spirv32 => spec.AddressingModel.Physical32, - .spirv64 => spec.AddressingModel.Physical64, - else => unreachable, // TODO + .spirv32 => .Physical32, + .spirv64 => .Physical64, + else => unreachable, }, - .opengl, .vulkan => spec.AddressingModel.Logical, - else => unreachable, // TODO + .opengl, .vulkan => switch (target.cpu.arch) { + .spirv32 => .Logical, // TODO: I don't think this will ever be implemented. + .spirv64 => .PhysicalStorageBuffer64, + else => unreachable, + }, + else => unreachable, }; const memory_model: spec.MemoryModel = switch (target.os.tag) { From 39013619b943956f0c26422a01f026d845dc96a9 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 20 Oct 2024 17:10:55 +0200 Subject: [PATCH 4/7] spirv: generate test entry points for vulkan --- src/codegen/spirv.zig | 147 +++++++++++++++++--- src/link/SpirV/lower_invocation_globals.zig | 9 ++ 2 files changed, 134 insertions(+), 22 deletions(-) diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index b0253ff2cf..46c23d7d53 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -169,6 +169,13 @@ pub const Object = struct { /// via the usual `intern_map` mechanism. ptr_types: PtrTypeMap = .{}, + /// For test declarations for Vulkan, we have to add a push constant with a pointer to a + /// buffer that we can use. We only need to generate this once, this holds the link information + /// related to that. + error_push_constant: ?struct { + push_constant_ptr: SpvModule.Decl.Index, + } = null, + pub fn init(gpa: Allocator) Object { return .{ .gpa = gpa, @@ -2908,30 +2915,118 @@ const NavGen = struct { .flags = .{ .address_space = .global }, }); const ptr_anyerror_ty_id = try self.resolveType(ptr_anyerror_ty, .direct); - const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty}); - - const test_id = self.spv.declPtr(spv_test_decl_index).result_id; const spv_decl_index = try self.spv.allocDecl(.func); const kernel_id = self.spv.declPtr(spv_decl_index).result_id; - - const error_id = self.spv.allocId(); - const p_error_id = self.spv.allocId(); + // for some reason we don't need to decorate the push constant here... + try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index}); const section = &self.spv.sections.functions; - try section.emit(self.spv.gpa, .OpFunction, .{ - .id_result_type = try self.resolveType(Type.void, .direct), - .id_result = kernel_id, - .function_control = .{}, - .function_type = kernel_proto_ty_id, - }); - try section.emit(self.spv.gpa, .OpFunctionParameter, .{ - .id_result_type = ptr_anyerror_ty_id, - .id_result = p_error_id, - }); - try section.emit(self.spv.gpa, .OpLabel, .{ - .id_result = self.spv.allocId(), - }); + + const target = self.getTarget(); + + const p_error_id = self.spv.allocId(); + switch (target.os.tag) { + .opencl => { + const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty}); + + try section.emit(self.spv.gpa, .OpFunction, .{ + .id_result_type = try self.resolveType(Type.void, .direct), + .id_result = kernel_id, + .function_control = .{}, + .function_type = kernel_proto_ty_id, + }); + + try section.emit(self.spv.gpa, .OpFunctionParameter, .{ + .id_result_type = ptr_anyerror_ty_id, + .id_result = p_error_id, + }); + + try section.emit(self.spv.gpa, .OpLabel, .{ + .id_result = self.spv.allocId(), + }); + }, + .vulkan => { + 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, + .storage_class = .PushConstant, + .type = ptr_anyerror_ty_id, + }); + + if (self.object.error_push_constant == null) { + const spv_err_decl_index = try self.spv.allocDecl(.global); + try self.spv.declareDeclDeps(spv_err_decl_index, &.{}); + + const push_constant_struct_ty_id = try self.spv.structType( + &.{ptr_anyerror_ty_id}, + &.{"error_out_ptr"}, + ); + try self.spv.decorate(push_constant_struct_ty_id, .Block); + try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } }); + + const ptr_push_constant_struct_ty_id = self.spv.allocId(); + try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ + .id_result = ptr_push_constant_struct_ty_id, + .storage_class = .PushConstant, + .type = push_constant_struct_ty_id, + }); + + try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{ + .id_result_type = ptr_push_constant_struct_ty_id, + .id_result = self.spv.declPtr(spv_err_decl_index).result_id, + .storage_class = .PushConstant, + }); + + self.object.error_push_constant = .{ + .push_constant_ptr = spv_err_decl_index, + }; + } + + try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{ + .entry_point = kernel_id, + .mode = .{ .LocalSize = .{ + .x_size = 1, + .y_size = 1, + .z_size = 1, + } }, + }); + + const kernel_proto_ty_id = try self.functionType(Type.void, &.{}); + try section.emit(self.spv.gpa, .OpFunction, .{ + .id_result_type = try self.resolveType(Type.void, .direct), + .id_result = kernel_id, + .function_control = .{}, + .function_type = kernel_proto_ty_id, + }); + try section.emit(self.spv.gpa, .OpLabel, .{ + .id_result = self.spv.allocId(), + }); + + 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; + + const zero_id = try self.constInt(Type.u32, 0, .direct); + // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use + // a load. + const tmp = self.spv.allocId(); + try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{ + .id_result_type = ptr_ptr_anyerror_ty_id, + .id_result = tmp, + .base = push_constant_id, + .indexes = &.{zero_id}, + }); + try section.emit(self.spv.gpa, .OpLoad, .{ + .id_result_type = ptr_anyerror_ty_id, + .id_result = p_error_id, + .pointer = tmp, + }); + }, + else => unreachable, + } + + const test_id = self.spv.declPtr(spv_test_decl_index).result_id; + const error_id = self.spv.allocId(); try section.emit(self.spv.gpa, .OpFunctionCall, .{ .id_result_type = anyerror_ty_id, .id_result = error_id, @@ -2941,17 +3036,25 @@ const NavGen = struct { try section.emit(self.spv.gpa, .OpStore, .{ .pointer = p_error_id, .object = error_id, + .memory_access = .{ + .Aligned = .{ .literal_integer = @sizeOf(u16) }, + }, }); try section.emit(self.spv.gpa, .OpReturn, {}); try section.emit(self.spv.gpa, .OpFunctionEnd, {}); - try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index}); - // Just generate a quick other name because the intel runtime crashes when the entry- // point name is the same as a different OpName. const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name}); defer self.gpa.free(test_name); - try self.spv.declareEntryPoint(spv_decl_index, test_name, .Kernel); + + const execution_mode: spec.ExecutionModel = switch (target.os.tag) { + .vulkan => .GLCompute, + .opencl => .Kernel, + else => unreachable, + }; + + try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode); } fn genNav(self: *NavGen, do_codegen: bool) !void { diff --git a/src/link/SpirV/lower_invocation_globals.zig b/src/link/SpirV/lower_invocation_globals.zig index a06a868e18..9d91a142e4 100644 --- a/src/link/SpirV/lower_invocation_globals.zig +++ b/src/link/SpirV/lower_invocation_globals.zig @@ -400,6 +400,15 @@ const ModuleBuilder = struct { self.section.writeWords(inst.operands[2..]); continue; }, + .OpExecutionMode, .OpExecutionModeId => { + const original_id: ResultId = @enumFromInt(inst.operands[0]); + const new_id_index = info.entry_points.getIndex(original_id).?; + const new_id: ResultId = @enumFromInt(self.entry_point_new_id_base + new_id_index); + try self.section.emitRaw(self.arena, inst.opcode, inst.operands.len); + self.section.writeOperand(ResultId, new_id); + self.section.writeWords(inst.operands[1..]); + continue; + }, .OpTypeFunction => { // Re-emitted in `emitFunctionTypes()`. We can do this because // OpTypeFunction's may not currently be used anywhere that is not From 49a067ccfe6cc3ee59b0fe0f2dc32f80ab75d574 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 27 Oct 2024 16:31:45 +0100 Subject: [PATCH 5/7] spirv: forbid merging logical pointers Under some architecture/operating system combinations it is forbidden to return a pointer from a merge, as these pointers must point to a location at compile time. This adds a check for those cases when returning a pointer from a block merge. --- src/Sema.zig | 36 +++++++++++++++++++ src/target.zig | 25 +++++++++++++ .../spirv_merge_logical_pointers.zig | 19 ++++++++++ test/cases/spirv_mergable_pointers.zig | 16 +++++++++ test/src/Cases.zig | 2 +- 5 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/spirv_merge_logical_pointers.zig create mode 100644 test/cases/spirv_mergable_pointers.zig diff --git a/src/Sema.zig b/src/Sema.zig index b0ce7729cf..b0ce90d277 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6319,6 +6319,9 @@ fn resolveAnalyzedBlock( for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| { try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst); } + + try sema.checkMergeAllowed(child_block, type_src, resolved_ty); + const ty_inst = Air.internedToRef(resolved_ty.toIntern()); switch (block_tag) { .block => { @@ -9754,6 +9757,39 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: } } +fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void { + const pt = sema.pt; + const zcu = pt.zcu; + const target = zcu.getTarget(); + + if (!peer_ty.isPtrAtRuntime(zcu)) { + return; + } + + const as = peer_ty.ptrAddressSpace(zcu); + if (!target_util.arePointersLogical(target, as)) { + return; + } + + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "value with non-mergable pointer type '{}' depends on runtime control flow", .{peer_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + + const runtime_src = block.runtime_cond orelse block.runtime_loop.?; + try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); + + const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm); + try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target {s}-{s} by compiler backend {s}", .{ + @tagName(as), + target.cpu.arch.genericName(), + @tagName(target.os.tag), + @tagName(backend), + }); + + break :msg msg; + }); +} + const Section = union(enum) { generic, default, diff --git a/src/target.zig b/src/target.zig index 1f8a567f03..b0d2bdfe74 100644 --- a/src/target.zig +++ b/src/target.zig @@ -398,6 +398,31 @@ pub fn addrSpaceCastIsValid( } } +/// Under SPIR-V with Vulkan, pointers are not 'real' (physical), but rather 'logical'. Effectively, +/// this means that all such pointers have to be resolvable to a location at compile time, and places +/// a number of restrictions on usage of such pointers. For example, a logical pointer may not be +/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns +/// for a particular architecture and address space wether such pointers are logical. +pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool { + if (target.os.tag != .vulkan) { + return false; + } + + return switch (as) { + // TODO: Vulkan doesn't support pointers in the generic address space, we + // should remove this case but this requires a change in defaultAddressSpace(). + // For now, at least disable them from being regarded as physical. + .generic => true, + // For now, all global pointers are represented using PhysicalStorageBuffer, so these are real + // pointers. + .global => false, + // TODO: Allowed with VK_KHR_variable_pointers. + .shared => true, + .constant, .local, .input, .output, .uniform => true, + else => unreachable, + }; +} + pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 { // LLD does not support ELFv1. Rather than having LLVM produce ELFv1 code and then linking it // into a broken ELFv2 binary, just force LLVM to use ELFv2 as well. This will break when glibc diff --git a/test/cases/compile_errors/spirv_merge_logical_pointers.zig b/test/cases/compile_errors/spirv_merge_logical_pointers.zig new file mode 100644 index 0000000000..ea81ef903c --- /dev/null +++ b/test/cases/compile_errors/spirv_merge_logical_pointers.zig @@ -0,0 +1,19 @@ +export fn a() void { + var x: *i32 = undefined; + _ = &x; + var y: *i32 = undefined; + _ = &y; + var rt_cond = false; + _ = &rt_cond; + + var z = if (rt_cond) x else y; + _ = &z; +} + +// error +// backend=stage2 +// target=spirv64-vulkan +// +// :9:13: error: value with non-mergable pointer type '*i32' depends on runtime control flow +// :9:17: note: runtime control flow here +// :9:13: note: pointers with address space 'generic' cannot be returned from a branch on target spirv-vulkan by compiler backend stage2_spirv64 diff --git a/test/cases/spirv_mergable_pointers.zig b/test/cases/spirv_mergable_pointers.zig new file mode 100644 index 0000000000..240c485f9d --- /dev/null +++ b/test/cases/spirv_mergable_pointers.zig @@ -0,0 +1,16 @@ +export fn a() void { + var x: *addrspace(.global) i32 = undefined; + _ = &x; + var y: *addrspace(.global) i32 = undefined; + _ = &y; + var rt_cond = false; + _ = &rt_cond; + + var z = if (rt_cond) x else y; + _ = &z; +} + +// compile +// output_mode=Obj +// backend=stage2 +// target=spirv64-vulkan diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 0e88bdd133..9314b5677b 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -467,7 +467,7 @@ fn addFromDirInner( const target = resolved_target.result; for (backends) |backend| { if (backend == .stage2 and - target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64) + target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64 and target.cpu.arch != .spirv64) { // Other backends don't support new liveness format continue; From 6d8ee89721f742a90d218a621640c9e11633e758 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sun, 27 Oct 2024 18:02:12 +0100 Subject: [PATCH 6/7] fix compile error tests with unstable error sets The print order of error sets depends on the order that the compiler adds names to its internal state. These names can be anything, and do not necessarily need to be from the same error set or be errors at all. When the last remaining reference to builtin.cpu.arch was removed in start.zig in 9b42bc1ce5, this order changed. Likely there is something that has the name 'C' that is referenced somewhere recursively from builtin.cpu.arch. This all causes these few tests to fail, and hence the expected order is simply updated now. Perhaps there is a better way to add this. --- ...cit_error_set_cast_known_at_comptime_violates_error_sets.zig | 2 +- .../compile_errors/implicit_cast_of_error_set_not_a_subset.zig | 2 +- .../compile_errors/int_to_err_non_global_invalid_number.zig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig b/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig index a1beac65f2..0418d327c9 100644 --- a/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig +++ b/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig @@ -10,4 +10,4 @@ comptime { // backend=stage2 // target=native // -// :5:21: error: 'error.B' not a member of error set 'error{C,A}' +// :5:21: error: 'error.B' not a member of error set 'error{A,C}' diff --git a/test/cases/compile_errors/implicit_cast_of_error_set_not_a_subset.zig b/test/cases/compile_errors/implicit_cast_of_error_set_not_a_subset.zig index 617e638627..1dab9d81c9 100644 --- a/test/cases/compile_errors/implicit_cast_of_error_set_not_a_subset.zig +++ b/test/cases/compile_errors/implicit_cast_of_error_set_not_a_subset.zig @@ -12,5 +12,5 @@ fn foo(set1: Set1) void { // backend=stage2 // target=native // -// :7:21: error: expected type 'error{C,A}', found 'error{A,B}' +// :7:21: error: expected type 'error{A,C}', found 'error{A,B}' // :7:21: note: 'error.B' not a member of destination error set diff --git a/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig b/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig index c9f6c1e2e4..829192ab8d 100644 --- a/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig +++ b/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig @@ -16,4 +16,4 @@ comptime { // backend=llvm // target=native // -// :11:21: error: 'error.B' not a member of error set 'error{C,A}' +// :11:21: error: 'error.B' not a member of error set 'error{A,C}' From ae57f6fd07d1830f9179222207671d30d9f9d707 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Mon, 28 Oct 2024 20:39:37 +0100 Subject: [PATCH 7/7] dwarf: flush wip_nav after emitting anyerror type info This caused a missing reference for u16 to not be emitted. Triggered after removing something from start.zig which transitively added u16 to the module. --- src/link/Dwarf.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index d428377c3b..41e9adb2c3 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -3845,6 +3845,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void { } if (global_error_set_names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null)); try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items); + try wip_nav.flush(.unneeded); } {