| ... | @@ -22,6 +22,7 @@ const IdResultType = spec.IdResultType; | ... | @@ -22,6 +22,7 @@ const IdResultType = spec.IdResultType; |
| 22 | const StorageClass = spec.StorageClass; | 22 | const StorageClass = spec.StorageClass; |
| 23 | | 23 | |
| 24 | const SpvModule = @import("spirv/Module.zig"); | 24 | const SpvModule = @import("spirv/Module.zig"); |
| | 25 | const IdRange = SpvModule.IdRange; |
| 25 | | 26 | |
| 26 | const SpvSection = @import("spirv/Section.zig"); | 27 | const SpvSection = @import("spirv/Section.zig"); |
| 27 | const SpvAssembler = @import("spirv/Assembler.zig"); | 28 | const SpvAssembler = @import("spirv/Assembler.zig"); |
| ... | @@ -32,7 +33,7 @@ pub const zig_call_abi_ver = 3; | ... | @@ -32,7 +33,7 @@ pub const zig_call_abi_ver = 3; |
| 32 | | 33 | |
| 33 | const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, DeclGen.Repr }, IdResult); | 34 | const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, DeclGen.Repr }, IdResult); |
| 34 | const PtrTypeMap = std.AutoHashMapUnmanaged( | 35 | const PtrTypeMap = std.AutoHashMapUnmanaged( |
| 35 | struct { InternPool.Index, StorageClass }, | 36 | struct { InternPool.Index, StorageClass, DeclGen.Repr }, |
| 36 | struct { ty_id: IdRef, fwd_emitted: bool }, | 37 | struct { ty_id: IdRef, fwd_emitted: bool }, |
| 37 | ); | 38 | ); |
| 38 | | 39 | |
| ... | @@ -626,7 +627,7 @@ const DeclGen = struct { | ... | @@ -626,7 +627,7 @@ const DeclGen = struct { |
| 626 | } | 627 | } |
| 627 | | 628 | |
| 628 | /// Checks whether the type can be directly translated to SPIR-V vectors | 629 | /// Checks whether the type can be directly translated to SPIR-V vectors |
| 629 | fn isVector(self: *DeclGen, ty: Type) bool { | 630 | fn isSpvVector(self: *DeclGen, ty: Type) bool { |
| 630 | const mod = self.module; | 631 | const mod = self.module; |
| 631 | const target = self.getTarget(); | 632 | const target = self.getTarget(); |
| 632 | if (ty.zigTypeTag(mod) != .Vector) return false; | 633 | if (ty.zigTypeTag(mod) != .Vector) return false; |
| ... | @@ -798,26 +799,39 @@ const DeclGen = struct { | ... | @@ -798,26 +799,39 @@ const DeclGen = struct { |
| 798 | | 799 | |
| 799 | /// Construct a vector at runtime. | 800 | /// Construct a vector at runtime. |
| 800 | /// ty must be an vector type. | 801 | /// ty must be an vector type. |
| 801 | /// Constituents should be in `indirect` representation (as the elements of an vector should be). | | |
| 802 | /// Result is in `direct` representation. | | |
| 803 | fn constructVector(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { | 802 | fn constructVector(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { |
| 804 | // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' | 803 | const mod = self.module; |
| 805 | // operands are not constant. | 804 | assert(ty.vectorLen(mod) == constituents.len); |
| | 805 | |
| | 806 | // Note: older versions of the Khronos SPRIV-LLVM translator crash on this instruction |
| | 807 | // because it cannot construct structs which' operands are not constant. |
| 806 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 | 808 | // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 |
| 807 | // For now, just initialize the struct by setting the fields manually... | 809 | // Currently this is the case for Intel OpenCL CPU runtime (2023-WW46), but the |
| 808 | // TODO: Make this OpCompositeConstruct when we can | 810 | // alternatives dont work properly: |
| | 811 | // - using temporaries/pointers doesn't work properly with vectors of bool, causes |
| | 812 | // backends that use llvm to crash |
| | 813 | // - using OpVectorInsertDynamic doesn't work for non-spirv-vectors of bool. |
| | 814 | |
| | 815 | const result_id = self.spv.allocId(); |
| | 816 | try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ |
| | 817 | .id_result_type = try self.resolveType(ty, .direct), |
| | 818 | .id_result = result_id, |
| | 819 | .constituents = constituents, |
| | 820 | }); |
| | 821 | return result_id; |
| | 822 | } |
| | 823 | |
| | 824 | /// Construct a vector at runtime with all lanes set to the same value. |
| | 825 | /// ty must be an vector type. |
| | 826 | fn constructVectorSplat(self: *DeclGen, ty: Type, constituent: IdRef) !IdRef { |
| 809 | const mod = self.module; | 827 | const mod = self.module; |
| 810 | const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); | 828 | const n = ty.vectorLen(mod); |
| 811 | const ptr_elem_ty_id = try self.ptrType(ty.elemType2(mod), .Function); | | |
| 812 | for (constituents, 0..) |constitent_id, index| { | | |
| 813 | const ptr_id = try self.accessChain(ptr_elem_ty_id, ptr_composite_id, &.{@as(u32, @intCast(index))}); | | |
| 814 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ | | |
| 815 | .pointer = ptr_id, | | |
| 816 | .object = constitent_id, | | |
| 817 | }); | | |
| 818 | } | | |
| 819 | | 829 | |
| 820 | return try self.load(ty, ptr_composite_id, .{}); | 830 | const constituents = try self.gpa.alloc(IdRef, n); |
| | 831 | defer self.gpa.free(constituents); |
| | 832 | @memset(constituents, constituent); |
| | 833 | |
| | 834 | return try self.constructVector(ty, constituents); |
| 821 | } | 835 | } |
| 822 | | 836 | |
| 823 | /// Construct an array at runtime. | 837 | /// Construct an array at runtime. |
| ... | @@ -1031,21 +1045,27 @@ const DeclGen = struct { | ... | @@ -1031,21 +1045,27 @@ const DeclGen = struct { |
| 1031 | const constituents = try self.gpa.alloc(IdRef, @intCast(ty.arrayLenIncludingSentinel(mod))); | 1045 | const constituents = try self.gpa.alloc(IdRef, @intCast(ty.arrayLenIncludingSentinel(mod))); |
| 1032 | defer self.gpa.free(constituents); | 1046 | defer self.gpa.free(constituents); |
| 1033 | | 1047 | |
| | 1048 | const child_repr: Repr = switch (tag) { |
| | 1049 | .array_type => .indirect, |
| | 1050 | .vector_type => .direct, |
| | 1051 | else => unreachable, |
| | 1052 | }; |
| | 1053 | |
| 1034 | switch (aggregate.storage) { | 1054 | switch (aggregate.storage) { |
| 1035 | .bytes => |bytes| { | 1055 | .bytes => |bytes| { |
| 1036 | // TODO: This is really space inefficient, perhaps there is a better | 1056 | // TODO: This is really space inefficient, perhaps there is a better |
| 1037 | // way to do it? | 1057 | // way to do it? |
| 1038 | for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| { | 1058 | for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| { |
| 1039 | constituent.* = try self.constInt(elem_ty, byte, .indirect); | 1059 | constituent.* = try self.constInt(elem_ty, byte, child_repr); |
| 1040 | } | 1060 | } |
| 1041 | }, | 1061 | }, |
| 1042 | .elems => |elems| { | 1062 | .elems => |elems| { |
| 1043 | for (constituents, elems) |*constituent, elem| { | 1063 | for (constituents, elems) |*constituent, elem| { |
| 1044 | constituent.* = try self.constant(elem_ty, Value.fromInterned(elem), .indirect); | 1064 | constituent.* = try self.constant(elem_ty, Value.fromInterned(elem), child_repr); |
| 1045 | } | 1065 | } |
| 1046 | }, | 1066 | }, |
| 1047 | .repeated_elem => |elem| { | 1067 | .repeated_elem => |elem| { |
| 1048 | @memset(constituents, try self.constant(elem_ty, Value.fromInterned(elem), .indirect)); | 1068 | @memset(constituents, try self.constant(elem_ty, Value.fromInterned(elem), child_repr)); |
| 1049 | }, | 1069 | }, |
| 1050 | } | 1070 | } |
| 1051 | | 1071 | |
| ... | @@ -1334,7 +1354,11 @@ const DeclGen = struct { | ... | @@ -1334,7 +1354,11 @@ const DeclGen = struct { |
| 1334 | } | 1354 | } |
| 1335 | | 1355 | |
| 1336 | fn ptrType(self: *DeclGen, child_ty: Type, storage_class: StorageClass) !IdRef { | 1356 | fn ptrType(self: *DeclGen, child_ty: Type, storage_class: StorageClass) !IdRef { |
| 1337 | const key = .{ child_ty.toIntern(), storage_class }; | 1357 | return try self.ptrType2(child_ty, storage_class, .indirect); |
| | 1358 | } |
| | 1359 | |
| | 1360 | fn ptrType2(self: *DeclGen, child_ty: Type, storage_class: StorageClass, child_repr: Repr) !IdRef { |
| | 1361 | const key = .{ child_ty.toIntern(), storage_class, child_repr }; |
| 1338 | const entry = try self.ptr_types.getOrPut(self.gpa, key); | 1362 | const entry = try self.ptr_types.getOrPut(self.gpa, key); |
| 1339 | if (entry.found_existing) { | 1363 | if (entry.found_existing) { |
| 1340 | const fwd_id = entry.value_ptr.ty_id; | 1364 | const fwd_id = entry.value_ptr.ty_id; |
| ... | @@ -1354,7 +1378,7 @@ const DeclGen = struct { | ... | @@ -1354,7 +1378,7 @@ const DeclGen = struct { |
| 1354 | .fwd_emitted = false, | 1378 | .fwd_emitted = false, |
| 1355 | }; | 1379 | }; |
| 1356 | | 1380 | |
| 1357 | const child_ty_id = try self.resolveType(child_ty, .indirect); | 1381 | const child_ty_id = try self.resolveType(child_ty, child_repr); |
| 1358 | | 1382 | |
| 1359 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ | 1383 | try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{ |
| 1360 | .id_result = result_id, | 1384 | .id_result = result_id, |
| ... | @@ -1645,11 +1669,10 @@ const DeclGen = struct { | ... | @@ -1645,11 +1669,10 @@ const DeclGen = struct { |
| 1645 | }, | 1669 | }, |
| 1646 | .Vector => { | 1670 | .Vector => { |
| 1647 | const elem_ty = ty.childType(mod); | 1671 | const elem_ty = ty.childType(mod); |
| 1648 | // TODO: Make `.direct`. | 1672 | const elem_ty_id = try self.resolveType(elem_ty, repr); |
| 1649 | const elem_ty_id = try self.resolveType(elem_ty, .indirect); | | |
| 1650 | const len = ty.vectorLen(mod); | 1673 | const len = ty.vectorLen(mod); |
| 1651 | | 1674 | |
| 1652 | if (self.isVector(ty)) { | 1675 | if (self.isSpvVector(ty)) { |
| 1653 | return try self.spv.vectorType(len, elem_ty_id); | 1676 | return try self.spv.vectorType(len, elem_ty_id); |
| 1654 | } else { | 1677 | } else { |
| 1655 | return try self.arrayType(len, elem_ty_id); | 1678 | return try self.arrayType(len, elem_ty_id); |
| ... | @@ -1948,7 +1971,7 @@ const DeclGen = struct { | ... | @@ -1948,7 +1971,7 @@ const DeclGen = struct { |
| 1948 | const mod = wip.dg.module; | 1971 | const mod = wip.dg.module; |
| 1949 | if (wip.is_array) { | 1972 | if (wip.is_array) { |
| 1950 | assert(ty.isVector(mod)); | 1973 | assert(ty.isVector(mod)); |
| 1951 | return try wip.dg.extractField(ty.childType(mod), value, @intCast(index)); | 1974 | return try wip.dg.extractVectorComponent(ty.childType(mod), value, @intCast(index)); |
| 1952 | } else { | 1975 | } else { |
| 1953 | assert(index == 0); | 1976 | assert(index == 0); |
| 1954 | return value; | 1977 | return value; |
| ... | @@ -1961,11 +1984,7 @@ const DeclGen = struct { | ... | @@ -1961,11 +1984,7 @@ const DeclGen = struct { |
| 1961 | /// Results is in `direct` representation. | 1984 | /// Results is in `direct` representation. |
| 1962 | fn finalize(wip: *WipElementWise) !IdRef { | 1985 | fn finalize(wip: *WipElementWise) !IdRef { |
| 1963 | if (wip.is_array) { | 1986 | if (wip.is_array) { |
| 1964 | // Convert all the constituents to indirect, as required for the array. | 1987 | return try wip.dg.constructVector(wip.result_ty, wip.results); |
| 1965 | for (wip.results) |*result| { | | |
| 1966 | result.* = try wip.dg.convertToIndirect(wip.ty, result.*); | | |
| 1967 | } | | |
| 1968 | return try wip.dg.constructArray(wip.result_ty, wip.results); | | |
| 1969 | } else { | 1988 | } else { |
| 1970 | return wip.results[0]; | 1989 | return wip.results[0]; |
| 1971 | } | 1990 | } |
| ... | @@ -1982,7 +2001,7 @@ const DeclGen = struct { | ... | @@ -1982,7 +2001,7 @@ const DeclGen = struct { |
| 1982 | /// Create a new element-wise operation. | 2001 | /// Create a new element-wise operation. |
| 1983 | fn elementWise(self: *DeclGen, result_ty: Type, force_element_wise: bool) !WipElementWise { | 2002 | fn elementWise(self: *DeclGen, result_ty: Type, force_element_wise: bool) !WipElementWise { |
| 1984 | const mod = self.module; | 2003 | const mod = self.module; |
| 1985 | const is_array = result_ty.isVector(mod) and (!self.isVector(result_ty) or force_element_wise); | 2004 | const is_array = result_ty.isVector(mod) and (!self.isSpvVector(result_ty) or force_element_wise); |
| 1986 | const num_results = if (is_array) result_ty.vectorLen(mod) else 1; | 2005 | const num_results = if (is_array) result_ty.vectorLen(mod) else 1; |
| 1987 | const results = try self.gpa.alloc(IdRef, num_results); | 2006 | const results = try self.gpa.alloc(IdRef, num_results); |
| 1988 | @memset(results, undefined); | 2007 | @memset(results, undefined); |
| ... | @@ -2253,29 +2272,102 @@ const DeclGen = struct { | ... | @@ -2253,29 +2272,102 @@ const DeclGen = struct { |
| 2253 | /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). | 2272 | /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct). |
| 2254 | fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { | 2273 | fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { |
| 2255 | const mod = self.module; | 2274 | const mod = self.module; |
| 2256 | return switch (ty.zigTypeTag(mod)) { | 2275 | const scalar_ty = ty.scalarType(mod); |
| 2257 | .Bool => blk: { | 2276 | const is_spv_vector = self.isSpvVector(ty); |
| 2258 | const result_id = self.spv.allocId(); | 2277 | switch (scalar_ty.zigTypeTag(mod)) { |
| 2259 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ | 2278 | .Bool => { |
| 2260 | .id_result_type = try self.resolveType(Type.bool, .direct), | 2279 | // TODO: We may want to use something like elementWise in this function. |
| 2261 | .id_result = result_id, | 2280 | // First we need to audit whether this would recursively call into itself. |
| 2262 | .operand_1 = operand_id, | 2281 | if (!ty.isVector(mod) or is_spv_vector) { |
| 2263 | .operand_2 = try self.constBool(false, .indirect), | 2282 | const result_id = self.spv.allocId(); |
| 2264 | }); | 2283 | const scalar_false_id = try self.constBool(false, .indirect); |
| 2265 | break :blk result_id; | 2284 | const false_id = if (is_spv_vector) blk: { |
| | 2285 | const index = try mod.intern_pool.get(mod.gpa, .{ |
| | 2286 | .vector_type = .{ |
| | 2287 | .len = ty.vectorLen(mod), |
| | 2288 | .child = Type.u1.toIntern(), |
| | 2289 | }, |
| | 2290 | }); |
| | 2291 | const vec_ty = Type.fromInterned(index); |
| | 2292 | break :blk try self.constructVectorSplat(vec_ty, scalar_false_id); |
| | 2293 | } else scalar_false_id; |
| | 2294 | |
| | 2295 | try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ |
| | 2296 | .id_result_type = try self.resolveType(ty, .direct), |
| | 2297 | .id_result = result_id, |
| | 2298 | .operand_1 = operand_id, |
| | 2299 | .operand_2 = false_id, |
| | 2300 | }); |
| | 2301 | return result_id; |
| | 2302 | } |
| | 2303 | |
| | 2304 | const constituents = try self.gpa.alloc(IdRef, ty.vectorLen(mod)); |
| | 2305 | for (constituents, 0..) |*id, i| { |
| | 2306 | const element = try self.extractVectorComponent(scalar_ty, operand_id, @intCast(i)); |
| | 2307 | id.* = try self.convertToDirect(scalar_ty, element); |
| | 2308 | } |
| | 2309 | return try self.constructVector(ty, constituents); |
| 2266 | }, | 2310 | }, |
| 2267 | else => operand_id, | 2311 | else => return operand_id, |
| 2268 | }; | 2312 | } |
| 2269 | } | 2313 | } |
| 2270 | | 2314 | |
| 2271 | /// Convert representation from direct (in 'register) to direct (in memory) | 2315 | /// Convert representation from direct (in 'register) to direct (in memory) |
| 2272 | /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect). | 2316 | /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect). |
| 2273 | fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { | 2317 | fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef { |
| 2274 | const mod = self.module; | 2318 | const mod = self.module; |
| 2275 | return switch (ty.zigTypeTag(mod)) { | 2319 | const scalar_ty = ty.scalarType(mod); |
| 2276 | .Bool => try self.intFromBool(Type.u1, operand_id), | 2320 | const is_spv_vector = self.isSpvVector(ty); |
| 2277 | else => operand_id, | 2321 | switch (scalar_ty.zigTypeTag(mod)) { |
| 2278 | }; | 2322 | .Bool => { |
| | 2323 | const result_ty = if (is_spv_vector) blk: { |
| | 2324 | const index = try mod.intern_pool.get(mod.gpa, .{ |
| | 2325 | .vector_type = .{ |
| | 2326 | .len = ty.vectorLen(mod), |
| | 2327 | .child = Type.u1.toIntern(), |
| | 2328 | }, |
| | 2329 | }); |
| | 2330 | break :blk Type.fromInterned(index); |
| | 2331 | } else Type.u1; |
| | 2332 | |
| | 2333 | if (!ty.isVector(mod) or is_spv_vector) { |
| | 2334 | // TODO: We may want to use something like elementWise in this function. |
| | 2335 | // First we need to audit whether this would recursively call into itself. |
| | 2336 | // Also unify it with intFromBool |
| | 2337 | |
| | 2338 | const scalar_zero_id = try self.constInt(Type.u1, 0, .direct); |
| | 2339 | const scalar_one_id = try self.constInt(Type.u1, 1, .direct); |
| | 2340 | |
| | 2341 | const zero_id = if (is_spv_vector) |
| | 2342 | try self.constructVectorSplat(result_ty, scalar_zero_id) |
| | 2343 | else |
| | 2344 | scalar_zero_id; |
| | 2345 | |
| | 2346 | const one_id = if (is_spv_vector) |
| | 2347 | try self.constructVectorSplat(result_ty, scalar_one_id) |
| | 2348 | else |
| | 2349 | scalar_one_id; |
| | 2350 | |
| | 2351 | const result_id = self.spv.allocId(); |
| | 2352 | try self.func.body.emit(self.spv.gpa, .OpSelect, .{ |
| | 2353 | .id_result_type = try self.resolveType(result_ty, .direct), |
| | 2354 | .id_result = result_id, |
| | 2355 | .condition = operand_id, |
| | 2356 | .object_1 = one_id, |
| | 2357 | .object_2 = zero_id, |
| | 2358 | }); |
| | 2359 | return result_id; |
| | 2360 | } |
| | 2361 | |
| | 2362 | const constituents = try self.gpa.alloc(IdRef, ty.vectorLen(mod)); |
| | 2363 | for (constituents, 0..) |*id, i| { |
| | 2364 | const element = try self.extractVectorComponent(scalar_ty, operand_id, @intCast(i)); |
| | 2365 | id.* = try self.convertToIndirect(scalar_ty, element); |
| | 2366 | } |
| | 2367 | return try self.constructVector(result_ty, constituents); |
| | 2368 | }, |
| | 2369 | else => return operand_id, |
| | 2370 | } |
| 2279 | } | 2371 | } |
| 2280 | | 2372 | |
| 2281 | fn extractField(self: *DeclGen, result_ty: Type, object: IdRef, field: u32) !IdRef { | 2373 | fn extractField(self: *DeclGen, result_ty: Type, object: IdRef, field: u32) !IdRef { |
| ... | @@ -2292,6 +2384,21 @@ const DeclGen = struct { | ... | @@ -2292,6 +2384,21 @@ const DeclGen = struct { |
| 2292 | return try self.convertToDirect(result_ty, result_id); | 2384 | return try self.convertToDirect(result_ty, result_id); |
| 2293 | } | 2385 | } |
| 2294 | | 2386 | |
| | 2387 | fn extractVectorComponent(self: *DeclGen, result_ty: Type, vector_id: IdRef, field: u32) !IdRef { |
| | 2388 | // Whether this is an OpTypeVector or OpTypeArray, we need to emit the same instruction regardless. |
| | 2389 | const result_ty_id = try self.resolveType(result_ty, .direct); |
| | 2390 | const result_id = self.spv.allocId(); |
| | 2391 | const indexes = [_]u32{field}; |
| | 2392 | try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{ |
| | 2393 | .id_result_type = result_ty_id, |
| | 2394 | .id_result = result_id, |
| | 2395 | .composite = vector_id, |
| | 2396 | .indexes = &indexes, |
| | 2397 | }); |
| | 2398 | // Vector components are already stored in direct representation. |
| | 2399 | return result_id; |
| | 2400 | } |
| | 2401 | |
| 2295 | const MemoryOptions = struct { | 2402 | const MemoryOptions = struct { |
| 2296 | is_volatile: bool = false, | 2403 | is_volatile: bool = false, |
| 2297 | }; | 2404 | }; |
| ... | @@ -2926,7 +3033,7 @@ const DeclGen = struct { | ... | @@ -2926,7 +3033,7 @@ const DeclGen = struct { |
| 2926 | const ov_ty = result_ty.structFieldType(1, self.module); | 3033 | const ov_ty = result_ty.structFieldType(1, self.module); |
| 2927 | | 3034 | |
| 2928 | const bool_ty_id = try self.resolveType(Type.bool, .direct); | 3035 | const bool_ty_id = try self.resolveType(Type.bool, .direct); |
| 2929 | const cmp_ty_id = if (self.isVector(operand_ty)) | 3036 | const cmp_ty_id = if (self.isSpvVector(operand_ty)) |
| 2930 | // TODO: Resolving a vector type with .direct should return a SPIR-V vector | 3037 | // TODO: Resolving a vector type with .direct should return a SPIR-V vector |
| 2931 | try self.spv.vectorType(operand_ty.vectorLen(mod), try self.resolveType(Type.bool, .direct)) | 3038 | try self.spv.vectorType(operand_ty.vectorLen(mod), try self.resolveType(Type.bool, .direct)) |
| 2932 | else | 3039 | else |
| ... | @@ -3100,7 +3207,7 @@ const DeclGen = struct { | ... | @@ -3100,7 +3207,7 @@ const DeclGen = struct { |
| 3100 | const ov_ty = result_ty.structFieldType(1, self.module); | 3207 | const ov_ty = result_ty.structFieldType(1, self.module); |
| 3101 | | 3208 | |
| 3102 | const bool_ty_id = try self.resolveType(Type.bool, .direct); | 3209 | const bool_ty_id = try self.resolveType(Type.bool, .direct); |
| 3103 | const cmp_ty_id = if (self.isVector(operand_ty)) | 3210 | const cmp_ty_id = if (self.isSpvVector(operand_ty)) |
| 3104 | // TODO: Resolving a vector type with .direct should return a SPIR-V vector | 3211 | // TODO: Resolving a vector type with .direct should return a SPIR-V vector |
| 3105 | try self.spv.vectorType(operand_ty.vectorLen(mod), try self.resolveType(Type.bool, .direct)) | 3212 | try self.spv.vectorType(operand_ty.vectorLen(mod), try self.resolveType(Type.bool, .direct)) |
| 3106 | else | 3213 | else |
| ... | @@ -3312,7 +3419,7 @@ const DeclGen = struct { | ... | @@ -3312,7 +3419,7 @@ const DeclGen = struct { |
| 3312 | | 3419 | |
| 3313 | const info = self.arithmeticTypeInfo(operand_ty); | 3420 | const info = self.arithmeticTypeInfo(operand_ty); |
| 3314 | | 3421 | |
| 3315 | var result_id = try self.extractField(scalar_ty, operand, 0); | 3422 | var result_id = try self.extractVectorComponent(scalar_ty, operand, 0); |
| 3316 | const len = operand_ty.vectorLen(mod); | 3423 | const len = operand_ty.vectorLen(mod); |
| 3317 | | 3424 | |
| 3318 | switch (reduce.operation) { | 3425 | switch (reduce.operation) { |
| ... | @@ -3320,7 +3427,7 @@ const DeclGen = struct { | ... | @@ -3320,7 +3427,7 @@ const DeclGen = struct { |
| 3320 | const cmp_op: std.math.CompareOperator = if (op == .Max) .gt else .lt; | 3427 | const cmp_op: std.math.CompareOperator = if (op == .Max) .gt else .lt; |
| 3321 | for (1..len) |i| { | 3428 | for (1..len) |i| { |
| 3322 | const lhs = result_id; | 3429 | const lhs = result_id; |
| 3323 | const rhs = try self.extractField(scalar_ty, operand, @intCast(i)); | 3430 | const rhs = try self.extractVectorComponent(scalar_ty, operand, @intCast(i)); |
| 3324 | result_id = try self.minMax(scalar_ty, cmp_op, lhs, rhs); | 3431 | result_id = try self.minMax(scalar_ty, cmp_op, lhs, rhs); |
| 3325 | } | 3432 | } |
| 3326 | | 3433 | |
| ... | @@ -3354,7 +3461,7 @@ const DeclGen = struct { | ... | @@ -3354,7 +3461,7 @@ const DeclGen = struct { |
| 3354 | | 3461 | |
| 3355 | for (1..len) |i| { | 3462 | for (1..len) |i| { |
| 3356 | const lhs = result_id; | 3463 | const lhs = result_id; |
| 3357 | const rhs = try self.extractField(scalar_ty, operand, @intCast(i)); | 3464 | const rhs = try self.extractVectorComponent(scalar_ty, operand, @intCast(i)); |
| 3358 | result_id = self.spv.allocId(); | 3465 | result_id = self.spv.allocId(); |
| 3359 | | 3466 | |
| 3360 | try self.func.body.emitRaw(self.spv.gpa, opcode, 4); | 3467 | try self.func.body.emitRaw(self.spv.gpa, opcode, 4); |
| ... | @@ -3388,9 +3495,9 @@ const DeclGen = struct { | ... | @@ -3388,9 +3495,9 @@ const DeclGen = struct { |
| 3388 | | 3495 | |
| 3389 | const index = elem.toSignedInt(mod); | 3496 | const index = elem.toSignedInt(mod); |
| 3390 | if (index >= 0) { | 3497 | if (index >= 0) { |
| 3391 | result_id.* = try self.extractField(wip.ty, a, @intCast(index)); | 3498 | result_id.* = try self.extractVectorComponent(wip.ty, a, @intCast(index)); |
| 3392 | } else { | 3499 | } else { |
| 3393 | result_id.* = try self.extractField(wip.ty, b, @intCast(~index)); | 3500 | result_id.* = try self.extractVectorComponent(wip.ty, b, @intCast(~index)); |
| 3394 | } | 3501 | } |
| 3395 | } | 3502 | } |
| 3396 | return try wip.finalize(); | 3503 | return try wip.finalize(); |
| ... | @@ -4086,8 +4193,7 @@ const DeclGen = struct { | ... | @@ -4086,8 +4193,7 @@ const DeclGen = struct { |
| 4086 | defer self.gpa.free(elem_ids); | 4193 | defer self.gpa.free(elem_ids); |
| 4087 | | 4194 | |
| 4088 | for (elements, 0..) |element, i| { | 4195 | for (elements, 0..) |element, i| { |
| 4089 | const id = try self.resolve(element); | 4196 | elem_ids[i] = try self.resolve(element); |
| 4090 | elem_ids[i] = try self.convertToIndirect(result_ty.childType(mod), id); | | |
| 4091 | } | 4197 | } |
| 4092 | | 4198 | |
| 4093 | return try self.constructVector(result_ty, elem_ids); | 4199 | return try self.constructVector(result_ty, elem_ids); |
| ... | @@ -4234,16 +4340,54 @@ const DeclGen = struct { | ... | @@ -4234,16 +4340,54 @@ const DeclGen = struct { |
| 4234 | const array_id = try self.resolve(bin_op.lhs); | 4340 | const array_id = try self.resolve(bin_op.lhs); |
| 4235 | const index_id = try self.resolve(bin_op.rhs); | 4341 | const index_id = try self.resolve(bin_op.rhs); |
| 4236 | | 4342 | |
| | 4343 | if (self.isSpvVector(array_ty)) { |
| | 4344 | const result_id = self.spv.allocId(); |
| | 4345 | try self.func.body.emit(self.spv.gpa, .OpVectorExtractDynamic, .{ |
| | 4346 | .id_result_type = try self.resolveType(elem_ty, .direct), |
| | 4347 | .id_result = result_id, |
| | 4348 | .vector = array_id, |
| | 4349 | .index = index_id, |
| | 4350 | }); |
| | 4351 | return result_id; |
| | 4352 | } |
| | 4353 | |
| 4237 | // SPIR-V doesn't have an array indexing function for some damn reason. | 4354 | // SPIR-V doesn't have an array indexing function for some damn reason. |
| 4238 | // For now, just generate a temporary and use that. | 4355 | // For now, just generate a temporary and use that. |
| 4239 | // TODO: This backend probably also should use isByRef from llvm... | 4356 | // TODO: This backend probably also should use isByRef from llvm... |
| 4240 | | 4357 | |
| 4241 | const elem_ptr_ty_id = try self.ptrType(elem_ty, .Function); | 4358 | const ptr_array_ty_id = try self.ptrType2(array_ty, .Function, .direct); |
| | 4359 | const ptr_elem_ty_id = try self.ptrType2(elem_ty, .Function, .direct); |
| | 4360 | |
| | 4361 | const tmp_id = self.spv.allocId(); |
| | 4362 | try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{ |
| | 4363 | .id_result_type = ptr_array_ty_id, |
| | 4364 | .id_result = tmp_id, |
| | 4365 | .storage_class = .Function, |
| | 4366 | }); |
| | 4367 | |
| | 4368 | try self.func.body.emit(self.spv.gpa, .OpStore, .{ |
| | 4369 | .pointer = tmp_id, |
| | 4370 | .object = array_id, |
| | 4371 | }); |
| | 4372 | |
| | 4373 | const elem_ptr_id = try self.accessChainId(ptr_elem_ty_id, tmp_id, &.{index_id}); |
| | 4374 | |
| | 4375 | const result_id = self.spv.allocId(); |
| | 4376 | try self.func.body.emit(self.spv.gpa, .OpLoad, .{ |
| | 4377 | .id_result_type = try self.resolveType(elem_ty, .direct), |
| | 4378 | .id_result = result_id, |
| | 4379 | .pointer = elem_ptr_id, |
| | 4380 | }); |
| | 4381 | |
| | 4382 | if (array_ty.isVector(mod)) { |
| | 4383 | // Result is already in direct representation |
| | 4384 | return result_id; |
| | 4385 | } |
| | 4386 | |
| | 4387 | // This is an array type; the elements are stored in indirect representation. |
| | 4388 | // We have to convert the type to direct. |
| 4242 | | 4389 | |
| 4243 | const tmp_id = try self.alloc(array_ty, .{ .storage_class = .Function }); | 4390 | return try self.convertToDirect(elem_ty, result_id); |
| 4244 | try self.store(array_ty, tmp_id, array_id, .{}); | | |
| 4245 | const elem_ptr_id = try self.accessChainId(elem_ptr_ty_id, tmp_id, &.{index_id}); | | |
| 4246 | return try self.load(elem_ty, elem_ptr_id, .{}); | | |
| 4247 | } | 4391 | } |
| 4248 | | 4392 | |
| 4249 | fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 4393 | fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |