authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2023-10-12 22:55:22+03:30
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-10-15 14:00:32+02:00
log2fe16e072ac447de5826ee436f50f73f56876ff9
treec887aa7f4fab985c6f8170ac88fdd726116255ea
parent245c0847265d3b0b904747ac4be298fe763a4454
signaturebadge-check Signed by SSH key SHA256:CQ99aPxq+RueiL9u7z0FEki5Fm7V6T8q4PrEGmINrA4

spirv: emit vectors as arrays


2 files changed, 66 insertions(+), 24 deletions(-)

src/codegen/spirv.zig+66-23
...@@ -531,7 +531,17 @@ const DeclGen = struct {...@@ -531,7 +531,17 @@ const DeclGen = struct {
531 },531 },
532 .Enum => return self.arithmeticTypeInfo(ty.intTagType(mod)),532 .Enum => return self.arithmeticTypeInfo(ty.intTagType(mod)),
533 // As of yet, there is no vector support in the self-hosted compiler.533 // As of yet, there is no vector support in the self-hosted compiler.
534 .Vector => self.todo("implement arithmeticTypeInfo for Vector", .{}),534 .Vector => blk: {
535 const child_type = ty.childType(mod);
536 const child_ty_info = try self.arithmeticTypeInfo(child_type);
537 break :blk ArithmeticTypeInfo{
538 .bits = child_ty_info.bits,
539 .backing_bits = child_ty_info.backing_bits,
540 .is_vector = true,
541 .signedness = child_ty_info.signedness,
542 .class = child_ty_info.class,
543 };
544 },
535 // TODO: For which types is this the case?545 // TODO: For which types is this the case?
536 // else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmt(self.module)}),546 // else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmt(self.module)}),
537 else => unreachable,547 else => unreachable,
...@@ -609,7 +619,7 @@ const DeclGen = struct {...@@ -609,7 +619,7 @@ const DeclGen = struct {
609 return result_id;619 return result_id;
610 }620 }
611621
612 /// Construct a struct at runtime.622 /// Construct an array at runtime.
613 /// result_ty_ref must be an array type.623 /// result_ty_ref must be an array type.
614 /// Constituents should be in `indirect` representation (as the elements of an array should be).624 /// Constituents should be in `indirect` representation (as the elements of an array should be).
615 /// Result is in `direct` representation.625 /// Result is in `direct` representation.
...@@ -812,7 +822,7 @@ const DeclGen = struct {...@@ -812,7 +822,7 @@ const DeclGen = struct {
812 return try self.constructStruct(result_ty_ref, &.{ payload_id, has_pl_id });822 return try self.constructStruct(result_ty_ref, &.{ payload_id, has_pl_id });
813 },823 },
814 .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {824 .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
815 .array_type => |array_type| {825 inline .array_type, .vector_type => |array_type, tag| {
816 const elem_ty = array_type.child.toType();826 const elem_ty = array_type.child.toType();
817 const elem_ty_ref = try self.resolveType(elem_ty, .indirect);827 const elem_ty_ref = try self.resolveType(elem_ty, .indirect);
818828
...@@ -839,9 +849,14 @@ const DeclGen = struct {...@@ -839,9 +849,14 @@ const DeclGen = struct {
839 }849 }
840 },850 },
841 }851 }
842 if (array_type.sentinel != .none) {852
843 constituents[constituents.len - 1] = try self.constant(elem_ty, array_type.sentinel.toValue(), .indirect);853 switch (tag) {
854 inline .array_type => if (array_type.sentinel != .none) {
855 constituents[constituents.len - 1] = try self.constant(elem_ty, array_type.sentinel.toValue(), .indirect);
856 },
857 else => {},
844 }858 }
859
845 return try self.constructArray(result_ty_ref, constituents);860 return try self.constructArray(result_ty_ref, constituents);
846 },861 },
847 .struct_type => {862 .struct_type => {
...@@ -870,7 +885,6 @@ const DeclGen = struct {...@@ -870,7 +885,6 @@ const DeclGen = struct {
870885
871 return try self.constructStruct(result_ty_ref, constituents.items);886 return try self.constructStruct(result_ty_ref, constituents.items);
872 },887 },
873 .vector_type => unreachable, // TODO
874 .anon_struct_type => unreachable, // TODO888 .anon_struct_type => unreachable, // TODO
875 else => unreachable,889 else => unreachable,
876 },890 },
...@@ -1347,19 +1361,14 @@ const DeclGen = struct {...@@ -1347,19 +1361,14 @@ const DeclGen = struct {
1347 } });1361 } });
1348 },1362 },
1349 .Vector => {1363 .Vector => {
1350 // Although not 100% the same, Zig vectors map quite neatly to SPIR-V vectors (including many integer and float operations1364 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
1351 // which work on them), so simply use those.1365
1352 // Note: SPIR-V vectors only support bools, ints and floats, so pointer vectors need to be supported another way.1366 const elem_ty = ty.childType(mod);
1353 // "composite integers" (larger than the largest supported native type) can probably be represented by an array of vectors.1367 const elem_ty_ref = try self.resolveType(elem_ty, .indirect);
1354 // TODO: The SPIR-V spec mentions that vector sizes may be quite restricted! look into which we can use, and whether OpTypeVector1368
1355 // is adequate at all for this.1369 const ty_ref = try self.spv.arrayType(ty.vectorLen(mod), elem_ty_ref);
13561370 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
1357 // TODO: Properly verify sizes and child type.1371 return ty_ref;
1358
1359 return try self.spv.resolve(.{ .vector_type = .{
1360 .component_type = try self.resolveType(ty.childType(mod), repr),
1361 .component_count = @as(u32, @intCast(ty.vectorLen(mod))),
1362 } });
1363 },1372 },
1364 .Struct => {1373 .Struct => {
1365 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;1374 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
...@@ -2223,18 +2232,52 @@ const DeclGen = struct {...@@ -2223,18 +2232,52 @@ const DeclGen = struct {
2223 comptime modular: bool,2232 comptime modular: bool,
2224 ) !?IdRef {2233 ) !?IdRef {
2225 if (self.liveness.isUnused(inst)) return null;2234 if (self.liveness.isUnused(inst)) return null;
2235
2226 // LHS and RHS are guaranteed to have the same type, and AIR guarantees2236 // LHS and RHS are guaranteed to have the same type, and AIR guarantees
2227 // the result to be the same as the LHS and RHS, which matches SPIR-V.2237 // the result to be the same as the LHS and RHS, which matches SPIR-V.
2228 const ty = self.typeOfIndex(inst);2238 const ty = self.typeOfIndex(inst);
2229 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2239 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2230 var lhs_id = try self.resolve(bin_op.lhs);2240 const lhs_id = try self.resolve(bin_op.lhs);
2231 var rhs_id = try self.resolve(bin_op.rhs);2241 const rhs_id = try self.resolve(bin_op.rhs);
2232
2233 const result_ty_ref = try self.resolveType(ty, .direct);
22342242
2235 assert(self.typeOf(bin_op.lhs).eql(ty, self.module));2243 assert(self.typeOf(bin_op.lhs).eql(ty, self.module));
2236 assert(self.typeOf(bin_op.rhs).eql(ty, self.module));2244 assert(self.typeOf(bin_op.rhs).eql(ty, self.module));
22372245
2246 return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop, modular);
2247 }
2248
2249 fn arithOp(
2250 self: *DeclGen,
2251 ty: Type,
2252 lhs_id_: IdRef,
2253 rhs_id_: IdRef,
2254 comptime fop: Opcode,
2255 comptime sop: Opcode,
2256 comptime uop: Opcode,
2257 /// true if this operation holds under modular arithmetic.
2258 comptime modular: bool,
2259 ) !IdRef {
2260 var rhs_id = rhs_id_;
2261 var lhs_id = lhs_id_;
2262
2263 const mod = self.module;
2264 const result_ty_ref = try self.resolveType(ty, .direct);
2265
2266 if (ty.isVector(mod)) {
2267 const child_ty = ty.childType(mod);
2268 const vector_len = ty.vectorLen(mod);
2269 var constituents = try self.gpa.alloc(IdRef, vector_len);
2270 defer self.gpa.free(constituents);
2271
2272 for (constituents, 0..) |*constituent, i| {
2273 const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i));
2274 const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i));
2275 constituent.* = try self.arithOp(child_ty, lhs_index_id, rhs_index_id, fop, sop, uop, modular);
2276 }
2277
2278 return self.constructArray(result_ty_ref, constituents);
2279 }
2280
2238 // Binary operations are generally applicable to both scalar and vector operations2281 // Binary operations are generally applicable to both scalar and vector operations
2239 // in SPIR-V, but int and float versions of operations require different opcodes.2282 // in SPIR-V, but int and float versions of operations require different opcodes.
2240 const info = try self.arithmeticTypeInfo(ty);2283 const info = try self.arithmeticTypeInfo(ty);
test/behavior/vector.zig-1
...@@ -29,7 +29,6 @@ test "vector wrap operators" {...@@ -29,7 +29,6 @@ test "vector wrap operators" {
29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
30 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO30 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
31 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO31 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
32 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
33 if (builtin.zig_backend == .stage2_x86_64 and32 if (builtin.zig_backend == .stage2_x86_64 and
34 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO33 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest; // TODO
3534