| ... | ... | @@ -451,12 +451,12 @@ const DeclGen = struct { |
| 451 | 451 | const spv_decl_index = blk: { |
| 452 | 452 | const entry = try self.object.anon_decl_link.getOrPut(self.object.gpa, .{ val, storage_class }); |
| 453 | 453 | if (entry.found_existing) { |
| 454 | | try self.func.decl_deps.put(self.spv.gpa, entry.value_ptr.*, {}); |
| 454 | try self.addFunctionDep(entry.value_ptr.*, storage_class); |
| 455 | 455 | return self.spv.declPtr(entry.value_ptr.*).result_id; |
| 456 | 456 | } |
| 457 | 457 | |
| 458 | 458 | const spv_decl_index = try self.spv.allocDecl(.global); |
| 459 | | try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); |
| 459 | try self.addFunctionDep(spv_decl_index, storage_class); |
| 460 | 460 | entry.value_ptr.* = spv_decl_index; |
| 461 | 461 | break :blk spv_decl_index; |
| 462 | 462 | }; |
| ... | ... | @@ -529,6 +529,37 @@ const DeclGen = struct { |
| 529 | 529 | return var_id; |
| 530 | 530 | } |
| 531 | 531 | |
| 532 | fn addFunctionDep(self: *DeclGen, decl_index: SpvModule.Decl.Index, storage_class: StorageClass) !void { |
| 533 | const target = self.getTarget(); |
| 534 | if (target.os.tag == .vulkan) { |
| 535 | // Shader entry point dependencies must be variables with Input or Output storage class |
| 536 | switch (storage_class) { |
| 537 | .Input, .Output => { |
| 538 | try self.func.decl_deps.put(self.spv.gpa, decl_index, {}); |
| 539 | }, |
| 540 | else => {}, |
| 541 | } |
| 542 | } else { |
| 543 | try self.func.decl_deps.put(self.spv.gpa, decl_index, {}); |
| 544 | } |
| 545 | } |
| 546 | |
| 547 | fn castToGeneric(self: *DeclGen, type_id: IdRef, ptr_id: IdRef) !IdRef { |
| 548 | const target = self.getTarget(); |
| 549 | |
| 550 | if (target.os.tag == .vulkan) { |
| 551 | return ptr_id; |
| 552 | } else { |
| 553 | const result_id = self.spv.allocId(); |
| 554 | try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 555 | .id_result_type = type_id, |
| 556 | .id_result = result_id, |
| 557 | .pointer = ptr_id, |
| 558 | }); |
| 559 | return result_id; |
| 560 | } |
| 561 | } |
| 562 | |
| 532 | 563 | /// Start a new SPIR-V block, Emits the label of the new block, and stores which |
| 533 | 564 | /// block we are currently generating. |
| 534 | 565 | /// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to |
| ... | ... | @@ -1019,7 +1050,7 @@ const DeclGen = struct { |
| 1019 | 1050 | |
| 1020 | 1051 | // TODO: Can we consolidate this in ptrElemPtr? |
| 1021 | 1052 | const elem_ty = parent_ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T. |
| 1022 | | const elem_ptr_ty_ref = try self.ptrType(elem_ty, spvStorageClass(parent_ptr_ty.ptrAddressSpace(mod))); |
| 1053 | const elem_ptr_ty_ref = try self.ptrType(elem_ty, self.spvStorageClass(parent_ptr_ty.ptrAddressSpace(mod))); |
| 1023 | 1054 | |
| 1024 | 1055 | if (elem_ptr_ty_ref == result_ty_ref) { |
| 1025 | 1056 | return elem_ptr_id; |
| ... | ... | @@ -1074,7 +1105,7 @@ const DeclGen = struct { |
| 1074 | 1105 | unreachable; // TODO |
| 1075 | 1106 | } |
| 1076 | 1107 | |
| 1077 | | const final_storage_class = spvStorageClass(ty.ptrAddressSpace(mod)); |
| 1108 | const final_storage_class = self.spvStorageClass(ty.ptrAddressSpace(mod)); |
| 1078 | 1109 | const actual_storage_class = switch (final_storage_class) { |
| 1079 | 1110 | .Generic => .CrossWorkgroup, |
| 1080 | 1111 | else => |other| other, |
| ... | ... | @@ -1084,15 +1115,7 @@ const DeclGen = struct { |
| 1084 | 1115 | const decl_ptr_ty_ref = try self.ptrType(decl_ty, final_storage_class); |
| 1085 | 1116 | |
| 1086 | 1117 | const ptr_id = switch (final_storage_class) { |
| 1087 | | .Generic => blk: { |
| 1088 | | const result_id = self.spv.allocId(); |
| 1089 | | try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1090 | | .id_result_type = self.typeId(decl_ptr_ty_ref), |
| 1091 | | .id_result = result_id, |
| 1092 | | .pointer = decl_id, |
| 1093 | | }); |
| 1094 | | break :blk result_id; |
| 1095 | | }, |
| 1118 | .Generic => try self.castToGeneric(self.typeId(decl_ptr_ty_ref), decl_id), |
| 1096 | 1119 | else => decl_id, |
| 1097 | 1120 | }; |
| 1098 | 1121 | |
| ... | ... | @@ -1115,6 +1138,7 @@ const DeclGen = struct { |
| 1115 | 1138 | const ty_ref = try self.resolveType(ty, .direct); |
| 1116 | 1139 | const ty_id = self.typeId(ty_ref); |
| 1117 | 1140 | const decl = mod.declPtr(decl_index); |
| 1141 | |
| 1118 | 1142 | switch (mod.intern_pool.indexToKey(decl.val.ip_index)) { |
| 1119 | 1143 | .func => { |
| 1120 | 1144 | // TODO: Properly lower function pointers. For now we are going to hack around it and |
| ... | ... | @@ -1133,23 +1157,13 @@ const DeclGen = struct { |
| 1133 | 1157 | const spv_decl_index = try self.object.resolveDecl(mod, decl_index); |
| 1134 | 1158 | |
| 1135 | 1159 | const decl_id = self.spv.declPtr(spv_decl_index).result_id; |
| 1136 | | try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {}); |
| 1137 | | |
| 1138 | | const final_storage_class = spvStorageClass(decl.@"addrspace"); |
| 1160 | const final_storage_class = self.spvStorageClass(decl.@"addrspace"); |
| 1161 | try self.addFunctionDep(spv_decl_index, final_storage_class); |
| 1139 | 1162 | |
| 1140 | 1163 | const decl_ptr_ty_ref = try self.ptrType(decl.ty, final_storage_class); |
| 1141 | 1164 | |
| 1142 | 1165 | const ptr_id = switch (final_storage_class) { |
| 1143 | | .Generic => blk: { |
| 1144 | | // Pointer should be Generic, but is actually placed in CrossWorkgroup. |
| 1145 | | const result_id = self.spv.allocId(); |
| 1146 | | try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 1147 | | .id_result_type = self.typeId(decl_ptr_ty_ref), |
| 1148 | | .id_result = result_id, |
| 1149 | | .pointer = decl_id, |
| 1150 | | }); |
| 1151 | | break :blk result_id; |
| 1152 | | }, |
| 1166 | .Generic => try self.castToGeneric(self.typeId(decl_ptr_ty_ref), decl_id), |
| 1153 | 1167 | else => decl_id, |
| 1154 | 1168 | }; |
| 1155 | 1169 | |
| ... | ... | @@ -1195,8 +1209,12 @@ const DeclGen = struct { |
| 1195 | 1209 | // An array of largestSupportedIntBits. |
| 1196 | 1210 | return self.todo("Implement {s} composite int type of {} bits", .{ @tagName(signedness), bits }); |
| 1197 | 1211 | }; |
| 1212 | |
| 1198 | 1213 | // Kernel only supports unsigned ints. |
| 1199 | | // TODO: Only do this with Kernels |
| 1214 | if (self.getTarget().os.tag == .vulkan) { |
| 1215 | return self.spv.intType(signedness, backing_bits); |
| 1216 | } |
| 1217 | |
| 1200 | 1218 | return self.spv.intType(.unsigned, backing_bits); |
| 1201 | 1219 | } |
| 1202 | 1220 | |
| ... | ... | @@ -1453,7 +1471,7 @@ const DeclGen = struct { |
| 1453 | 1471 | // Note: Don't cache this pointer type, it would mess up the recursive pointer functionality |
| 1454 | 1472 | // in ptrType()! |
| 1455 | 1473 | |
| 1456 | | const storage_class = spvStorageClass(ptr_info.flags.address_space); |
| 1474 | const storage_class = self.spvStorageClass(ptr_info.flags.address_space); |
| 1457 | 1475 | const ptr_ty_ref = try self.ptrType(Type.fromInterned(ptr_info.child), storage_class); |
| 1458 | 1476 | |
| 1459 | 1477 | if (ptr_info.flags.size != .Slice) { |
| ... | ... | @@ -1634,13 +1652,20 @@ const DeclGen = struct { |
| 1634 | 1652 | } |
| 1635 | 1653 | } |
| 1636 | 1654 | |
| 1637 | | fn spvStorageClass(as: std.builtin.AddressSpace) StorageClass { |
| 1655 | fn spvStorageClass(self: *DeclGen, as: std.builtin.AddressSpace) StorageClass { |
| 1656 | const target = self.getTarget(); |
| 1638 | 1657 | return switch (as) { |
| 1639 | | .generic => .Generic, |
| 1658 | .generic => switch (target.os.tag) { |
| 1659 | .vulkan => .Private, |
| 1660 | else => .Generic, |
| 1661 | }, |
| 1640 | 1662 | .shared => .Workgroup, |
| 1641 | 1663 | .local => .Private, |
| 1642 | 1664 | .global => .CrossWorkgroup, |
| 1643 | 1665 | .constant => .UniformConstant, |
| 1666 | .input => .Input, |
| 1667 | .output => .Output, |
| 1668 | .uniform => .Uniform, |
| 1644 | 1669 | .gs, |
| 1645 | 1670 | .fs, |
| 1646 | 1671 | .ss, |
| ... | ... | @@ -1920,7 +1945,7 @@ const DeclGen = struct { |
| 1920 | 1945 | // point name is the same as a different OpName. |
| 1921 | 1946 | const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name}); |
| 1922 | 1947 | defer self.gpa.free(test_name); |
| 1923 | | try self.spv.declareEntryPoint(spv_decl_index, test_name); |
| 1948 | try self.spv.declareEntryPoint(spv_decl_index, test_name, .Kernel); |
| 1924 | 1949 | } |
| 1925 | 1950 | |
| 1926 | 1951 | fn genDecl(self: *DeclGen) !void { |
| ... | ... | @@ -1928,6 +1953,7 @@ const DeclGen = struct { |
| 1928 | 1953 | const ip = &mod.intern_pool; |
| 1929 | 1954 | const decl = mod.declPtr(self.decl_index); |
| 1930 | 1955 | const spv_decl_index = try self.object.resolveDecl(mod, self.decl_index); |
| 1956 | const target = self.getTarget(); |
| 1931 | 1957 | |
| 1932 | 1958 | const decl_id = self.spv.declPtr(spv_decl_index).result_id; |
| 1933 | 1959 | |
| ... | ... | @@ -1994,30 +2020,24 @@ const DeclGen = struct { |
| 1994 | 2020 | try self.generateTestEntryPoint(fqn, spv_decl_index); |
| 1995 | 2021 | } |
| 1996 | 2022 | } else { |
| 1997 | | const init_val = if (decl.val.getVariable(mod)) |payload| |
| 1998 | | Value.fromInterned(payload.init) |
| 1999 | | else |
| 2000 | | decl.val; |
| 2001 | | |
| 2002 | | if (init_val.ip_index == .unreachable_value) { |
| 2003 | | return self.todo("importing extern variables", .{}); |
| 2004 | | } |
| 2005 | | |
| 2006 | | // Currently, initializers for CrossWorkgroup variables is not implemented |
| 2007 | | // in Mesa. Therefore we generate an initialization kernel instead. |
| 2008 | | |
| 2009 | | const void_ty_ref = try self.resolveType(Type.void, .direct); |
| 2010 | | |
| 2011 | | const initializer_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{ |
| 2012 | | .return_type = void_ty_ref, |
| 2013 | | .parameters = &.{}, |
| 2014 | | } }); |
| 2023 | const opt_init_val: ?Value = blk: { |
| 2024 | if (decl.val.getVariable(mod)) |payload| { |
| 2025 | if (payload.is_extern) break :blk null; |
| 2026 | break :blk Value.fromInterned(payload.init); |
| 2027 | } |
| 2028 | break :blk decl.val; |
| 2029 | }; |
| 2015 | 2030 | |
| 2016 | 2031 | // Generate the actual variable for the global... |
| 2017 | | const final_storage_class = spvStorageClass(decl.@"addrspace"); |
| 2018 | | const actual_storage_class = switch (final_storage_class) { |
| 2019 | | .Generic => .CrossWorkgroup, |
| 2020 | | else => final_storage_class, |
| 2032 | const final_storage_class = self.spvStorageClass(decl.@"addrspace"); |
| 2033 | const actual_storage_class = blk: { |
| 2034 | if (target.os.tag != .vulkan) { |
| 2035 | break :blk switch (final_storage_class) { |
| 2036 | .Generic => .CrossWorkgroup, |
| 2037 | else => final_storage_class, |
| 2038 | }; |
| 2039 | } |
| 2040 | break :blk final_storage_class; |
| 2021 | 2041 | }; |
| 2022 | 2042 | |
| 2023 | 2043 | const ptr_ty_ref = try self.ptrType(decl.ty, actual_storage_class); |
| ... | ... | @@ -2028,37 +2048,51 @@ const DeclGen = struct { |
| 2028 | 2048 | .id_result = decl_id, |
| 2029 | 2049 | .storage_class = actual_storage_class, |
| 2030 | 2050 | }); |
| 2051 | const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); |
| 2052 | try self.spv.debugName(decl_id, fqn); |
| 2031 | 2053 | |
| 2032 | | // Now emit the instructions that initialize the variable. |
| 2033 | | const initializer_id = self.spv.allocId(); |
| 2034 | | try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ |
| 2035 | | .id_result_type = self.typeId(void_ty_ref), |
| 2036 | | .id_result = initializer_id, |
| 2037 | | .function_control = .{}, |
| 2038 | | .function_type = self.typeId(initializer_proto_ty_ref), |
| 2039 | | }); |
| 2040 | | const root_block_id = self.spv.allocId(); |
| 2041 | | try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ |
| 2042 | | .id_result = root_block_id, |
| 2043 | | }); |
| 2044 | | self.current_block_label = root_block_id; |
| 2054 | if (opt_init_val) |init_val| { |
| 2055 | // Currently, initializers for CrossWorkgroup variables is not implemented |
| 2056 | // in Mesa. Therefore we generate an initialization kernel instead. |
| 2057 | const void_ty_ref = try self.resolveType(Type.void, .direct); |
| 2045 | 2058 | |
| 2046 | | const val_id = try self.constant(decl.ty, init_val, .indirect); |
| 2047 | | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 2048 | | .pointer = decl_id, |
| 2049 | | .object = val_id, |
| 2050 | | }); |
| 2059 | const initializer_proto_ty_ref = try self.spv.resolve(.{ .function_type = .{ |
| 2060 | .return_type = void_ty_ref, |
| 2061 | .parameters = &.{}, |
| 2062 | } }); |
| 2051 | 2063 | |
| 2052 | | // TODO: We should be able to get rid of this by now... |
| 2053 | | self.spv.endGlobal(spv_decl_index, begin, decl_id, initializer_id); |
| 2064 | // Now emit the instructions that initialize the variable. |
| 2065 | const initializer_id = self.spv.allocId(); |
| 2066 | try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ |
| 2067 | .id_result_type = self.typeId(void_ty_ref), |
| 2068 | .id_result = initializer_id, |
| 2069 | .function_control = .{}, |
| 2070 | .function_type = self.typeId(initializer_proto_ty_ref), |
| 2071 | }); |
| 2072 | const root_block_id = self.spv.allocId(); |
| 2073 | try self.func.prologue.emit(self.spv.gpa, .OpLabel, .{ |
| 2074 | .id_result = root_block_id, |
| 2075 | }); |
| 2076 | self.current_block_label = root_block_id; |
| 2054 | 2077 | |
| 2055 | | try self.func.body.emit(self.spv.gpa, .OpReturn, {}); |
| 2056 | | try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| 2057 | | try self.spv.addFunction(spv_decl_index, self.func); |
| 2078 | const val_id = try self.constant(decl.ty, init_val, .indirect); |
| 2079 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| 2080 | .pointer = decl_id, |
| 2081 | .object = val_id, |
| 2082 | }); |
| 2058 | 2083 | |
| 2059 | | const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); |
| 2060 | | try self.spv.debugName(decl_id, fqn); |
| 2061 | | try self.spv.debugNameFmt(initializer_id, "initializer of {s}", .{fqn}); |
| 2084 | // TODO: We should be able to get rid of this by now... |
| 2085 | self.spv.endGlobal(spv_decl_index, begin, decl_id, initializer_id); |
| 2086 | |
| 2087 | try self.func.body.emit(self.spv.gpa, .OpReturn, {}); |
| 2088 | try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); |
| 2089 | try self.spv.addFunction(spv_decl_index, self.func); |
| 2090 | |
| 2091 | try self.spv.debugNameFmt(initializer_id, "initializer of {s}", .{fqn}); |
| 2092 | } else { |
| 2093 | self.spv.endGlobal(spv_decl_index, begin, decl_id, null); |
| 2094 | try self.spv.declareDeclDeps(spv_decl_index, &.{}); |
| 2095 | } |
| 2062 | 2096 | } |
| 2063 | 2097 | } |
| 2064 | 2098 | |
| ... | ... | @@ -3761,7 +3795,7 @@ const DeclGen = struct { |
| 3761 | 3795 | const mod = self.module; |
| 3762 | 3796 | // Construct new pointer type for the resulting pointer |
| 3763 | 3797 | const elem_ty = ptr_ty.elemType2(mod); // use elemType() so that we get T for *[N]T. |
| 3764 | | const elem_ptr_ty_ref = try self.ptrType(elem_ty, spvStorageClass(ptr_ty.ptrAddressSpace(mod))); |
| 3798 | const elem_ptr_ty_ref = try self.ptrType(elem_ty, self.spvStorageClass(ptr_ty.ptrAddressSpace(mod))); |
| 3765 | 3799 | if (ptr_ty.isSinglePointer(mod)) { |
| 3766 | 3800 | // Pointer-to-array. In this case, the resulting pointer is not of the same type |
| 3767 | 3801 | // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. |
| ... | ... | @@ -3835,7 +3869,7 @@ const DeclGen = struct { |
| 3835 | 3869 | const vector_ty = vector_ptr_ty.childType(mod); |
| 3836 | 3870 | const scalar_ty = vector_ty.scalarType(mod); |
| 3837 | 3871 | |
| 3838 | | const storage_class = spvStorageClass(vector_ptr_ty.ptrAddressSpace(mod)); |
| 3872 | const storage_class = self.spvStorageClass(vector_ptr_ty.ptrAddressSpace(mod)); |
| 3839 | 3873 | const scalar_ptr_ty_ref = try self.ptrType(scalar_ty, storage_class); |
| 3840 | 3874 | |
| 3841 | 3875 | const vector_ptr = try self.resolve(data.vector_ptr); |
| ... | ... | @@ -3858,7 +3892,7 @@ const DeclGen = struct { |
| 3858 | 3892 | if (layout.tag_size == 0) return; |
| 3859 | 3893 | |
| 3860 | 3894 | const tag_ty = un_ty.unionTagTypeSafety(mod).?; |
| 3861 | | const tag_ptr_ty_ref = try self.ptrType(tag_ty, spvStorageClass(un_ptr_ty.ptrAddressSpace(mod))); |
| 3895 | const tag_ptr_ty_ref = try self.ptrType(tag_ty, self.spvStorageClass(un_ptr_ty.ptrAddressSpace(mod))); |
| 3862 | 3896 | |
| 3863 | 3897 | const union_ptr_id = try self.resolve(bin_op.lhs); |
| 3864 | 3898 | const new_tag_id = try self.resolve(bin_op.rhs); |
| ... | ... | @@ -4079,7 +4113,7 @@ const DeclGen = struct { |
| 4079 | 4113 | return try self.spv.constUndef(result_ty_ref); |
| 4080 | 4114 | } |
| 4081 | 4115 | |
| 4082 | | const storage_class = spvStorageClass(object_ptr_ty.ptrAddressSpace(mod)); |
| 4116 | const storage_class = self.spvStorageClass(object_ptr_ty.ptrAddressSpace(mod)); |
| 4083 | 4117 | const pl_ptr_ty_ref = try self.ptrType(layout.payload_ty, storage_class); |
| 4084 | 4118 | const pl_ptr_id = try self.accessChain(pl_ptr_ty_ref, object_ptr, &.{layout.payload_index}); |
| 4085 | 4119 | |
| ... | ... | @@ -4134,17 +4168,16 @@ const DeclGen = struct { |
| 4134 | 4168 | .initializer = options.initializer, |
| 4135 | 4169 | }); |
| 4136 | 4170 | |
| 4171 | const target = self.getTarget(); |
| 4172 | if (target.os.tag == .vulkan) { |
| 4173 | return var_id; |
| 4174 | } |
| 4175 | |
| 4137 | 4176 | switch (options.storage_class) { |
| 4138 | 4177 | .Generic => { |
| 4139 | 4178 | const ptr_gn_ty_ref = try self.ptrType(ty, .Generic); |
| 4140 | 4179 | // Convert to a generic pointer |
| 4141 | | const result_id = self.spv.allocId(); |
| 4142 | | try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{ |
| 4143 | | .id_result_type = self.typeId(ptr_gn_ty_ref), |
| 4144 | | .id_result = result_id, |
| 4145 | | .pointer = var_id, |
| 4146 | | }); |
| 4147 | | return result_id; |
| 4180 | return self.castToGeneric(self.typeId(ptr_gn_ty_ref), var_id); |
| 4148 | 4181 | }, |
| 4149 | 4182 | .Function => return var_id, |
| 4150 | 4183 | else => unreachable, |
| ... | ... | @@ -4880,7 +4913,7 @@ const DeclGen = struct { |
| 4880 | 4913 | const is_non_null_id = blk: { |
| 4881 | 4914 | if (is_pointer) { |
| 4882 | 4915 | if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 4883 | | const storage_class = spvStorageClass(operand_ty.ptrAddressSpace(mod)); |
| 4916 | const storage_class = self.spvStorageClass(operand_ty.ptrAddressSpace(mod)); |
| 4884 | 4917 | const bool_ptr_ty = try self.ptrType(Type.bool, storage_class); |
| 4885 | 4918 | const tag_ptr_id = try self.accessChain(bool_ptr_ty, operand_id, &.{1}); |
| 4886 | 4919 | break :blk try self.load(Type.bool, tag_ptr_id, .{}); |