authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-10-29 19:58:51+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-10-29 19:58:51+01:00
log3450809e3db14226b7f8e57bdfa7fde590e803d8
treea1100c41f7a5be7c9f608e7b0316a42523f2b36c
parent7025c06eb6102117ec472cac4586a3841bdb6fa8
parentae57f6fd07d1830f9179222207671d30d9f9d707
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21826 from Snektron/spirv-vulkan

spirv: vulkan setup

14 files changed, 305 insertions(+), 72 deletions(-)

lib/std/start.zig+2-3
......@@ -19,8 +19,7 @@ pub const simplified_logic =
1919 builtin.zig_backend == .stage2_aarch64 or
2020 builtin.zig_backend == .stage2_arm or
2121 builtin.zig_backend == .stage2_sparc64 or
22 builtin.cpu.arch == .spirv32 or
23 builtin.cpu.arch == .spirv64;
22 builtin.zig_backend == .stage2_spirv64;
2423
2524comptime {
2625 // No matter what, we import the root file, so that any export, test, comptime
......@@ -37,7 +36,7 @@ comptime {
3736 if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) {
3837 @export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
3938 }
40 } else if (builtin.os.tag == .opencl) {
39 } else if (builtin.os.tag == .opencl or builtin.os.tag == .vulkan) {
4140 if (@hasDecl(root, "main"))
4241 @export(&spirvMain2, .{ .name = "main" });
4342 } else {
src/Sema.zig+36
......@@ -6253,6 +6253,9 @@ fn resolveAnalyzedBlock(
62536253 for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| {
62546254 try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst);
62556255 }
6256
6257 try sema.checkMergeAllowed(child_block, type_src, resolved_ty);
6258
62566259 const ty_inst = Air.internedToRef(resolved_ty.toIntern());
62576260 switch (block_tag) {
62586261 .block => {
......@@ -9688,6 +9691,39 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc:
96889691 }
96899692}
96909693
9694fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void {
9695 const pt = sema.pt;
9696 const zcu = pt.zcu;
9697 const target = zcu.getTarget();
9698
9699 if (!peer_ty.isPtrAtRuntime(zcu)) {
9700 return;
9701 }
9702
9703 const as = peer_ty.ptrAddressSpace(zcu);
9704 if (!target_util.arePointersLogical(target, as)) {
9705 return;
9706 }
9707
9708 return sema.failWithOwnedErrorMsg(block, msg: {
9709 const msg = try sema.errMsg(src, "value with non-mergable pointer type '{}' depends on runtime control flow", .{peer_ty.fmt(pt)});
9710 errdefer msg.destroy(sema.gpa);
9711
9712 const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
9713 try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
9714
9715 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
9716 try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target {s}-{s} by compiler backend {s}", .{
9717 @tagName(as),
9718 target.cpu.arch.genericName(),
9719 @tagName(target.os.tag),
9720 @tagName(backend),
9721 });
9722
9723 break :msg msg;
9724 });
9725}
9726
96919727const Section = union(enum) {
96929728 generic,
96939729 default,
src/Zcu.zig+2-5
......@@ -3639,11 +3639,8 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
36393639 else => false,
36403640 },
36413641 .stage2_spirv64 => switch (cc) {
3642 .spirv_device,
3643 .spirv_kernel,
3644 .spirv_fragment,
3645 .spirv_vertex,
3646 => true,
3642 .spirv_device, .spirv_kernel => true,
3643 .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan,
36473644 else => false,
36483645 },
36493646 };
src/codegen/spirv.zig+144-32
......@@ -169,6 +169,13 @@ pub const Object = struct {
169169 /// via the usual `intern_map` mechanism.
170170 ptr_types: PtrTypeMap = .{},
171171
172 /// For test declarations for Vulkan, we have to add a push constant with a pointer to a
173 /// buffer that we can use. We only need to generate this once, this holds the link information
174 /// related to that.
175 error_push_constant: ?struct {
176 push_constant_ptr: SpvModule.Decl.Index,
177 } = null,
178
172179 pub fn init(gpa: Allocator) Object {
173180 return .{
174181 .gpa = gpa,
......@@ -1640,13 +1647,18 @@ const NavGen = struct {
16401647
16411648 comptime assert(zig_call_abi_ver == 3);
16421649 switch (fn_info.cc) {
1643 .auto, .spirv_kernel, .spirv_fragment, .spirv_vertex => {},
1644 else => @panic("TODO"),
1650 .auto,
1651 .spirv_kernel,
1652 .spirv_fragment,
1653 .spirv_vertex,
1654 .spirv_device,
1655 => {},
1656 else => unreachable,
16451657 }
16461658
1647 // TODO: Put this somewhere in Sema.zig
1648 if (fn_info.is_var_args)
1649 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
1659 // Guaranteed by callConvSupportsVarArgs, there are nog SPIR-V CCs which support
1660 // varargs.
1661 assert(!fn_info.is_var_args);
16501662
16511663 // Note: Logic is different from functionType().
16521664 const param_ty_ids = try self.gpa.alloc(IdRef, fn_info.param_types.len);
......@@ -1838,11 +1850,16 @@ const NavGen = struct {
18381850 return switch (as) {
18391851 .generic => switch (target.os.tag) {
18401852 .vulkan => .Private,
1841 else => .Generic,
1853 .opencl => .Generic,
1854 else => unreachable,
18421855 },
18431856 .shared => .Workgroup,
18441857 .local => .Private,
1845 .global => .CrossWorkgroup,
1858 .global => switch (target.os.tag) {
1859 .opencl => .CrossWorkgroup,
1860 .vulkan => .PhysicalStorageBuffer,
1861 else => unreachable,
1862 },
18461863 .constant => .UniformConstant,
18471864 .input => .Input,
18481865 .output => .Output,
......@@ -2898,30 +2915,118 @@ const NavGen = struct {
28982915 .flags = .{ .address_space = .global },
28992916 });
29002917 const ptr_anyerror_ty_id = try self.resolveType(ptr_anyerror_ty, .direct);
2901 const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty});
2902
2903 const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
29042918
29052919 const spv_decl_index = try self.spv.allocDecl(.func);
29062920 const kernel_id = self.spv.declPtr(spv_decl_index).result_id;
2921 // for some reason we don't need to decorate the push constant here...
2922 try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index});
2923
2924 const section = &self.spv.sections.functions;
2925
2926 const target = self.getTarget();
29072927
2908 const error_id = self.spv.allocId();
29092928 const p_error_id = self.spv.allocId();
2929 switch (target.os.tag) {
2930 .opencl => {
2931 const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty});
29102932
2911 const section = &self.spv.sections.functions;
2912 try section.emit(self.spv.gpa, .OpFunction, .{
2913 .id_result_type = try self.resolveType(Type.void, .direct),
2914 .id_result = kernel_id,
2915 .function_control = .{},
2916 .function_type = kernel_proto_ty_id,
2917 });
2918 try section.emit(self.spv.gpa, .OpFunctionParameter, .{
2919 .id_result_type = ptr_anyerror_ty_id,
2920 .id_result = p_error_id,
2921 });
2922 try section.emit(self.spv.gpa, .OpLabel, .{
2923 .id_result = self.spv.allocId(),
2924 });
2933 try section.emit(self.spv.gpa, .OpFunction, .{
2934 .id_result_type = try self.resolveType(Type.void, .direct),
2935 .id_result = kernel_id,
2936 .function_control = .{},
2937 .function_type = kernel_proto_ty_id,
2938 });
2939
2940 try section.emit(self.spv.gpa, .OpFunctionParameter, .{
2941 .id_result_type = ptr_anyerror_ty_id,
2942 .id_result = p_error_id,
2943 });
2944
2945 try section.emit(self.spv.gpa, .OpLabel, .{
2946 .id_result = self.spv.allocId(),
2947 });
2948 },
2949 .vulkan => {
2950 const ptr_ptr_anyerror_ty_id = self.spv.allocId();
2951 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2952 .id_result = ptr_ptr_anyerror_ty_id,
2953 .storage_class = .PushConstant,
2954 .type = ptr_anyerror_ty_id,
2955 });
2956
2957 if (self.object.error_push_constant == null) {
2958 const spv_err_decl_index = try self.spv.allocDecl(.global);
2959 try self.spv.declareDeclDeps(spv_err_decl_index, &.{});
2960
2961 const push_constant_struct_ty_id = try self.spv.structType(
2962 &.{ptr_anyerror_ty_id},
2963 &.{"error_out_ptr"},
2964 );
2965 try self.spv.decorate(push_constant_struct_ty_id, .Block);
2966 try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });
2967
2968 const ptr_push_constant_struct_ty_id = self.spv.allocId();
2969 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
2970 .id_result = ptr_push_constant_struct_ty_id,
2971 .storage_class = .PushConstant,
2972 .type = push_constant_struct_ty_id,
2973 });
2974
2975 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
2976 .id_result_type = ptr_push_constant_struct_ty_id,
2977 .id_result = self.spv.declPtr(spv_err_decl_index).result_id,
2978 .storage_class = .PushConstant,
2979 });
2980
2981 self.object.error_push_constant = .{
2982 .push_constant_ptr = spv_err_decl_index,
2983 };
2984 }
2985
2986 try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{
2987 .entry_point = kernel_id,
2988 .mode = .{ .LocalSize = .{
2989 .x_size = 1,
2990 .y_size = 1,
2991 .z_size = 1,
2992 } },
2993 });
2994
2995 const kernel_proto_ty_id = try self.functionType(Type.void, &.{});
2996 try section.emit(self.spv.gpa, .OpFunction, .{
2997 .id_result_type = try self.resolveType(Type.void, .direct),
2998 .id_result = kernel_id,
2999 .function_control = .{},
3000 .function_type = kernel_proto_ty_id,
3001 });
3002 try section.emit(self.spv.gpa, .OpLabel, .{
3003 .id_result = self.spv.allocId(),
3004 });
3005
3006 const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;
3007 const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;
3008
3009 const zero_id = try self.constInt(Type.u32, 0, .direct);
3010 // We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use
3011 // a load.
3012 const tmp = self.spv.allocId();
3013 try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{
3014 .id_result_type = ptr_ptr_anyerror_ty_id,
3015 .id_result = tmp,
3016 .base = push_constant_id,
3017 .indexes = &.{zero_id},
3018 });
3019 try section.emit(self.spv.gpa, .OpLoad, .{
3020 .id_result_type = ptr_anyerror_ty_id,
3021 .id_result = p_error_id,
3022 .pointer = tmp,
3023 });
3024 },
3025 else => unreachable,
3026 }
3027
3028 const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
3029 const error_id = self.spv.allocId();
29253030 try section.emit(self.spv.gpa, .OpFunctionCall, .{
29263031 .id_result_type = anyerror_ty_id,
29273032 .id_result = error_id,
......@@ -2931,17 +3036,25 @@ const NavGen = struct {
29313036 try section.emit(self.spv.gpa, .OpStore, .{
29323037 .pointer = p_error_id,
29333038 .object = error_id,
3039 .memory_access = .{
3040 .Aligned = .{ .literal_integer = @sizeOf(u16) },
3041 },
29343042 });
29353043 try section.emit(self.spv.gpa, .OpReturn, {});
29363044 try section.emit(self.spv.gpa, .OpFunctionEnd, {});
29373045
2938 try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index});
2939
29403046 // Just generate a quick other name because the intel runtime crashes when the entry-
29413047 // point name is the same as a different OpName.
29423048 const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name});
29433049 defer self.gpa.free(test_name);
2944 try self.spv.declareEntryPoint(spv_decl_index, test_name, .Kernel);
3050
3051 const execution_mode: spec.ExecutionModel = switch (target.os.tag) {
3052 .vulkan => .GLCompute,
3053 .opencl => .Kernel,
3054 else => unreachable,
3055 };
3056
3057 try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode);
29453058 }
29463059
29473060 fn genNav(self: *NavGen, do_codegen: bool) !void {
......@@ -2969,11 +3082,10 @@ const NavGen = struct {
29693082 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
29703083 .id_result_type = return_ty_id,
29713084 .id_result = result_id,
2972 .function_control = switch (fn_info.cc) {
2973 .@"inline" => .{ .Inline = true },
2974 else => .{},
2975 },
29763085 .function_type = prototype_ty_id,
3086 // Note: the backend will never be asked to generate an inline function
3087 // (this is handled in sema), so we don't need to set function_control here.
3088 .function_control = .{},
29773089 });
29783090
29793091 comptime assert(zig_call_abi_ver == 3);
src/link/Dwarf.zig+1
......@@ -3845,6 +3845,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
38453845 }
38463846 if (global_error_set_names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null));
38473847 try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items);
3848 try wip_nav.flush(.unneeded);
38483849 }
38493850
38503851 {
src/link/SpirV.zig+47-28
......@@ -161,28 +161,35 @@ pub fn updateExports(
161161 },
162162 };
163163 const nav_ty = ip.getNav(nav_index).typeOf(ip);
164 const target = zcu.getTarget();
164165 if (ip.isFunctionType(nav_ty)) {
165 const target = zcu.getTarget();
166166 const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
167 const execution_model = switch (Type.fromInterned(nav_ty).fnCallingConvention(zcu)) {
168 .spirv_vertex => spec.ExecutionModel.Vertex,
169 .spirv_fragment => spec.ExecutionModel.Fragment,
170 .spirv_kernel => spec.ExecutionModel.Kernel,
167 const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
168 const execution_model: spec.ExecutionModel = switch (target.os.tag) {
169 .vulkan => switch (cc) {
170 .spirv_vertex => .Vertex,
171 .spirv_fragment => .Fragment,
172 .spirv_kernel => .GLCompute,
173 // TODO: We should integrate with the Linkage capability and export this function
174 .spirv_device => return,
175 else => unreachable,
176 },
177 .opencl => switch (cc) {
178 .spirv_kernel => .Kernel,
179 // TODO: We should integrate with the Linkage capability and export this function
180 .spirv_device => return,
181 else => unreachable,
182 },
171183 else => unreachable,
172184 };
173 const is_vulkan = target.os.tag == .vulkan;
174
175 if ((!is_vulkan and execution_model == .Kernel) or
176 (is_vulkan and (execution_model == .Fragment or execution_model == .Vertex)))
177 {
178 for (export_indices) |export_idx| {
179 const exp = zcu.all_exports.items[export_idx];
180 try self.object.spv.declareEntryPoint(
181 spv_decl_index,
182 exp.opts.name.toSlice(ip),
183 execution_model,
184 );
185 }
185
186 for (export_indices) |export_idx| {
187 const exp = zcu.all_exports.items[export_idx];
188 try self.object.spv.declareEntryPoint(
189 spv_decl_index,
190 exp.opts.name.toSlice(ip),
191 execution_model,
192 );
186193 }
187194 }
188195
......@@ -258,7 +265,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
258265 const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
259266 error.OutOfMemory => return error.OutOfMemory,
260267 else => |other| {
261 log.err("error while linking: {s}\n", .{@errorName(other)});
268 log.err("error while linking: {s}", .{@errorName(other)});
262269 return error.FlushFailure;
263270 },
264271 };
......@@ -289,9 +296,8 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
289296 // TODO: Integrate with a hypothetical feature system
290297 const caps: []const spec.Capability = switch (target.os.tag) {
291298 .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .Vector16, .GenericPointer },
292 .opengl => &.{.Shader},
293 .vulkan => &.{ .Shader, .VariablePointersStorageBuffer, .Int8, .Int16, .Int64, .Float64, .Float16 },
294 else => unreachable, // TODO
299 .vulkan => &.{ .Shader, .PhysicalStorageBufferAddresses, .StoragePushConstant16, .Int8, .Int16, .Int64, .Float64, .Float16 },
300 else => unreachable,
295301 };
296302
297303 for (caps) |cap| {
......@@ -299,19 +305,32 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
299305 .capability = cap,
300306 });
301307 }
308
309 switch (target.os.tag) {
310 .vulkan => {
311 try spv.sections.extensions.emit(gpa, .OpExtension, .{
312 .name = "SPV_KHR_physical_storage_buffer",
313 });
314 },
315 else => {},
316 }
302317}
303318
304319fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void {
305320 const gpa = spv.gpa;
306321
307 const addressing_model = switch (target.os.tag) {
322 const addressing_model: spec.AddressingModel = switch (target.os.tag) {
308323 .opencl => switch (target.cpu.arch) {
309 .spirv32 => spec.AddressingModel.Physical32,
310 .spirv64 => spec.AddressingModel.Physical64,
311 else => unreachable, // TODO
324 .spirv32 => .Physical32,
325 .spirv64 => .Physical64,
326 else => unreachable,
312327 },
313 .opengl, .vulkan => spec.AddressingModel.Logical,
314 else => unreachable, // TODO
328 .opengl, .vulkan => switch (target.cpu.arch) {
329 .spirv32 => .Logical, // TODO: I don't think this will ever be implemented.
330 .spirv64 => .PhysicalStorageBuffer64,
331 else => unreachable,
332 },
333 else => unreachable,
315334 };
316335
317336 const memory_model: spec.MemoryModel = switch (target.os.tag) {
src/link/SpirV/lower_invocation_globals.zig+9
......@@ -400,6 +400,15 @@ const ModuleBuilder = struct {
400400 self.section.writeWords(inst.operands[2..]);
401401 continue;
402402 },
403 .OpExecutionMode, .OpExecutionModeId => {
404 const original_id: ResultId = @enumFromInt(inst.operands[0]);
405 const new_id_index = info.entry_points.getIndex(original_id).?;
406 const new_id: ResultId = @enumFromInt(self.entry_point_new_id_base + new_id_index);
407 try self.section.emitRaw(self.arena, inst.opcode, inst.operands.len);
408 self.section.writeOperand(ResultId, new_id);
409 self.section.writeWords(inst.operands[1..]);
410 continue;
411 },
403412 .OpTypeFunction => {
404413 // Re-emitted in `emitFunctionTypes()`. We can do this because
405414 // OpTypeFunction's may not currently be used anywhere that is not
src/target.zig+25
......@@ -398,6 +398,31 @@ pub fn addrSpaceCastIsValid(
398398 }
399399}
400400
401/// Under SPIR-V with Vulkan, pointers are not 'real' (physical), but rather 'logical'. Effectively,
402/// this means that all such pointers have to be resolvable to a location at compile time, and places
403/// a number of restrictions on usage of such pointers. For example, a logical pointer may not be
404/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns
405/// for a particular architecture and address space wether such pointers are logical.
406pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool {
407 if (target.os.tag != .vulkan) {
408 return false;
409 }
410
411 return switch (as) {
412 // TODO: Vulkan doesn't support pointers in the generic address space, we
413 // should remove this case but this requires a change in defaultAddressSpace().
414 // For now, at least disable them from being regarded as physical.
415 .generic => true,
416 // For now, all global pointers are represented using PhysicalStorageBuffer, so these are real
417 // pointers.
418 .global => false,
419 // TODO: Allowed with VK_KHR_variable_pointers.
420 .shared => true,
421 .constant, .local, .input, .output, .uniform => true,
422 else => unreachable,
423 };
424}
425
401426pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 {
402427 // LLD does not support ELFv1. Rather than having LLVM produce ELFv1 code and then linking it
403428 // into a broken ELFv2 binary, just force LLVM to use ELFv2 as well. This will break when glibc
test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig+1-1
......@@ -10,4 +10,4 @@ comptime {
1010// backend=stage2
1111// target=native
1212//
13// :5:21: error: 'error.B' not a member of error set 'error{C,A}'
13// :5:21: error: 'error.B' not a member of error set 'error{A,C}'
test/cases/compile_errors/implicit_cast_of_error_set_not_a_subset.zig+1-1
......@@ -12,5 +12,5 @@ fn foo(set1: Set1) void {
1212// backend=stage2
1313// target=native
1414//
15// :7:21: error: expected type 'error{C,A}', found 'error{A,B}'
15// :7:21: error: expected type 'error{A,C}', found 'error{A,B}'
1616// :7:21: note: 'error.B' not a member of destination error set
test/cases/compile_errors/int_to_err_non_global_invalid_number.zig+1-1
......@@ -16,4 +16,4 @@ comptime {
1616// backend=llvm
1717// target=native
1818//
19// :11:21: error: 'error.B' not a member of error set 'error{C,A}'
19// :11:21: error: 'error.B' not a member of error set 'error{A,C}'
test/cases/compile_errors/spirv_merge_logical_pointers.zig created+19
......@@ -0,0 +1,19 @@
1export fn a() void {
2 var x: *i32 = undefined;
3 _ = &x;
4 var y: *i32 = undefined;
5 _ = &y;
6 var rt_cond = false;
7 _ = &rt_cond;
8
9 var z = if (rt_cond) x else y;
10 _ = &z;
11}
12
13// error
14// backend=stage2
15// target=spirv64-vulkan
16//
17// :9:13: error: value with non-mergable pointer type '*i32' depends on runtime control flow
18// :9:17: note: runtime control flow here
19// :9:13: note: pointers with address space 'generic' cannot be returned from a branch on target spirv-vulkan by compiler backend stage2_spirv64
test/cases/spirv_mergable_pointers.zig created+16
......@@ -0,0 +1,16 @@
1export fn a() void {
2 var x: *addrspace(.global) i32 = undefined;
3 _ = &x;
4 var y: *addrspace(.global) i32 = undefined;
5 _ = &y;
6 var rt_cond = false;
7 _ = &rt_cond;
8
9 var z = if (rt_cond) x else y;
10 _ = &z;
11}
12
13// compile
14// output_mode=Obj
15// backend=stage2
16// target=spirv64-vulkan
test/src/Cases.zig+1-1
......@@ -467,7 +467,7 @@ fn addFromDirInner(
467467 const target = resolved_target.result;
468468 for (backends) |backend| {
469469 if (backend == .stage2 and
470 target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64)
470 target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64 and target.cpu.arch != .spirv64)
471471 {
472472 // Other backends don't support new liveness format
473473 continue;