authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-06-02 15:57:18+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-06-10 20:32:43+02:00
log4bd9d9b7e0d769dd8d7701b73e54ae249ac7f1da
treeff9124805c74888ba8d08df4ae406caba37ff4ba
parentb9d738a5cff0ab896c25f1c8abe15757bcd6a0ba
signaturebadge-check Signed by SSH key SHA256:ZS52FNyUv2WUXvO4njmVaFVO46RHojFuOrxRc4LuKzg

spirv: change direct vector child repr to direct

Previously the child type of a vector was always in indirect representation. Concretely, this meant that vectors of bools are represented by vectors of u8. This was undesirable because it introduced a difference between vectorizable operations with a scalar bool and a vector of bool. This commit changes the representation to be the same for vectors and scalars everywhere. Some issues arised with constructing vectors: it seems the previous temporary- and-pointer approach does not work properly with vectors of bool. To work around this, simply use OpCompositeConstruct. This is the proper instruction for this, but it was previously not used because of a now-solved limitation in the SPIRV-LLVM-Translator. It was not yet applied to Zig because the Intel OpenCL CPU runtime does not have a recent enough version of the translator yet, but to solve that we just switch to testing with POCL instead.

1 files changed, 207 insertions(+), 63 deletions(-)

src/codegen/spirv.zig+207-63
...@@ -22,6 +22,7 @@ const IdResultType = spec.IdResultType;...@@ -22,6 +22,7 @@ const IdResultType = spec.IdResultType;
22const StorageClass = spec.StorageClass;22const StorageClass = spec.StorageClass;
2323
24const SpvModule = @import("spirv/Module.zig");24const SpvModule = @import("spirv/Module.zig");
25const IdRange = SpvModule.IdRange;
2526
26const SpvSection = @import("spirv/Section.zig");27const SpvSection = @import("spirv/Section.zig");
27const SpvAssembler = @import("spirv/Assembler.zig");28const 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;
3233
33const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, DeclGen.Repr }, IdResult);34const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, DeclGen.Repr }, IdResult);
34const PtrTypeMap = std.AutoHashMapUnmanaged(35const 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);
3839
...@@ -626,7 +627,7 @@ const DeclGen = struct {...@@ -626,7 +627,7 @@ const DeclGen = struct {
626 }627 }
627628
628 /// Checks whether the type can be directly translated to SPIR-V vectors629 /// 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 {
798799
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/1349808 // 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 can810 // 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 }
819829
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 }
822836
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);
10331047
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 better1056 // 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 }
10511071
...@@ -1334,7 +1354,11 @@ const DeclGen = struct {...@@ -1334,7 +1354,11 @@ const DeclGen = struct {
1334 }1354 }
13351355
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 };
13561380
1357 const child_ty_id = try self.resolveType(child_ty, .indirect);1381 const child_ty_id = try self.resolveType(child_ty, child_repr);
13581382
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);
16511674
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 }
22702314
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 }
22802372
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 }
22942386
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);
29273034
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 vector3037 // 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 else3039 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);
31013208
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 vector3211 // 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 else3213 else
...@@ -3312,7 +3419,7 @@ const DeclGen = struct {...@@ -3312,7 +3419,7 @@ const DeclGen = struct {
33123419
3313 const info = self.arithmeticTypeInfo(operand_ty);3420 const info = self.arithmeticTypeInfo(operand_ty);
33143421
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);
33173424
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 }
33263433
...@@ -3354,7 +3461,7 @@ const DeclGen = struct {...@@ -3354,7 +3461,7 @@ const DeclGen = struct {
33543461
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();
33593466
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 {
33883495
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);
40874194
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 }
40924198
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);
42364342
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...
42404357
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.
42424389
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 }
42484392
4249 fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4393 fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {