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 =...@@ -19,8 +19,7 @@ pub const simplified_logic =
19 builtin.zig_backend == .stage2_aarch64 or19 builtin.zig_backend == .stage2_aarch64 or
20 builtin.zig_backend == .stage2_arm or20 builtin.zig_backend == .stage2_arm or
21 builtin.zig_backend == .stage2_sparc64 or21 builtin.zig_backend == .stage2_sparc64 or
22 builtin.cpu.arch == .spirv32 or22 builtin.zig_backend == .stage2_spirv64;
23 builtin.cpu.arch == .spirv64;
2423
25comptime {24comptime {
26 // No matter what, we import the root file, so that any export, test, comptime25 // No matter what, we import the root file, so that any export, test, comptime
...@@ -37,7 +36,7 @@ comptime {...@@ -37,7 +36,7 @@ comptime {
37 if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) {36 if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) {
38 @export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });37 @export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
39 }38 }
40 } else if (builtin.os.tag == .opencl) {39 } else if (builtin.os.tag == .opencl or builtin.os.tag == .vulkan) {
41 if (@hasDecl(root, "main"))40 if (@hasDecl(root, "main"))
42 @export(&spirvMain2, .{ .name = "main" });41 @export(&spirvMain2, .{ .name = "main" });
43 } else {42 } else {
src/Sema.zig+36
...@@ -6253,6 +6253,9 @@ fn resolveAnalyzedBlock(...@@ -6253,6 +6253,9 @@ fn resolveAnalyzedBlock(
6253 for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| {6253 for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| {
6254 try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst);6254 try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst);
6255 }6255 }
6256
6257 try sema.checkMergeAllowed(child_block, type_src, resolved_ty);
6258
6256 const ty_inst = Air.internedToRef(resolved_ty.toIntern());6259 const ty_inst = Air.internedToRef(resolved_ty.toIntern());
6257 switch (block_tag) {6260 switch (block_tag) {
6258 .block => {6261 .block => {
...@@ -9688,6 +9691,39 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc:...@@ -9688,6 +9691,39 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc:
9688 }9691 }
9689}9692}
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
9691const Section = union(enum) {9727const Section = union(enum) {
9692 generic,9728 generic,
9693 default,9729 default,
src/Zcu.zig+2-5
...@@ -3639,11 +3639,8 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu...@@ -3639,11 +3639,8 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
3639 else => false,3639 else => false,
3640 },3640 },
3641 .stage2_spirv64 => switch (cc) {3641 .stage2_spirv64 => switch (cc) {
3642 .spirv_device,3642 .spirv_device, .spirv_kernel => true,
3643 .spirv_kernel,3643 .spirv_fragment, .spirv_vertex => target.os.tag == .vulkan,
3644 .spirv_fragment,
3645 .spirv_vertex,
3646 => true,
3647 else => false,3644 else => false,
3648 },3645 },
3649 };3646 };
src/codegen/spirv.zig+144-32
...@@ -169,6 +169,13 @@ pub const Object = struct {...@@ -169,6 +169,13 @@ pub const Object = struct {
169 /// via the usual `intern_map` mechanism.169 /// via the usual `intern_map` mechanism.
170 ptr_types: PtrTypeMap = .{},170 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
172 pub fn init(gpa: Allocator) Object {179 pub fn init(gpa: Allocator) Object {
173 return .{180 return .{
174 .gpa = gpa,181 .gpa = gpa,
...@@ -1640,13 +1647,18 @@ const NavGen = struct {...@@ -1640,13 +1647,18 @@ const NavGen = struct {
16401647
1641 comptime assert(zig_call_abi_ver == 3);1648 comptime assert(zig_call_abi_ver == 3);
1642 switch (fn_info.cc) {1649 switch (fn_info.cc) {
1643 .auto, .spirv_kernel, .spirv_fragment, .spirv_vertex => {},1650 .auto,
1644 else => @panic("TODO"),1651 .spirv_kernel,
1652 .spirv_fragment,
1653 .spirv_vertex,
1654 .spirv_device,
1655 => {},
1656 else => unreachable,
1645 }1657 }
16461658
1647 // TODO: Put this somewhere in Sema.zig1659 // Guaranteed by callConvSupportsVarArgs, there are nog SPIR-V CCs which support
1648 if (fn_info.is_var_args)1660 // varargs.
1649 return self.fail("VarArgs functions are unsupported for SPIR-V", .{});1661 assert(!fn_info.is_var_args);
16501662
1651 // Note: Logic is different from functionType().1663 // Note: Logic is different from functionType().
1652 const param_ty_ids = try self.gpa.alloc(IdRef, fn_info.param_types.len);1664 const param_ty_ids = try self.gpa.alloc(IdRef, fn_info.param_types.len);
...@@ -1838,11 +1850,16 @@ const NavGen = struct {...@@ -1838,11 +1850,16 @@ const NavGen = struct {
1838 return switch (as) {1850 return switch (as) {
1839 .generic => switch (target.os.tag) {1851 .generic => switch (target.os.tag) {
1840 .vulkan => .Private,1852 .vulkan => .Private,
1841 else => .Generic,1853 .opencl => .Generic,
1854 else => unreachable,
1842 },1855 },
1843 .shared => .Workgroup,1856 .shared => .Workgroup,
1844 .local => .Private,1857 .local => .Private,
1845 .global => .CrossWorkgroup,1858 .global => switch (target.os.tag) {
1859 .opencl => .CrossWorkgroup,
1860 .vulkan => .PhysicalStorageBuffer,
1861 else => unreachable,
1862 },
1846 .constant => .UniformConstant,1863 .constant => .UniformConstant,
1847 .input => .Input,1864 .input => .Input,
1848 .output => .Output,1865 .output => .Output,
...@@ -2898,30 +2915,118 @@ const NavGen = struct {...@@ -2898,30 +2915,118 @@ const NavGen = struct {
2898 .flags = .{ .address_space = .global },2915 .flags = .{ .address_space = .global },
2899 });2916 });
2900 const ptr_anyerror_ty_id = try self.resolveType(ptr_anyerror_ty, .direct);2917 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
2905 const spv_decl_index = try self.spv.allocDecl(.func);2919 const spv_decl_index = try self.spv.allocDecl(.func);
2906 const kernel_id = self.spv.declPtr(spv_decl_index).result_id;2920 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();
2909 const p_error_id = self.spv.allocId();2928 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;2933 try section.emit(self.spv.gpa, .OpFunction, .{
2912 try section.emit(self.spv.gpa, .OpFunction, .{2934 .id_result_type = try self.resolveType(Type.void, .direct),
2913 .id_result_type = try self.resolveType(Type.void, .direct),2935 .id_result = kernel_id,
2914 .id_result = kernel_id,2936 .function_control = .{},
2915 .function_control = .{},2937 .function_type = kernel_proto_ty_id,
2916 .function_type = kernel_proto_ty_id,2938 });
2917 });2939
2918 try section.emit(self.spv.gpa, .OpFunctionParameter, .{2940 try section.emit(self.spv.gpa, .OpFunctionParameter, .{
2919 .id_result_type = ptr_anyerror_ty_id,2941 .id_result_type = ptr_anyerror_ty_id,
2920 .id_result = p_error_id,2942 .id_result = p_error_id,
2921 });2943 });
2922 try section.emit(self.spv.gpa, .OpLabel, .{2944
2923 .id_result = self.spv.allocId(),2945 try section.emit(self.spv.gpa, .OpLabel, .{
2924 });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();
2925 try section.emit(self.spv.gpa, .OpFunctionCall, .{3030 try section.emit(self.spv.gpa, .OpFunctionCall, .{
2926 .id_result_type = anyerror_ty_id,3031 .id_result_type = anyerror_ty_id,
2927 .id_result = error_id,3032 .id_result = error_id,
...@@ -2931,17 +3036,25 @@ const NavGen = struct {...@@ -2931,17 +3036,25 @@ const NavGen = struct {
2931 try section.emit(self.spv.gpa, .OpStore, .{3036 try section.emit(self.spv.gpa, .OpStore, .{
2932 .pointer = p_error_id,3037 .pointer = p_error_id,
2933 .object = error_id,3038 .object = error_id,
3039 .memory_access = .{
3040 .Aligned = .{ .literal_integer = @sizeOf(u16) },
3041 },
2934 });3042 });
2935 try section.emit(self.spv.gpa, .OpReturn, {});3043 try section.emit(self.spv.gpa, .OpReturn, {});
2936 try section.emit(self.spv.gpa, .OpFunctionEnd, {});3044 try section.emit(self.spv.gpa, .OpFunctionEnd, {});
29373045
2938 try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index});
2939
2940 // Just generate a quick other name because the intel runtime crashes when the entry-3046 // Just generate a quick other name because the intel runtime crashes when the entry-
2941 // point name is the same as a different OpName.3047 // point name is the same as a different OpName.
2942 const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name});3048 const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name});
2943 defer self.gpa.free(test_name);3049 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);
2945 }3058 }
29463059
2947 fn genNav(self: *NavGen, do_codegen: bool) !void {3060 fn genNav(self: *NavGen, do_codegen: bool) !void {
...@@ -2969,11 +3082,10 @@ const NavGen = struct {...@@ -2969,11 +3082,10 @@ const NavGen = struct {
2969 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{3082 try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
2970 .id_result_type = return_ty_id,3083 .id_result_type = return_ty_id,
2971 .id_result = result_id,3084 .id_result = result_id,
2972 .function_control = switch (fn_info.cc) {
2973 .@"inline" => .{ .Inline = true },
2974 else => .{},
2975 },
2976 .function_type = prototype_ty_id,3085 .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 = .{},
2977 });3089 });
29783090
2979 comptime assert(zig_call_abi_ver == 3);3091 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 {...@@ -3845,6 +3845,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
3845 }3845 }
3846 if (global_error_set_names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null));3846 if (global_error_set_names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null));
3847 try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items);3847 try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items);
3848 try wip_nav.flush(.unneeded);
3848 }3849 }
38493850
3850 {3851 {
src/link/SpirV.zig+47-28
...@@ -161,28 +161,35 @@ pub fn updateExports(...@@ -161,28 +161,35 @@ pub fn updateExports(
161 },161 },
162 };162 };
163 const nav_ty = ip.getNav(nav_index).typeOf(ip);163 const nav_ty = ip.getNav(nav_index).typeOf(ip);
164 const target = zcu.getTarget();
164 if (ip.isFunctionType(nav_ty)) {165 if (ip.isFunctionType(nav_ty)) {
165 const target = zcu.getTarget();
166 const spv_decl_index = try self.object.resolveNav(zcu, nav_index);166 const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
167 const execution_model = switch (Type.fromInterned(nav_ty).fnCallingConvention(zcu)) {167 const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
168 .spirv_vertex => spec.ExecutionModel.Vertex,168 const execution_model: spec.ExecutionModel = switch (target.os.tag) {
169 .spirv_fragment => spec.ExecutionModel.Fragment,169 .vulkan => switch (cc) {
170 .spirv_kernel => spec.ExecutionModel.Kernel,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 },
171 else => unreachable,183 else => unreachable,
172 };184 };
173 const is_vulkan = target.os.tag == .vulkan;185
174186 for (export_indices) |export_idx| {
175 if ((!is_vulkan and execution_model == .Kernel) or187 const exp = zcu.all_exports.items[export_idx];
176 (is_vulkan and (execution_model == .Fragment or execution_model == .Vertex)))188 try self.object.spv.declareEntryPoint(
177 {189 spv_decl_index,
178 for (export_indices) |export_idx| {190 exp.opts.name.toSlice(ip),
179 const exp = zcu.all_exports.items[export_idx];191 execution_model,
180 try self.object.spv.declareEntryPoint(192 );
181 spv_decl_index,
182 exp.opts.name.toSlice(ip),
183 execution_model,
184 );
185 }
186 }193 }
187 }194 }
188195
...@@ -258,7 +265,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n...@@ -258,7 +265,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
258 const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {265 const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
259 error.OutOfMemory => return error.OutOfMemory,266 error.OutOfMemory => return error.OutOfMemory,
260 else => |other| {267 else => |other| {
261 log.err("error while linking: {s}\n", .{@errorName(other)});268 log.err("error while linking: {s}", .{@errorName(other)});
262 return error.FlushFailure;269 return error.FlushFailure;
263 },270 },
264 };271 };
...@@ -289,9 +296,8 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {...@@ -289,9 +296,8 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
289 // TODO: Integrate with a hypothetical feature system296 // TODO: Integrate with a hypothetical feature system
290 const caps: []const spec.Capability = switch (target.os.tag) {297 const caps: []const spec.Capability = switch (target.os.tag) {
291 .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .Vector16, .GenericPointer },298 .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .Vector16, .GenericPointer },
292 .opengl => &.{.Shader},299 .vulkan => &.{ .Shader, .PhysicalStorageBufferAddresses, .StoragePushConstant16, .Int8, .Int16, .Int64, .Float64, .Float16 },
293 .vulkan => &.{ .Shader, .VariablePointersStorageBuffer, .Int8, .Int16, .Int64, .Float64, .Float16 },300 else => unreachable,
294 else => unreachable, // TODO
295 };301 };
296302
297 for (caps) |cap| {303 for (caps) |cap| {
...@@ -299,19 +305,32 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {...@@ -299,19 +305,32 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
299 .capability = cap,305 .capability = cap,
300 });306 });
301 }307 }
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 }
302}317}
303318
304fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void {319fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void {
305 const gpa = spv.gpa;320 const gpa = spv.gpa;
306321
307 const addressing_model = switch (target.os.tag) {322 const addressing_model: spec.AddressingModel = switch (target.os.tag) {
308 .opencl => switch (target.cpu.arch) {323 .opencl => switch (target.cpu.arch) {
309 .spirv32 => spec.AddressingModel.Physical32,324 .spirv32 => .Physical32,
310 .spirv64 => spec.AddressingModel.Physical64,325 .spirv64 => .Physical64,
311 else => unreachable, // TODO326 else => unreachable,
312 },327 },
313 .opengl, .vulkan => spec.AddressingModel.Logical,328 .opengl, .vulkan => switch (target.cpu.arch) {
314 else => unreachable, // TODO329 .spirv32 => .Logical, // TODO: I don't think this will ever be implemented.
330 .spirv64 => .PhysicalStorageBuffer64,
331 else => unreachable,
332 },
333 else => unreachable,
315 };334 };
316335
317 const memory_model: spec.MemoryModel = switch (target.os.tag) {336 const memory_model: spec.MemoryModel = switch (target.os.tag) {
src/link/SpirV/lower_invocation_globals.zig+9
...@@ -400,6 +400,15 @@ const ModuleBuilder = struct {...@@ -400,6 +400,15 @@ const ModuleBuilder = struct {
400 self.section.writeWords(inst.operands[2..]);400 self.section.writeWords(inst.operands[2..]);
401 continue;401 continue;
402 },402 },
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 },
403 .OpTypeFunction => {412 .OpTypeFunction => {
404 // Re-emitted in `emitFunctionTypes()`. We can do this because413 // Re-emitted in `emitFunctionTypes()`. We can do this because
405 // OpTypeFunction's may not currently be used anywhere that is not414 // OpTypeFunction's may not currently be used anywhere that is not
src/target.zig+25
...@@ -398,6 +398,31 @@ pub fn addrSpaceCastIsValid(...@@ -398,6 +398,31 @@ pub fn addrSpaceCastIsValid(
398 }398 }
399}399}
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
401pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 {426pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 {
402 // LLD does not support ELFv1. Rather than having LLVM produce ELFv1 code and then linking it427 // LLD does not support ELFv1. Rather than having LLVM produce ELFv1 code and then linking it
403 // into a broken ELFv2 binary, just force LLVM to use ELFv2 as well. This will break when glibc428 // 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 {...@@ -10,4 +10,4 @@ comptime {
10// backend=stage210// backend=stage2
11// target=native11// target=native
12//12//
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 {...@@ -12,5 +12,5 @@ fn foo(set1: Set1) void {
12// backend=stage212// backend=stage2
13// target=native13// target=native
14//14//
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}'
16// :7:21: note: 'error.B' not a member of destination error set16// :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 {...@@ -16,4 +16,4 @@ comptime {
16// backend=llvm16// backend=llvm
17// target=native17// target=native
18//18//
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(...@@ -467,7 +467,7 @@ fn addFromDirInner(
467 const target = resolved_target.result;467 const target = resolved_target.result;
468 for (backends) |backend| {468 for (backends) |backend| {
469 if (backend == .stage2 and469 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)
471 {471 {
472 // Other backends don't support new liveness format472 // Other backends don't support new liveness format
473 continue;473 continue;