| author | |
| committer | |
| log | 3450809e3db14226b7f8e57bdfa7fde590e803d8 |
| tree | a1100c41f7a5be7c9f608e7b0316a42523f2b36c |
| parent | 7025c06eb6102117ec472cac4586a3841bdb6fa8 |
| parent | ae57f6fd07d1830f9179222207671d30d9f9d707 |
| signature |
spirv: vulkan setup14 files changed, 305 insertions(+), 72 deletions(-)
lib/std/start.zig+2-3| ... | ... | @@ -19,8 +19,7 @@ pub const simplified_logic = |
| 19 | 19 | builtin.zig_backend == .stage2_aarch64 or |
| 20 | 20 | builtin.zig_backend == .stage2_arm or |
| 21 | 21 | builtin.zig_backend == .stage2_sparc64 or |
| 22 | builtin.cpu.arch == .spirv32 or | |
| 23 | builtin.cpu.arch == .spirv64; | |
| 22 | builtin.zig_backend == .stage2_spirv64; | |
| 24 | 23 | |
| 25 | 24 | comptime { |
| 26 | 25 | // No matter what, we import the root file, so that any export, test, comptime |
| ... | ... | @@ -37,7 +36,7 @@ comptime { |
| 37 | 36 | if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) { |
| 38 | 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 | 40 | if (@hasDecl(root, "main")) |
| 42 | 41 | @export(&spirvMain2, .{ .name = "main" }); |
| 43 | 42 | } else { |
src/Sema.zig+36| ... | ... | @@ -6253,6 +6253,9 @@ fn resolveAnalyzedBlock( |
| 6253 | 6253 | for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| { |
| 6254 | 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 | 6259 | const ty_inst = Air.internedToRef(resolved_ty.toIntern()); |
| 6257 | 6260 | switch (block_tag) { |
| 6258 | 6261 | .block => { |
| ... | ... | @@ -9688,6 +9691,39 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: |
| 9688 | 9691 | } |
| 9689 | 9692 | } |
| 9690 | 9693 | |
| 9694 | fn 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 | ||
| 9691 | 9727 | const Section = union(enum) { |
| 9692 | 9728 | generic, |
| 9693 | 9729 | default, |
src/Zcu.zig+2-5| ... | ... | @@ -3639,11 +3639,8 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu |
| 3639 | 3639 | else => false, |
| 3640 | 3640 | }, |
| 3641 | 3641 | .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, | |
| 3647 | 3644 | else => false, |
| 3648 | 3645 | }, |
| 3649 | 3646 | }; |
src/codegen/spirv.zig+144-32| ... | ... | @@ -169,6 +169,13 @@ pub const Object = struct { |
| 169 | 169 | /// via the usual `intern_map` mechanism. |
| 170 | 170 | ptr_types: PtrTypeMap = .{}, |
| 171 | 171 | |
| 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 | 179 | pub fn init(gpa: Allocator) Object { |
| 173 | 180 | return .{ |
| 174 | 181 | .gpa = gpa, |
| ... | ... | @@ -1640,13 +1647,18 @@ const NavGen = struct { |
| 1640 | 1647 | |
| 1641 | 1648 | comptime assert(zig_call_abi_ver == 3); |
| 1642 | 1649 | 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, | |
| 1645 | 1657 | } |
| 1646 | 1658 | |
| 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); | |
| 1650 | 1662 | |
| 1651 | 1663 | // Note: Logic is different from functionType(). |
| 1652 | 1664 | const param_ty_ids = try self.gpa.alloc(IdRef, fn_info.param_types.len); |
| ... | ... | @@ -1838,11 +1850,16 @@ const NavGen = struct { |
| 1838 | 1850 | return switch (as) { |
| 1839 | 1851 | .generic => switch (target.os.tag) { |
| 1840 | 1852 | .vulkan => .Private, |
| 1841 | else => .Generic, | |
| 1853 | .opencl => .Generic, | |
| 1854 | else => unreachable, | |
| 1842 | 1855 | }, |
| 1843 | 1856 | .shared => .Workgroup, |
| 1844 | 1857 | .local => .Private, |
| 1845 | .global => .CrossWorkgroup, | |
| 1858 | .global => switch (target.os.tag) { | |
| 1859 | .opencl => .CrossWorkgroup, | |
| 1860 | .vulkan => .PhysicalStorageBuffer, | |
| 1861 | else => unreachable, | |
| 1862 | }, | |
| 1846 | 1863 | .constant => .UniformConstant, |
| 1847 | 1864 | .input => .Input, |
| 1848 | 1865 | .output => .Output, |
| ... | ... | @@ -2898,30 +2915,118 @@ const NavGen = struct { |
| 2898 | 2915 | .flags = .{ .address_space = .global }, |
| 2899 | 2916 | }); |
| 2900 | 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; | |
| 2904 | 2918 | |
| 2905 | 2919 | const spv_decl_index = try self.spv.allocDecl(.func); |
| 2906 | 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(); | |
| 2907 | 2927 | |
| 2908 | const error_id = self.spv.allocId(); | |
| 2909 | 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}); | |
| 2910 | 2932 | |
| 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(); | |
| 2925 | 3030 | try section.emit(self.spv.gpa, .OpFunctionCall, .{ |
| 2926 | 3031 | .id_result_type = anyerror_ty_id, |
| 2927 | 3032 | .id_result = error_id, |
| ... | ... | @@ -2931,17 +3036,25 @@ const NavGen = struct { |
| 2931 | 3036 | try section.emit(self.spv.gpa, .OpStore, .{ |
| 2932 | 3037 | .pointer = p_error_id, |
| 2933 | 3038 | .object = error_id, |
| 3039 | .memory_access = .{ | |
| 3040 | .Aligned = .{ .literal_integer = @sizeOf(u16) }, | |
| 3041 | }, | |
| 2934 | 3042 | }); |
| 2935 | 3043 | try section.emit(self.spv.gpa, .OpReturn, {}); |
| 2936 | 3044 | try section.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| 2937 | 3045 | |
| 2938 | try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index}); | |
| 2939 | ||
| 2940 | 3046 | // Just generate a quick other name because the intel runtime crashes when the entry- |
| 2941 | 3047 | // point name is the same as a different OpName. |
| 2942 | 3048 | const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name}); |
| 2943 | 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 | } |
| 2946 | 3059 | |
| 2947 | 3060 | fn genNav(self: *NavGen, do_codegen: bool) !void { |
| ... | ... | @@ -2969,11 +3082,10 @@ const NavGen = struct { |
| 2969 | 3082 | try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ |
| 2970 | 3083 | .id_result_type = return_ty_id, |
| 2971 | 3084 | .id_result = result_id, |
| 2972 | .function_control = switch (fn_info.cc) { | |
| 2973 | .@"inline" => .{ .Inline = true }, | |
| 2974 | else => .{}, | |
| 2975 | }, | |
| 2976 | 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 | }); |
| 2978 | 3090 | |
| 2979 | 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 | 3845 | } |
| 3846 | 3846 | if (global_error_set_names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null)); |
| 3847 | 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 | } |
| 3849 | 3850 | |
| 3850 | 3851 | { |
src/link/SpirV.zig+47-28| ... | ... | @@ -161,28 +161,35 @@ pub fn updateExports( |
| 161 | 161 | }, |
| 162 | 162 | }; |
| 163 | 163 | const nav_ty = ip.getNav(nav_index).typeOf(ip); |
| 164 | const target = zcu.getTarget(); | |
| 164 | 165 | if (ip.isFunctionType(nav_ty)) { |
| 165 | const target = zcu.getTarget(); | |
| 166 | 166 | 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 | }, | |
| 171 | 183 | else => unreachable, |
| 172 | 184 | }; |
| 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 | ); | |
| 186 | 193 | } |
| 187 | 194 | } |
| 188 | 195 | |
| ... | ... | @@ -258,7 +265,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n |
| 258 | 265 | const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) { |
| 259 | 266 | error.OutOfMemory => return error.OutOfMemory, |
| 260 | 267 | else => |other| { |
| 261 | log.err("error while linking: {s}\n", .{@errorName(other)}); | |
| 268 | log.err("error while linking: {s}", .{@errorName(other)}); | |
| 262 | 269 | return error.FlushFailure; |
| 263 | 270 | }, |
| 264 | 271 | }; |
| ... | ... | @@ -289,9 +296,8 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { |
| 289 | 296 | // TODO: Integrate with a hypothetical feature system |
| 290 | 297 | const caps: []const spec.Capability = switch (target.os.tag) { |
| 291 | 298 | .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, | |
| 295 | 301 | }; |
| 296 | 302 | |
| 297 | 303 | for (caps) |cap| { |
| ... | ... | @@ -299,19 +305,32 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { |
| 299 | 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 | } |
| 303 | 318 | |
| 304 | 319 | fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { |
| 305 | 320 | const gpa = spv.gpa; |
| 306 | 321 | |
| 307 | const addressing_model = switch (target.os.tag) { | |
| 322 | const addressing_model: spec.AddressingModel = switch (target.os.tag) { | |
| 308 | 323 | .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, | |
| 312 | 327 | }, |
| 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, | |
| 315 | 334 | }; |
| 316 | 335 | |
| 317 | 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 | 400 | self.section.writeWords(inst.operands[2..]); |
| 401 | 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 | 412 | .OpTypeFunction => { |
| 404 | 413 | // Re-emitted in `emitFunctionTypes()`. We can do this because |
| 405 | 414 | // OpTypeFunction's may not currently be used anywhere that is not |
src/target.zig+25| ... | ... | @@ -398,6 +398,31 @@ pub fn addrSpaceCastIsValid( |
| 398 | 398 | } |
| 399 | 399 | } |
| 400 | 400 | |
| 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. | |
| 406 | pub 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 | ||
| 401 | 426 | pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 { |
| 402 | 427 | // LLD does not support ELFv1. Rather than having LLVM produce ELFv1 code and then linking it |
| 403 | 428 | // 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 | 10 | // backend=stage2 |
| 11 | 11 | // 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 | 12 | // backend=stage2 |
| 13 | 13 | // 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 | 16 | // :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 | 16 | // backend=llvm |
| 17 | 17 | // 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 @@ |
| 1 | export 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 @@ |
| 1 | export 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 | 467 | const target = resolved_target.result; |
| 468 | 468 | for (backends) |backend| { |
| 469 | 469 | 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 | 472 | // Other backends don't support new liveness format |
| 473 | 473 | continue; |