authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-02-05 09:24:49+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-05 09:24:49+01:00
log7634a115c50ef66edbdd5644c4ba310eb31e6343
treeb8be56f0db16691e2939e87bac1222ba2c9fd4a8
parentaebf20cc9a0469a778d6276d3797525660746e91
parent25111061504a652bfed45b26252349f363b109af
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18580 from Snektron/spirv-more-vectors

spirv: more vector operations

37 files changed, 956 insertions(+), 516 deletions(-)

lib/std/mem.zig+11-1
......@@ -632,10 +632,16 @@ test "lessThan" {
632632 try testing.expect(lessThan(u8, "", "a"));
633633}
634634
635const backend_can_use_eql_bytes = switch (builtin.zig_backend) {
636 // The SPIR-V backend does not support the optimized path yet.
637 .stage2_spirv64 => false,
638 else => true,
639};
640
635641/// Compares two slices and returns whether they are equal.
636642pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
637643 if (@sizeOf(T) == 0) return true;
638 if (!@inComptime() and std.meta.hasUniqueRepresentation(T)) return eqlBytes(sliceAsBytes(a), sliceAsBytes(b));
644 if (!@inComptime() and std.meta.hasUniqueRepresentation(T) and backend_can_use_eql_bytes) return eqlBytes(sliceAsBytes(a), sliceAsBytes(b));
639645
640646 if (a.len != b.len) return false;
641647 if (a.len == 0 or a.ptr == b.ptr) return true;
......@@ -648,6 +654,10 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
648654
649655/// std.mem.eql heavily optimized for slices of bytes.
650656fn eqlBytes(a: []const u8, b: []const u8) bool {
657 if (!backend_can_use_eql_bytes) {
658 return eql(u8, a, b);
659 }
660
651661 if (a.len != b.len) return false;
652662 if (a.len == 0 or a.ptr == b.ptr) return true;
653663
src/codegen/spirv.zig+891-433
......@@ -373,8 +373,9 @@ const DeclGen = struct {
373373 /// For `composite_integer` this is 0 (TODO)
374374 backing_bits: u16,
375375
376 /// Whether the type is a vector.
377 is_vector: bool,
376 /// Null if this type is a scalar, or the length
377 /// of the vector otherwise.
378 vector_len: ?u32,
378379
379380 /// Whether the inner type is signed. Only relevant for integers.
380381 signedness: std.builtin.Signedness,
......@@ -597,32 +598,37 @@ const DeclGen = struct {
597598 return self.backingIntBits(ty) == null;
598599 }
599600
600 fn arithmeticTypeInfo(self: *DeclGen, ty: Type) !ArithmeticTypeInfo {
601 fn arithmeticTypeInfo(self: *DeclGen, ty: Type) ArithmeticTypeInfo {
601602 const mod = self.module;
602603 const target = self.getTarget();
603 return switch (ty.zigTypeTag(mod)) {
604 var scalar_ty = ty.scalarType(mod);
605 if (scalar_ty.zigTypeTag(mod) == .Enum) {
606 scalar_ty = scalar_ty.intTagType(mod);
607 }
608 const vector_len = if (ty.isVector(mod)) ty.vectorLen(mod) else null;
609 return switch (scalar_ty.zigTypeTag(mod)) {
604610 .Bool => ArithmeticTypeInfo{
605611 .bits = 1, // Doesn't matter for this class.
606612 .backing_bits = self.backingIntBits(1).?,
607 .is_vector = false,
613 .vector_len = vector_len,
608614 .signedness = .unsigned, // Technically, but doesn't matter for this class.
609615 .class = .bool,
610616 },
611617 .Float => ArithmeticTypeInfo{
612 .bits = ty.floatBits(target),
613 .backing_bits = ty.floatBits(target), // TODO: F80?
614 .is_vector = false,
618 .bits = scalar_ty.floatBits(target),
619 .backing_bits = scalar_ty.floatBits(target), // TODO: F80?
620 .vector_len = vector_len,
615621 .signedness = .signed, // Technically, but doesn't matter for this class.
616622 .class = .float,
617623 },
618624 .Int => blk: {
619 const int_info = ty.intInfo(mod);
625 const int_info = scalar_ty.intInfo(mod);
620626 // TODO: Maybe it's useful to also return this value.
621627 const maybe_backing_bits = self.backingIntBits(int_info.bits);
622628 break :blk ArithmeticTypeInfo{
623629 .bits = int_info.bits,
624630 .backing_bits = maybe_backing_bits orelse 0,
625 .is_vector = false,
631 .vector_len = vector_len,
626632 .signedness = int_info.signedness,
627633 .class = if (maybe_backing_bits) |backing_bits|
628634 if (backing_bits == int_info.bits)
......@@ -633,22 +639,9 @@ const DeclGen = struct {
633639 .composite_integer,
634640 };
635641 },
636 .Enum => return self.arithmeticTypeInfo(ty.intTagType(mod)),
637 // As of yet, there is no vector support in the self-hosted compiler.
638 .Vector => blk: {
639 const child_type = ty.childType(mod);
640 const child_ty_info = try self.arithmeticTypeInfo(child_type);
641 break :blk ArithmeticTypeInfo{
642 .bits = child_ty_info.bits,
643 .backing_bits = child_ty_info.backing_bits,
644 .is_vector = true,
645 .signedness = child_ty_info.signedness,
646 .class = child_ty_info.class,
647 };
648 },
649 // TODO: For which types is this the case?
650 // else => self.todo("implement arithmeticTypeInfo for {}", .{ty.fmt(self.module)}),
651 else => unreachable,
642 .Enum => unreachable,
643 .Vector => unreachable,
644 else => unreachable, // Unhandled arithmetic type
652645 };
653646 }
654647
......@@ -685,6 +678,18 @@ const DeclGen = struct {
685678 }
686679 }
687680
681 /// Emits a float constant
682 fn constFloat(self: *DeclGen, ty_ref: CacheRef, value: f128) !IdRef {
683 const ty = self.spv.cache.lookup(ty_ref).float_type;
684 return switch (ty.bits) {
685 16 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float16 = @floatCast(value) } } }),
686 32 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float32 = @floatCast(value) } } }),
687 64 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float64 = @floatCast(value) } } }),
688 80, 128 => unreachable, // TODO
689 else => unreachable,
690 };
691 }
692
688693 /// Construct a struct at runtime.
689694 /// ty must be a struct type.
690695 /// Constituents should be in `indirect` representation (as the elements of a struct should be).
......@@ -1760,6 +1765,92 @@ const DeclGen = struct {
17601765 return union_layout;
17611766 }
17621767
1768 /// This structure is used as helper for element-wise operations. It is intended
1769 /// to be used with both vectors and single elements.
1770 const WipElementWise = struct {
1771 dg: *DeclGen,
1772 result_ty: Type,
1773 /// Always in direct representation.
1774 result_ty_ref: CacheRef,
1775 scalar_ty: Type,
1776 /// Always in direct representation.
1777 scalar_ty_ref: CacheRef,
1778 scalar_ty_id: IdRef,
1779 /// True if the input is actually a vector type.
1780 is_vector: bool,
1781 /// The element-wise operation should fill these results before calling finalize().
1782 /// These should all be in **direct** representation! `finalize()` will convert
1783 /// them to indirect if required.
1784 results: []IdRef,
1785
1786 fn deinit(wip: *WipElementWise) void {
1787 wip.dg.gpa.free(wip.results);
1788 }
1789
1790 /// Utility function to extract the element at a particular index in an
1791 /// input vector. This type is expected to be a vector if `wip.is_vector`, and
1792 /// a scalar otherwise.
1793 fn elementAt(wip: WipElementWise, ty: Type, value: IdRef, index: usize) !IdRef {
1794 const mod = wip.dg.module;
1795 if (wip.is_vector) {
1796 assert(ty.isVector(mod));
1797 return try wip.dg.extractField(ty.childType(mod), value, @intCast(index));
1798 } else {
1799 assert(!ty.isVector(mod));
1800 assert(index == 0);
1801 return value;
1802 }
1803 }
1804
1805 /// Turns the results of this WipElementWise into a result. This can either
1806 /// be a vector or single element, depending on `result_ty`.
1807 /// After calling this function, this WIP is no longer usable.
1808 /// Results is in `direct` representation.
1809 fn finalize(wip: *WipElementWise) !IdRef {
1810 if (wip.is_vector) {
1811 // Convert all the constituents to indirect, as required for the array.
1812 for (wip.results) |*result| {
1813 result.* = try wip.dg.convertToIndirect(wip.scalar_ty, result.*);
1814 }
1815 return try wip.dg.constructArray(wip.result_ty, wip.results);
1816 } else {
1817 return wip.results[0];
1818 }
1819 }
1820
1821 /// Allocate a result id at a particular index, and return it.
1822 fn allocId(wip: *WipElementWise, index: usize) IdRef {
1823 assert(wip.is_vector or index == 0);
1824 wip.results[index] = wip.dg.spv.allocId();
1825 return wip.results[index];
1826 }
1827 };
1828
1829 /// Create a new element-wise operation.
1830 fn elementWise(self: *DeclGen, result_ty: Type) !WipElementWise {
1831 const mod = self.module;
1832 // For now, this operation also reasons in terms of `.direct` representation.
1833 const result_ty_ref = try self.resolveType(result_ty, .direct);
1834 const is_vector = result_ty.isVector(mod);
1835 const num_results = if (is_vector) result_ty.vectorLen(mod) else 1;
1836 const results = try self.gpa.alloc(IdRef, num_results);
1837 for (results) |*result| result.* = undefined;
1838
1839 const scalar_ty = result_ty.scalarType(mod);
1840 const scalar_ty_ref = try self.resolveType(scalar_ty, .direct);
1841
1842 return .{
1843 .dg = self,
1844 .result_ty = result_ty,
1845 .result_ty_ref = result_ty_ref,
1846 .scalar_ty = scalar_ty,
1847 .scalar_ty_ref = scalar_ty_ref,
1848 .scalar_ty_id = self.typeId(scalar_ty_ref),
1849 .is_vector = is_vector,
1850 .results = results,
1851 };
1852 }
1853
17631854 /// The SPIR-V backend is not yet advanced enough to support the std testing infrastructure.
17641855 /// In order to be able to run tests, we "temporarily" lower test kernels into separate entry-
17651856 /// points. The test executor will then be able to invoke these to run the tests.
......@@ -2081,25 +2172,31 @@ const DeclGen = struct {
20812172 const air_tags = self.air.instructions.items(.tag);
20822173 const maybe_result_id: ?IdRef = switch (air_tags[@intFromEnum(inst)]) {
20832174 // zig fmt: off
2084 .add, .add_wrap => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd, true),
2085 .sub, .sub_wrap => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub, true),
2086 .mul, .mul_wrap => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul, true),
2175 .add, .add_wrap, .add_optimized => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),
2176 .sub, .sub_wrap, .sub_optimized => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
2177 .mul, .mul_wrap, .mul_optimized => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),
2178
2179 .abs => try self.airAbs(inst),
20872180
20882181 .div_float,
20892182 .div_float_optimized,
20902183 // TODO: Check that this is the right operation.
20912184 .div_trunc,
20922185 .div_trunc_optimized,
2093 => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv, false),
2186 => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv),
20942187 // TODO: Check if this is the right operation
2095 // TODO: Make airArithOp for rem not emit a mask for the LHS.
20962188 .rem,
20972189 .rem_optimized,
2098 => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem, false),
2190 => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem),
20992191
21002192 .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),
21012193 .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),
2194 .shl_with_overflow => try self.airShlOverflow(inst),
21022195
2196 .mul_add => try self.airMulAdd(inst),
2197
2198 .splat => try self.airSplat(inst),
2199 .reduce, .reduce_optimized => try self.airReduce(inst),
21032200 .shuffle => try self.airShuffle(inst),
21042201
21052202 .ptr_add => try self.airPtrAdd(inst),
......@@ -2111,7 +2208,8 @@ const DeclGen = struct {
21112208 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),
21122209 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr),
21132210
2114 .shl => try self.airShift(inst, .OpShiftLeftLogical),
2211 .shl, .shl_exact => try self.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical),
2212 .shr, .shr_exact => try self.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic),
21152213
21162214 .min => try self.airMinMax(inst, .lt),
21172215 .max => try self.airMinMax(inst, .gt),
......@@ -2121,6 +2219,7 @@ const DeclGen = struct {
21212219 .int_from_ptr => try self.airIntFromPtr(inst),
21222220 .float_from_int => try self.airFloatFromInt(inst),
21232221 .int_from_float => try self.airIntFromFloat(inst),
2222 .int_from_bool => try self.airIntFromBool(inst),
21242223 .fpext, .fptrunc => try self.airFloatCast(inst),
21252224 .not => try self.airNot(inst),
21262225
......@@ -2137,6 +2236,8 @@ const DeclGen = struct {
21372236 .ptr_elem_val => try self.airPtrElemVal(inst),
21382237 .array_elem_val => try self.airArrayElemVal(inst),
21392238
2239 .vector_store_elem => return self.airVectorStoreElem(inst),
2240
21402241 .set_union_tag => return self.airSetUnionTag(inst),
21412242 .get_union_tag => try self.airGetUnionTag(inst),
21422243 .union_init => try self.airUnionInit(inst),
......@@ -2189,13 +2290,16 @@ const DeclGen = struct {
21892290 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
21902291 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
21912292
2192 .is_null => try self.airIsNull(inst, .is_null),
2193 .is_non_null => try self.airIsNull(inst, .is_non_null),
2194 .is_err => try self.airIsErr(inst, .is_err),
2195 .is_non_err => try self.airIsErr(inst, .is_non_err),
2293 .is_null => try self.airIsNull(inst, false, .is_null),
2294 .is_non_null => try self.airIsNull(inst, false, .is_non_null),
2295 .is_null_ptr => try self.airIsNull(inst, true, .is_null),
2296 .is_non_null_ptr => try self.airIsNull(inst, true, .is_non_null),
2297 .is_err => try self.airIsErr(inst, .is_err),
2298 .is_non_err => try self.airIsErr(inst, .is_non_err),
21962299
2197 .optional_payload => try self.airUnwrapOptional(inst),
2198 .wrap_optional => try self.airWrapOptional(inst),
2300 .optional_payload => try self.airUnwrapOptional(inst),
2301 .optional_payload_ptr => try self.airUnwrapOptionalPtr(inst),
2302 .wrap_optional => try self.airWrapOptional(inst),
21992303
22002304 .assembly => try self.airAssembly(inst),
22012305
......@@ -2213,34 +2317,17 @@ const DeclGen = struct {
22132317 }
22142318
22152319 fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef {
2216 const mod = self.module;
2217
2218 if (ty.isVector(mod)) {
2219 const child_ty = ty.childType(mod);
2220 const vector_len = ty.vectorLen(mod);
2221
2222 const constituents = try self.gpa.alloc(IdRef, vector_len);
2223 defer self.gpa.free(constituents);
2224
2225 for (constituents, 0..) |*constituent, i| {
2226 const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i));
2227 const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i));
2228 const result_id = try self.binOpSimple(child_ty, lhs_index_id, rhs_index_id, opcode);
2229 constituent.* = try self.convertToIndirect(child_ty, result_id);
2230 }
2231
2232 return try self.constructArray(ty, constituents);
2320 var wip = try self.elementWise(ty);
2321 defer wip.deinit();
2322 for (0..wip.results.len) |i| {
2323 try self.func.body.emit(self.spv.gpa, opcode, .{
2324 .id_result_type = wip.scalar_ty_id,
2325 .id_result = wip.allocId(i),
2326 .operand_1 = try wip.elementAt(ty, lhs_id, i),
2327 .operand_2 = try wip.elementAt(ty, rhs_id, i),
2328 });
22332329 }
2234
2235 const result_id = self.spv.allocId();
2236 const result_type_id = try self.resolveTypeId(ty);
2237 try self.func.body.emit(self.spv.gpa, opcode, .{
2238 .id_result_type = result_type_id,
2239 .id_result = result_id,
2240 .operand_1 = lhs_id,
2241 .operand_2 = rhs_id,
2242 });
2243 return result_id;
2330 return try wip.finalize();
22442331 }
22452332
22462333 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef {
......@@ -2254,29 +2341,59 @@ const DeclGen = struct {
22542341 return try self.binOpSimple(ty, lhs_id, rhs_id, opcode);
22552342 }
22562343
2257 fn airShift(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef {
2344 fn airShift(self: *DeclGen, inst: Air.Inst.Index, comptime unsigned: Opcode, comptime signed: Opcode) !?IdRef {
22582345 if (self.liveness.isUnused(inst)) return null;
2346 const mod = self.module;
22592347 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
22602348 const lhs_id = try self.resolve(bin_op.lhs);
22612349 const rhs_id = try self.resolve(bin_op.rhs);
2262 const result_type_id = try self.resolveTypeId(self.typeOfIndex(inst));
22632350
2264 // the shift and the base must be the same type in SPIR-V, but in Zig the shift is a smaller int.
2265 const shift_id = self.spv.allocId();
2266 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
2267 .id_result_type = result_type_id,
2268 .id_result = shift_id,
2269 .unsigned_value = rhs_id,
2270 });
2351 const result_ty = self.typeOfIndex(inst);
2352 const shift_ty = self.typeOf(bin_op.rhs);
2353 const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct);
22712354
2272 const result_id = self.spv.allocId();
2273 try self.func.body.emit(self.spv.gpa, opcode, .{
2274 .id_result_type = result_type_id,
2275 .id_result = result_id,
2276 .base = lhs_id,
2277 .shift = shift_id,
2278 });
2279 return result_id;
2355 const info = self.arithmeticTypeInfo(result_ty);
2356 switch (info.class) {
2357 .composite_integer => return self.todo("shift ops for composite integers", .{}),
2358 .integer, .strange_integer => {},
2359 .float, .bool => unreachable,
2360 }
2361
2362 var wip = try self.elementWise(result_ty);
2363 defer wip.deinit();
2364 for (wip.results, 0..) |*result_id, i| {
2365 const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i);
2366 const rhs_elem_id = try wip.elementAt(shift_ty, rhs_id, i);
2367
2368 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
2369 // so just manually upcast it if required.
2370 const shift_id = if (scalar_shift_ty_ref != wip.scalar_ty_ref) blk: {
2371 const shift_id = self.spv.allocId();
2372 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
2373 .id_result_type = wip.scalar_ty_id,
2374 .id_result = shift_id,
2375 .unsigned_value = rhs_elem_id,
2376 });
2377 break :blk shift_id;
2378 } else rhs_elem_id;
2379
2380 const value_id = self.spv.allocId();
2381 const args = .{
2382 .id_result_type = wip.scalar_ty_id,
2383 .id_result = value_id,
2384 .base = lhs_elem_id,
2385 .shift = shift_id,
2386 };
2387
2388 if (result_ty.isSignedInt(mod)) {
2389 try self.func.body.emit(self.spv.gpa, signed, args);
2390 } else {
2391 try self.func.body.emit(self.spv.gpa, unsigned, args);
2392 }
2393
2394 result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info);
2395 }
2396 return try wip.finalize();
22802397 }
22812398
22822399 fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef {
......@@ -2286,88 +2403,102 @@ const DeclGen = struct {
22862403 const lhs_id = try self.resolve(bin_op.lhs);
22872404 const rhs_id = try self.resolve(bin_op.rhs);
22882405 const result_ty = self.typeOfIndex(inst);
2289 const result_ty_ref = try self.resolveType(result_ty, .direct);
2290
2291 const info = try self.arithmeticTypeInfo(result_ty);
2292 // TODO: Use fmin for OpenCL
2293 const cmp_id = try self.cmp(op, Type.bool, result_ty, lhs_id, rhs_id);
2294 const selection_id = switch (info.class) {
2295 .float => blk: {
2296 // cmp uses OpFOrd. When we have 0 [<>] nan this returns false,
2297 // but we want it to pick lhs. Therefore we also have to check if
2298 // rhs is nan. We don't need to care about the result when both
2299 // are nan.
2300 const rhs_is_nan_id = self.spv.allocId();
2301 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
2302 try self.func.body.emit(self.spv.gpa, .OpIsNan, .{
2303 .id_result_type = self.typeId(bool_ty_ref),
2304 .id_result = rhs_is_nan_id,
2305 .x = rhs_id,
2306 });
2307 const float_cmp_id = self.spv.allocId();
2308 try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{
2309 .id_result_type = self.typeId(bool_ty_ref),
2310 .id_result = float_cmp_id,
2311 .operand_1 = cmp_id,
2312 .operand_2 = rhs_is_nan_id,
2313 });
2314 break :blk float_cmp_id;
2315 },
2316 else => cmp_id,
2317 };
23182406
2319 const result_id = self.spv.allocId();
2320 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
2321 .id_result_type = self.typeId(result_ty_ref),
2322 .id_result = result_id,
2323 .condition = selection_id,
2324 .object_1 = lhs_id,
2325 .object_2 = rhs_id,
2326 });
2327 return result_id;
2328 }
2407 return try self.minMax(result_ty, op, lhs_id, rhs_id);
2408 }
2409
2410 fn minMax(self: *DeclGen, result_ty: Type, op: std.math.CompareOperator, lhs_id: IdRef, rhs_id: IdRef) !IdRef {
2411 const info = self.arithmeticTypeInfo(result_ty);
2412
2413 var wip = try self.elementWise(result_ty);
2414 defer wip.deinit();
2415 for (wip.results, 0..) |*result_id, i| {
2416 const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i);
2417 const rhs_elem_id = try wip.elementAt(result_ty, rhs_id, i);
2418
2419 // TODO: Use fmin for OpenCL
2420 const cmp_id = try self.cmp(op, Type.bool, wip.scalar_ty, lhs_elem_id, rhs_elem_id);
2421 const selection_id = switch (info.class) {
2422 .float => blk: {
2423 // cmp uses OpFOrd. When we have 0 [<>] nan this returns false,
2424 // but we want it to pick lhs. Therefore we also have to check if
2425 // rhs is nan. We don't need to care about the result when both
2426 // are nan.
2427 const rhs_is_nan_id = self.spv.allocId();
2428 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
2429 try self.func.body.emit(self.spv.gpa, .OpIsNan, .{
2430 .id_result_type = self.typeId(bool_ty_ref),
2431 .id_result = rhs_is_nan_id,
2432 .x = rhs_elem_id,
2433 });
2434 const float_cmp_id = self.spv.allocId();
2435 try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{
2436 .id_result_type = self.typeId(bool_ty_ref),
2437 .id_result = float_cmp_id,
2438 .operand_1 = cmp_id,
2439 .operand_2 = rhs_is_nan_id,
2440 });
2441 break :blk float_cmp_id;
2442 },
2443 else => cmp_id,
2444 };
23292445
2330 /// This function canonicalizes a "strange" integer value:
2331 /// For unsigned integers, the value is masked so that only the relevant bits can contain
2332 /// non-zeros.
2333 /// For signed integers, the value is also sign extended.
2334 fn normalizeInt(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef {
2335 assert(info.class != .composite_integer); // TODO
2336 if (info.bits == info.backing_bits) {
2337 return value_id;
2446 result_id.* = self.spv.allocId();
2447 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
2448 .id_result_type = wip.scalar_ty_id,
2449 .id_result = result_id.*,
2450 .condition = selection_id,
2451 .object_1 = lhs_elem_id,
2452 .object_2 = rhs_elem_id,
2453 });
23382454 }
2339
2340 switch (info.signedness) {
2341 .unsigned => {
2342 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
2343 const result_id = self.spv.allocId();
2344 const mask_id = try self.constInt(ty_ref, mask_value);
2345 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{
2346 .id_result_type = self.typeId(ty_ref),
2347 .id_result = result_id,
2348 .operand_1 = value_id,
2349 .operand_2 = mask_id,
2350 });
2351 return result_id;
2352 },
2353 .signed => {
2354 // Shift left and right so that we can copy the sight bit that way.
2355 const shift_amt_id = try self.constInt(ty_ref, info.backing_bits - info.bits);
2356 const left_id = self.spv.allocId();
2357 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{
2358 .id_result_type = self.typeId(ty_ref),
2359 .id_result = left_id,
2360 .base = value_id,
2361 .shift = shift_amt_id,
2362 });
2363 const right_id = self.spv.allocId();
2364 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{
2365 .id_result_type = self.typeId(ty_ref),
2366 .id_result = right_id,
2367 .base = left_id,
2368 .shift = shift_amt_id,
2369 });
2370 return right_id;
2455 return wip.finalize();
2456 }
2457
2458 /// This function normalizes values to a canonical representation
2459 /// after some arithmetic operation. This mostly consists of wrapping
2460 /// behavior for strange integers:
2461 /// - Unsigned integers are bitwise masked with a mask that only passes
2462 /// the valid bits through.
2463 /// - Signed integers are also sign extended if they are negative.
2464 /// All other values are returned unmodified (this makes strange integer
2465 /// wrapping easier to use in generic operations).
2466 fn normalize(self: *DeclGen, ty_ref: CacheRef, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef {
2467 switch (info.class) {
2468 .integer, .bool, .float => return value_id,
2469 .composite_integer => unreachable, // TODO
2470 .strange_integer => switch (info.signedness) {
2471 .unsigned => {
2472 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
2473 const result_id = self.spv.allocId();
2474 const mask_id = try self.constInt(ty_ref, mask_value);
2475 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{
2476 .id_result_type = self.typeId(ty_ref),
2477 .id_result = result_id,
2478 .operand_1 = value_id,
2479 .operand_2 = mask_id,
2480 });
2481 return result_id;
2482 },
2483 .signed => {
2484 // Shift left and right so that we can copy the sight bit that way.
2485 const shift_amt_id = try self.constInt(ty_ref, info.backing_bits - info.bits);
2486 const left_id = self.spv.allocId();
2487 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{
2488 .id_result_type = self.typeId(ty_ref),
2489 .id_result = left_id,
2490 .base = value_id,
2491 .shift = shift_amt_id,
2492 });
2493 const right_id = self.spv.allocId();
2494 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{
2495 .id_result_type = self.typeId(ty_ref),
2496 .id_result = right_id,
2497 .base = left_id,
2498 .shift = shift_amt_id,
2499 });
2500 return right_id;
2501 },
23712502 },
23722503 }
23732504 }
......@@ -2378,8 +2509,6 @@ const DeclGen = struct {
23782509 comptime fop: Opcode,
23792510 comptime sop: Opcode,
23802511 comptime uop: Opcode,
2381 /// true if this operation holds under modular arithmetic.
2382 comptime modular: bool,
23832512 ) !?IdRef {
23842513 if (self.liveness.isUnused(inst)) return null;
23852514
......@@ -2393,60 +2522,27 @@ const DeclGen = struct {
23932522 assert(self.typeOf(bin_op.lhs).eql(ty, self.module));
23942523 assert(self.typeOf(bin_op.rhs).eql(ty, self.module));
23952524
2396 return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop, modular);
2525 return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop);
23972526 }
23982527
23992528 fn arithOp(
24002529 self: *DeclGen,
24012530 ty: Type,
2402 lhs_id_: IdRef,
2403 rhs_id_: IdRef,
2531 lhs_id: IdRef,
2532 rhs_id: IdRef,
24042533 comptime fop: Opcode,
24052534 comptime sop: Opcode,
24062535 comptime uop: Opcode,
2407 /// true if this operation holds under modular arithmetic.
2408 comptime modular: bool,
24092536 ) !IdRef {
2410 var rhs_id = rhs_id_;
2411 var lhs_id = lhs_id_;
2412
2413 const mod = self.module;
2414 const result_ty_ref = try self.resolveType(ty, .direct);
2415
2416 if (ty.isVector(mod)) {
2417 const child_ty = ty.childType(mod);
2418 const vector_len = ty.vectorLen(mod);
2419 const constituents = try self.gpa.alloc(IdRef, vector_len);
2420 defer self.gpa.free(constituents);
2421
2422 for (constituents, 0..) |*constituent, i| {
2423 const lhs_index_id = try self.extractField(child_ty, lhs_id, @intCast(i));
2424 const rhs_index_id = try self.extractField(child_ty, rhs_id, @intCast(i));
2425 constituent.* = try self.arithOp(child_ty, lhs_index_id, rhs_index_id, fop, sop, uop, modular);
2426 }
2427
2428 return self.constructArray(ty, constituents);
2429 }
2430
24312537 // Binary operations are generally applicable to both scalar and vector operations
24322538 // in SPIR-V, but int and float versions of operations require different opcodes.
2433 const info = try self.arithmeticTypeInfo(ty);
2539 const info = self.arithmeticTypeInfo(ty);
24342540
24352541 const opcode_index: usize = switch (info.class) {
24362542 .composite_integer => {
24372543 return self.todo("binary operations for composite integers", .{});
24382544 },
2439 .strange_integer => blk: {
2440 if (!modular) {
2441 lhs_id = try self.normalizeInt(result_ty_ref, lhs_id, info);
2442 rhs_id = try self.normalizeInt(result_ty_ref, rhs_id, info);
2443 }
2444 break :blk switch (info.signedness) {
2445 .signed => @as(usize, 1),
2446 .unsigned => @as(usize, 2),
2447 };
2448 },
2449 .integer => switch (info.signedness) {
2545 .integer, .strange_integer => switch (info.signedness) {
24502546 .signed => @as(usize, 1),
24512547 .unsigned => @as(usize, 2),
24522548 },
......@@ -2454,24 +2550,91 @@ const DeclGen = struct {
24542550 .bool => unreachable,
24552551 };
24562552
2457 const result_id = self.spv.allocId();
2458 const operands = .{
2459 .id_result_type = self.typeId(result_ty_ref),
2460 .id_result = result_id,
2461 .operand_1 = lhs_id,
2462 .operand_2 = rhs_id,
2463 };
2553 var wip = try self.elementWise(ty);
2554 defer wip.deinit();
2555 for (wip.results, 0..) |*result_id, i| {
2556 const lhs_elem_id = try wip.elementAt(ty, lhs_id, i);
2557 const rhs_elem_id = try wip.elementAt(ty, rhs_id, i);
2558
2559 const value_id = self.spv.allocId();
2560 const operands = .{
2561 .id_result_type = wip.scalar_ty_id,
2562 .id_result = value_id,
2563 .operand_1 = lhs_elem_id,
2564 .operand_2 = rhs_elem_id,
2565 };
24642566
2465 switch (opcode_index) {
2466 0 => try self.func.body.emit(self.spv.gpa, fop, operands),
2467 1 => try self.func.body.emit(self.spv.gpa, sop, operands),
2468 2 => try self.func.body.emit(self.spv.gpa, uop, operands),
2469 else => unreachable,
2567 switch (opcode_index) {
2568 0 => try self.func.body.emit(self.spv.gpa, fop, operands),
2569 1 => try self.func.body.emit(self.spv.gpa, sop, operands),
2570 2 => try self.func.body.emit(self.spv.gpa, uop, operands),
2571 else => unreachable,
2572 }
2573
2574 // TODO: Trap on overflow? Probably going to be annoying.
2575 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
2576 result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info);
24702577 }
2471 // TODO: Trap on overflow? Probably going to be annoying.
2472 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
24732578
2474 return result_id;
2579 return try wip.finalize();
2580 }
2581
2582 fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2583 if (self.liveness.isUnused(inst)) return null;
2584
2585 const mod = self.module;
2586 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2587 const operand_id = try self.resolve(ty_op.operand);
2588 // Note: operand_ty may be signed, while ty is always unsigned!
2589 const operand_ty = self.typeOf(ty_op.operand);
2590 const ty = self.typeOfIndex(inst);
2591 const info = self.arithmeticTypeInfo(ty);
2592 const operand_scalar_ty = operand_ty.scalarType(mod);
2593 const operand_scalar_ty_ref = try self.resolveType(operand_scalar_ty, .direct);
2594
2595 var wip = try self.elementWise(ty);
2596 defer wip.deinit();
2597
2598 const zero_id = switch (info.class) {
2599 .float => try self.constFloat(operand_scalar_ty_ref, 0),
2600 .integer, .strange_integer => try self.constInt(operand_scalar_ty_ref, 0),
2601 .composite_integer => unreachable, // TODO
2602 .bool => unreachable,
2603 };
2604 for (wip.results, 0..) |*result_id, i| {
2605 const elem_id = try wip.elementAt(operand_ty, operand_id, i);
2606 // Idk why spir-v doesn't have a dedicated abs() instruction in the base
2607 // instruction set. For now we're just going to negate and check to avoid
2608 // importing the extinst.
2609 // TODO: Make this a call to compiler rt / ext inst
2610 const neg_id = self.spv.allocId();
2611 const args = .{
2612 .id_result_type = self.typeId(operand_scalar_ty_ref),
2613 .id_result = neg_id,
2614 .operand_1 = zero_id,
2615 .operand_2 = elem_id,
2616 };
2617 switch (info.class) {
2618 .float => try self.func.body.emit(self.spv.gpa, .OpFSub, args),
2619 .integer, .strange_integer => try self.func.body.emit(self.spv.gpa, .OpISub, args),
2620 .composite_integer => unreachable, // TODO
2621 .bool => unreachable,
2622 }
2623 const neg_norm_id = try self.normalize(wip.scalar_ty_ref, neg_id, info);
2624
2625 const gt_zero_id = try self.cmp(.gt, Type.bool, operand_scalar_ty, elem_id, zero_id);
2626 const abs_id = self.spv.allocId();
2627 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
2628 .id_result_type = self.typeId(operand_scalar_ty_ref),
2629 .id_result = abs_id,
2630 .condition = gt_zero_id,
2631 .object_1 = elem_id,
2632 .object_2 = neg_norm_id,
2633 });
2634 // For Shader, we may need to cast from signed to unsigned here.
2635 result_id.* = try self.bitCast(wip.scalar_ty, operand_scalar_ty, abs_id);
2636 }
2637 return try wip.finalize();
24752638 }
24762639
24772640 fn airAddSubOverflow(
......@@ -2488,140 +2651,344 @@ const DeclGen = struct {
24882651 const lhs = try self.resolve(extra.lhs);
24892652 const rhs = try self.resolve(extra.rhs);
24902653
2491 const operand_ty = self.typeOf(extra.lhs);
24922654 const result_ty = self.typeOfIndex(inst);
2655 const operand_ty = self.typeOf(extra.lhs);
2656 const ov_ty = result_ty.structFieldType(1, self.module);
2657
2658 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
24932659
2494 const info = try self.arithmeticTypeInfo(operand_ty);
2660 const info = self.arithmeticTypeInfo(operand_ty);
24952661 switch (info.class) {
24962662 .composite_integer => return self.todo("overflow ops for composite integers", .{}),
2497 .strange_integer => return self.todo("overflow ops for strange integers", .{}),
2498 .integer => {},
2663 .strange_integer, .integer => {},
24992664 .float, .bool => unreachable,
25002665 }
25012666
2502 // The operand type must be the same as the result type in SPIR-V, which
2503 // is the same as in Zig.
2504 const operand_ty_ref = try self.resolveType(operand_ty, .direct);
2505 const operand_ty_id = self.typeId(operand_ty_ref);
2667 var wip_result = try self.elementWise(operand_ty);
2668 defer wip_result.deinit();
2669 var wip_ov = try self.elementWise(ov_ty);
2670 defer wip_ov.deinit();
2671 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| {
2672 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i);
2673 const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i);
2674
2675 // Normalize both so that we can properly check for overflow
2676 const value_id = self.spv.allocId();
2677
2678 try self.func.body.emit(self.spv.gpa, add, .{
2679 .id_result_type = wip_result.scalar_ty_id,
2680 .id_result = value_id,
2681 .operand_1 = lhs_elem_id,
2682 .operand_2 = rhs_elem_id,
2683 });
25062684
2507 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
2685 // Normalize the result so that the comparisons go well
2686 result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info);
2687
2688 const overflowed_id = switch (info.signedness) {
2689 .unsigned => blk: {
2690 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.
2691 // For subtraction the conditions need to be swapped.
2692 const overflowed_id = self.spv.allocId();
2693 try self.func.body.emit(self.spv.gpa, ucmp, .{
2694 .id_result_type = self.typeId(bool_ty_ref),
2695 .id_result = overflowed_id,
2696 .operand_1 = result_id.*,
2697 .operand_2 = lhs_elem_id,
2698 });
2699 break :blk overflowed_id;
2700 },
2701 .signed => blk: {
2702 // lhs - rhs
2703 // For addition, overflow happened if:
2704 // - rhs is negative and value > lhs
2705 // - rhs is positive and value < lhs
2706 // This can be shortened to:
2707 // (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs)
2708 // = (rhs < 0) == (value > lhs)
2709 // = (rhs < 0) == (lhs < value)
2710 // Note that signed overflow is also wrapping in spir-v.
2711 // For subtraction, overflow happened if:
2712 // - rhs is negative and value < lhs
2713 // - rhs is positive and value > lhs
2714 // This can be shortened to:
2715 // (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs)
2716 // = (rhs < 0) == (value < lhs)
2717 // = (rhs < 0) == (lhs > value)
2718
2719 const rhs_lt_zero_id = self.spv.allocId();
2720 const zero_id = try self.constInt(wip_result.scalar_ty_ref, 0);
2721 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{
2722 .id_result_type = self.typeId(bool_ty_ref),
2723 .id_result = rhs_lt_zero_id,
2724 .operand_1 = rhs_elem_id,
2725 .operand_2 = zero_id,
2726 });
2727
2728 const value_gt_lhs_id = self.spv.allocId();
2729 try self.func.body.emit(self.spv.gpa, scmp, .{
2730 .id_result_type = self.typeId(bool_ty_ref),
2731 .id_result = value_gt_lhs_id,
2732 .operand_1 = lhs_elem_id,
2733 .operand_2 = result_id.*,
2734 });
2735
2736 const overflowed_id = self.spv.allocId();
2737 try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{
2738 .id_result_type = self.typeId(bool_ty_ref),
2739 .id_result = overflowed_id,
2740 .operand_1 = rhs_lt_zero_id,
2741 .operand_2 = value_gt_lhs_id,
2742 });
2743 break :blk overflowed_id;
2744 },
2745 };
2746
2747 ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id);
2748 }
2749
2750 return try self.constructStruct(
2751 result_ty,
2752 &.{ operand_ty, ov_ty },
2753 &.{ try wip_result.finalize(), try wip_ov.finalize() },
2754 );
2755 }
2756
2757 fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2758 if (self.liveness.isUnused(inst)) return null;
2759 const mod = self.module;
2760 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2761 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2762 const lhs = try self.resolve(extra.lhs);
2763 const rhs = try self.resolve(extra.rhs);
2764
2765 const result_ty = self.typeOfIndex(inst);
2766 const operand_ty = self.typeOf(extra.lhs);
2767 const shift_ty = self.typeOf(extra.rhs);
2768 const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct);
25082769
25092770 const ov_ty = result_ty.structFieldType(1, self.module);
2510 // Note: result is stored in a struct, so indirect representation.
2511 const ov_ty_ref = try self.resolveType(ov_ty, .indirect);
2512
2513 // TODO: Operations other than addition.
2514 const value_id = self.spv.allocId();
2515 try self.func.body.emit(self.spv.gpa, add, .{
2516 .id_result_type = operand_ty_id,
2517 .id_result = value_id,
2518 .operand_1 = lhs,
2519 .operand_2 = rhs,
2520 });
25212771
2522 const overflowed_id = switch (info.signedness) {
2523 .unsigned => blk: {
2524 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.
2525 // For subtraction the conditions need to be swapped.
2526 const overflowed_id = self.spv.allocId();
2527 try self.func.body.emit(self.spv.gpa, ucmp, .{
2528 .id_result_type = self.typeId(bool_ty_ref),
2529 .id_result = overflowed_id,
2530 .operand_1 = value_id,
2531 .operand_2 = lhs,
2532 });
2533 break :blk overflowed_id;
2534 },
2535 .signed => blk: {
2536 // lhs - rhs
2537 // For addition, overflow happened if:
2538 // - rhs is negative and value > lhs
2539 // - rhs is positive and value < lhs
2540 // This can be shortened to:
2541 // (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs)
2542 // = (rhs < 0) == (value > lhs)
2543 // = (rhs < 0) == (lhs < value)
2544 // Note that signed overflow is also wrapping in spir-v.
2545 // For subtraction, overflow happened if:
2546 // - rhs is negative and value < lhs
2547 // - rhs is positive and value > lhs
2548 // This can be shortened to:
2549 // (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs)
2550 // = (rhs < 0) == (value < lhs)
2551 // = (rhs < 0) == (lhs > value)
2552
2553 const rhs_lt_zero_id = self.spv.allocId();
2554 const zero_id = try self.constInt(operand_ty_ref, 0);
2555 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{
2556 .id_result_type = self.typeId(bool_ty_ref),
2557 .id_result = rhs_lt_zero_id,
2558 .operand_1 = rhs,
2559 .operand_2 = zero_id,
2560 });
2772 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
25612773
2562 const value_gt_lhs_id = self.spv.allocId();
2563 try self.func.body.emit(self.spv.gpa, scmp, .{
2564 .id_result_type = self.typeId(bool_ty_ref),
2565 .id_result = value_gt_lhs_id,
2566 .operand_1 = lhs,
2567 .operand_2 = value_id,
2568 });
2774 const info = self.arithmeticTypeInfo(operand_ty);
2775 switch (info.class) {
2776 .composite_integer => return self.todo("overflow shift for composite integers", .{}),
2777 .integer, .strange_integer => {},
2778 .float, .bool => unreachable,
2779 }
25692780
2570 const overflowed_id = self.spv.allocId();
2571 try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{
2572 .id_result_type = self.typeId(bool_ty_ref),
2573 .id_result = overflowed_id,
2574 .operand_1 = rhs_lt_zero_id,
2575 .operand_2 = value_gt_lhs_id,
2781 var wip_result = try self.elementWise(operand_ty);
2782 defer wip_result.deinit();
2783 var wip_ov = try self.elementWise(ov_ty);
2784 defer wip_ov.deinit();
2785 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| {
2786 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i);
2787 const rhs_elem_id = try wip_result.elementAt(shift_ty, rhs, i);
2788
2789 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
2790 // so just manually upcast it if required.
2791 const shift_id = if (scalar_shift_ty_ref != wip_result.scalar_ty_ref) blk: {
2792 const shift_id = self.spv.allocId();
2793 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
2794 .id_result_type = wip_result.scalar_ty_id,
2795 .id_result = shift_id,
2796 .unsigned_value = rhs_elem_id,
25762797 });
2577 break :blk overflowed_id;
2578 },
2579 };
2798 break :blk shift_id;
2799 } else rhs_elem_id;
2800
2801 const value_id = self.spv.allocId();
2802 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{
2803 .id_result_type = wip_result.scalar_ty_id,
2804 .id_result = value_id,
2805 .base = lhs_elem_id,
2806 .shift = shift_id,
2807 });
2808 result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info);
2809
2810 const right_shift_id = self.spv.allocId();
2811 switch (info.signedness) {
2812 .signed => {
2813 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{
2814 .id_result_type = wip_result.scalar_ty_id,
2815 .id_result = right_shift_id,
2816 .base = result_id.*,
2817 .shift = shift_id,
2818 });
2819 },
2820 .unsigned => {
2821 try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{
2822 .id_result_type = wip_result.scalar_ty_id,
2823 .id_result = right_shift_id,
2824 .base = result_id.*,
2825 .shift = shift_id,
2826 });
2827 },
2828 }
2829
2830 const overflowed_id = self.spv.allocId();
2831 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
2832 .id_result_type = self.typeId(bool_ty_ref),
2833 .id_result = overflowed_id,
2834 .operand_1 = lhs_elem_id,
2835 .operand_2 = right_shift_id,
2836 });
2837
2838 ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id);
2839 }
25802840
2581 // Construct the struct that Zig wants as result.
2582 // The value should already be the correct type.
2583 const ov_id = try self.intFromBool(ov_ty_ref, overflowed_id);
25842841 return try self.constructStruct(
25852842 result_ty,
25862843 &.{ operand_ty, ov_ty },
2587 &.{ value_id, ov_id },
2844 &.{ try wip_result.finalize(), try wip_ov.finalize() },
25882845 );
25892846 }
25902847
2848 fn airMulAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2849 if (self.liveness.isUnused(inst)) return null;
2850
2851 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
2852 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
2853
2854 const mulend1 = try self.resolve(extra.lhs);
2855 const mulend2 = try self.resolve(extra.rhs);
2856 const addend = try self.resolve(pl_op.operand);
2857
2858 const ty = self.typeOfIndex(inst);
2859
2860 const info = self.arithmeticTypeInfo(ty);
2861 assert(info.class == .float); // .mul_add is only emitted for floats
2862
2863 var wip = try self.elementWise(ty);
2864 defer wip.deinit();
2865 for (0..wip.results.len) |i| {
2866 const mul_result = self.spv.allocId();
2867 try self.func.body.emit(self.spv.gpa, .OpFMul, .{
2868 .id_result_type = wip.scalar_ty_id,
2869 .id_result = mul_result,
2870 .operand_1 = try wip.elementAt(ty, mulend1, i),
2871 .operand_2 = try wip.elementAt(ty, mulend2, i),
2872 });
2873
2874 try self.func.body.emit(self.spv.gpa, .OpFAdd, .{
2875 .id_result_type = wip.scalar_ty_id,
2876 .id_result = wip.allocId(i),
2877 .operand_1 = mul_result,
2878 .operand_2 = try wip.elementAt(ty, addend, i),
2879 });
2880 }
2881 return try wip.finalize();
2882 }
2883
2884 fn airSplat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2885 if (self.liveness.isUnused(inst)) return null;
2886 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2887 const operand_id = try self.resolve(ty_op.operand);
2888 const result_ty = self.typeOfIndex(inst);
2889 var wip = try self.elementWise(result_ty);
2890 defer wip.deinit();
2891 for (wip.results) |*result_id| {
2892 result_id.* = operand_id;
2893 }
2894 return try wip.finalize();
2895 }
2896
2897 fn airReduce(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2898 if (self.liveness.isUnused(inst)) return null;
2899 const mod = self.module;
2900 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce;
2901 const operand = try self.resolve(reduce.operand);
2902 const operand_ty = self.typeOf(reduce.operand);
2903 const scalar_ty = operand_ty.scalarType(mod);
2904 const scalar_ty_ref = try self.resolveType(scalar_ty, .direct);
2905 const scalar_ty_id = self.typeId(scalar_ty_ref);
2906
2907 const info = self.arithmeticTypeInfo(operand_ty);
2908
2909 var result_id = try self.extractField(scalar_ty, operand, 0);
2910 const len = operand_ty.vectorLen(mod);
2911
2912 switch (reduce.operation) {
2913 .Min, .Max => |op| {
2914 const cmp_op: std.math.CompareOperator = if (op == .Max) .gt else .lt;
2915 for (1..len) |i| {
2916 const lhs = result_id;
2917 const rhs = try self.extractField(scalar_ty, operand, @intCast(i));
2918 result_id = try self.minMax(scalar_ty, cmp_op, lhs, rhs);
2919 }
2920
2921 return result_id;
2922 },
2923 else => {},
2924 }
2925
2926 const opcode: Opcode = switch (info.class) {
2927 .bool => switch (reduce.operation) {
2928 .And => .OpLogicalAnd,
2929 .Or => .OpLogicalOr,
2930 .Xor => .OpLogicalNotEqual,
2931 else => unreachable,
2932 },
2933 .strange_integer, .integer => switch (reduce.operation) {
2934 .And => .OpBitwiseAnd,
2935 .Or => .OpBitwiseOr,
2936 .Xor => .OpBitwiseXor,
2937 .Add => .OpIAdd,
2938 .Mul => .OpIMul,
2939 else => unreachable,
2940 },
2941 .float => switch (reduce.operation) {
2942 .Add => .OpFAdd,
2943 .Mul => .OpFMul,
2944 else => unreachable,
2945 },
2946 .composite_integer => unreachable, // TODO
2947 };
2948
2949 for (1..len) |i| {
2950 const lhs = result_id;
2951 const rhs = try self.extractField(scalar_ty, operand, @intCast(i));
2952 result_id = self.spv.allocId();
2953
2954 try self.func.body.emitRaw(self.spv.gpa, opcode, 4);
2955 self.func.body.writeOperand(spec.IdResultType, scalar_ty_id);
2956 self.func.body.writeOperand(spec.IdResult, result_id);
2957 self.func.body.writeOperand(spec.IdResultType, lhs);
2958 self.func.body.writeOperand(spec.IdResultType, rhs);
2959 }
2960
2961 return result_id;
2962 }
2963
25912964 fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
25922965 const mod = self.module;
25932966 if (self.liveness.isUnused(inst)) return null;
2594 const ty = self.typeOfIndex(inst);
25952967 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
25962968 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data;
25972969 const a = try self.resolve(extra.a);
25982970 const b = try self.resolve(extra.b);
25992971 const mask = Value.fromInterned(extra.mask);
2600 const mask_len = extra.mask_len;
2601 const a_len = self.typeOf(extra.a).vectorLen(mod);
26022972
2603 const result_id = self.spv.allocId();
2604 const result_type_id = try self.resolveTypeId(ty);
2605 // Similar to LLVM, SPIR-V uses indices larger than the length of the first vector
2606 // to index into the second vector.
2607 try self.func.body.emitRaw(self.spv.gpa, .OpVectorShuffle, 4 + mask_len);
2608 self.func.body.writeOperand(spec.IdResultType, result_type_id);
2609 self.func.body.writeOperand(spec.IdResult, result_id);
2610 self.func.body.writeOperand(spec.IdRef, a);
2611 self.func.body.writeOperand(spec.IdRef, b);
2973 const ty = self.typeOfIndex(inst);
26122974
2613 var i: usize = 0;
2614 while (i < mask_len) : (i += 1) {
2975 var wip = try self.elementWise(ty);
2976 defer wip.deinit();
2977 for (wip.results, 0..) |*result_id, i| {
26152978 const elem = try mask.elemValue(mod, i);
26162979 if (elem.isUndef(mod)) {
2617 self.func.body.writeOperand(spec.LiteralInteger, 0xFFFF_FFFF);
2980 result_id.* = try self.spv.constUndef(wip.scalar_ty_ref);
2981 continue;
2982 }
2983
2984 const index = elem.toSignedInt(mod);
2985 if (index >= 0) {
2986 result_id.* = try self.extractField(wip.scalar_ty, a, @intCast(index));
26182987 } else {
2619 const int = elem.toSignedInt(mod);
2620 const unsigned = if (int >= 0) @as(u32, @intCast(int)) else @as(u32, @intCast(~int + a_len));
2621 self.func.body.writeOperand(spec.LiteralInteger, unsigned);
2988 result_id.* = try self.extractField(wip.scalar_ty, b, @intCast(~index));
26222989 }
26232990 }
2624 return result_id;
2991 return try wip.finalize();
26252992 }
26262993
26272994 fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef {
......@@ -2828,26 +3195,21 @@ const DeclGen = struct {
28283195 return result_id;
28293196 },
28303197 .Vector => {
2831 const child_ty = ty.childType(mod);
2832 const vector_len = ty.vectorLen(mod);
2833
2834 const constituents = try self.gpa.alloc(IdRef, vector_len);
2835 defer self.gpa.free(constituents);
2836
2837 for (constituents, 0..) |*constituent, i| {
2838 const lhs_index_id = try self.extractField(child_ty, cmp_lhs_id, @intCast(i));
2839 const rhs_index_id = try self.extractField(child_ty, cmp_rhs_id, @intCast(i));
2840 const result_id = try self.cmp(op, Type.bool, child_ty, lhs_index_id, rhs_index_id);
2841 constituent.* = try self.convertToIndirect(Type.bool, result_id);
3198 var wip = try self.elementWise(result_ty);
3199 defer wip.deinit();
3200 const scalar_ty = ty.scalarType(mod);
3201 for (wip.results, 0..) |*result_id, i| {
3202 const lhs_elem_id = try wip.elementAt(ty, lhs_id, i);
3203 const rhs_elem_id = try wip.elementAt(ty, rhs_id, i);
3204 result_id.* = try self.cmp(op, Type.bool, scalar_ty, lhs_elem_id, rhs_elem_id);
28423205 }
2843
2844 return try self.constructArray(result_ty, constituents);
3206 return wip.finalize();
28453207 },
28463208 else => unreachable,
28473209 };
28483210
28493211 const opcode: Opcode = opcode: {
2850 const info = try self.arithmeticTypeInfo(op_ty);
3212 const info = self.arithmeticTypeInfo(op_ty);
28513213 const signedness = switch (info.class) {
28523214 .composite_integer => {
28533215 return self.todo("binary operations for composite integers", .{});
......@@ -2865,14 +3227,7 @@ const DeclGen = struct {
28653227 .neq => .OpLogicalNotEqual,
28663228 else => unreachable,
28673229 },
2868 .strange_integer => sign: {
2869 const op_ty_ref = try self.resolveType(op_ty, .direct);
2870 // Mask operands before performing comparison.
2871 cmp_lhs_id = try self.normalizeInt(op_ty_ref, cmp_lhs_id, info);
2872 cmp_rhs_id = try self.normalizeInt(op_ty_ref, cmp_rhs_id, info);
2873 break :sign info.signedness;
2874 },
2875 .integer => info.signedness,
3230 .integer, .strange_integer => info.signedness,
28763231 };
28773232
28783233 break :opcode switch (signedness) {
......@@ -2942,50 +3297,64 @@ const DeclGen = struct {
29423297 const mod = self.module;
29433298 const src_ty_ref = try self.resolveType(src_ty, .direct);
29443299 const dst_ty_ref = try self.resolveType(dst_ty, .direct);
2945 if (src_ty_ref == dst_ty_ref) {
2946 return src_id;
2947 }
3300 const src_key = self.spv.cache.lookup(src_ty_ref);
3301 const dst_key = self.spv.cache.lookup(dst_ty_ref);
29483302
2949 // TODO: Some more cases are missing here
2950 // See fn bitCast in llvm.zig
3303 const result_id = blk: {
3304 if (src_ty_ref == dst_ty_ref) {
3305 break :blk src_id;
3306 }
29513307
2952 if (src_ty.zigTypeTag(mod) == .Int and dst_ty.isPtrAtRuntime(mod)) {
2953 const result_id = self.spv.allocId();
2954 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{
2955 .id_result_type = self.typeId(dst_ty_ref),
2956 .id_result = result_id,
2957 .integer_value = src_id,
2958 });
2959 return result_id;
2960 }
3308 // TODO: Some more cases are missing here
3309 // See fn bitCast in llvm.zig
29613310
2962 // We can only use OpBitcast for specific conversions: between numerical types, and
2963 // between pointers. If the resolved spir-v types fall into this category then emit OpBitcast,
2964 // otherwise use a temporary and perform a pointer cast.
2965 const src_key = self.spv.cache.lookup(src_ty_ref);
2966 const dst_key = self.spv.cache.lookup(dst_ty_ref);
3311 if (src_ty.zigTypeTag(mod) == .Int and dst_ty.isPtrAtRuntime(mod)) {
3312 const result_id = self.spv.allocId();
3313 try self.func.body.emit(self.spv.gpa, .OpConvertUToPtr, .{
3314 .id_result_type = self.typeId(dst_ty_ref),
3315 .id_result = result_id,
3316 .integer_value = src_id,
3317 });
3318 break :blk result_id;
3319 }
29673320
2968 if ((src_key.isNumericalType() and dst_key.isNumericalType()) or (src_key == .ptr_type and dst_key == .ptr_type)) {
2969 const result_id = self.spv.allocId();
3321 // We can only use OpBitcast for specific conversions: between numerical types, and
3322 // between pointers. If the resolved spir-v types fall into this category then emit OpBitcast,
3323 // otherwise use a temporary and perform a pointer cast.
3324 if ((src_key.isNumericalType() and dst_key.isNumericalType()) or (src_key == .ptr_type and dst_key == .ptr_type)) {
3325 const result_id = self.spv.allocId();
3326 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
3327 .id_result_type = self.typeId(dst_ty_ref),
3328 .id_result = result_id,
3329 .operand = src_id,
3330 });
3331
3332 break :blk result_id;
3333 }
3334
3335 const dst_ptr_ty_ref = try self.ptrType(dst_ty, .Function);
3336
3337 const tmp_id = try self.alloc(src_ty, .{ .storage_class = .Function });
3338 try self.store(src_ty, tmp_id, src_id, .{});
3339 const casted_ptr_id = self.spv.allocId();
29703340 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
2971 .id_result_type = self.typeId(dst_ty_ref),
2972 .id_result = result_id,
2973 .operand = src_id,
3341 .id_result_type = self.typeId(dst_ptr_ty_ref),
3342 .id_result = casted_ptr_id,
3343 .operand = tmp_id,
29743344 });
2975 return result_id;
2976 }
3345 break :blk try self.load(dst_ty, casted_ptr_id, .{});
3346 };
29773347
2978 const dst_ptr_ty_ref = try self.ptrType(dst_ty, .Function);
3348 // Because strange integers use sign-extended representation, we may need to normalize
3349 // the result here.
3350 // TODO: This detail could cause stuff like @as(*const i1, @ptrCast(&@as(u1, 1))) to break
3351 // should we change the representation of strange integers?
3352 if (dst_ty.zigTypeTag(mod) == .Int) {
3353 const info = self.arithmeticTypeInfo(dst_ty);
3354 return try self.normalize(dst_ty_ref, result_id, info);
3355 }
29793356
2980 const tmp_id = try self.alloc(src_ty, .{ .storage_class = .Function });
2981 try self.store(src_ty, tmp_id, src_id, .{});
2982 const casted_ptr_id = self.spv.allocId();
2983 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
2984 .id_result_type = self.typeId(dst_ptr_ty_ref),
2985 .id_result = casted_ptr_id,
2986 .operand = tmp_id,
2987 });
2988 return try self.load(dst_ty, casted_ptr_id, .{});
3357 return result_id;
29893358 }
29903359
29913360 fn airBitCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
......@@ -3004,34 +3373,43 @@ const DeclGen = struct {
30043373 const operand_id = try self.resolve(ty_op.operand);
30053374 const src_ty = self.typeOf(ty_op.operand);
30063375 const dst_ty = self.typeOfIndex(inst);
3007 const src_ty_ref = try self.resolveType(src_ty, .direct);
3008 const dst_ty_ref = try self.resolveType(dst_ty, .direct);
3009
3010 const src_info = try self.arithmeticTypeInfo(src_ty);
3011 const dst_info = try self.arithmeticTypeInfo(dst_ty);
30123376
3013 // While intcast promises that the value already fits, the upper bits of a
3014 // strange integer may contain garbage. Therefore, mask/sign extend it before.
3015 const src_id = try self.normalizeInt(src_ty_ref, operand_id, src_info);
3377 const src_info = self.arithmeticTypeInfo(src_ty);
3378 const dst_info = self.arithmeticTypeInfo(dst_ty);
30163379
30173380 if (src_info.backing_bits == dst_info.backing_bits) {
3018 return src_id;
3381 return operand_id;
30193382 }
30203383
3021 const result_id = self.spv.allocId();
3022 switch (dst_info.signedness) {
3023 .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{
3024 .id_result_type = self.typeId(dst_ty_ref),
3025 .id_result = result_id,
3026 .signed_value = src_id,
3027 }),
3028 .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
3029 .id_result_type = self.typeId(dst_ty_ref),
3030 .id_result = result_id,
3031 .unsigned_value = src_id,
3032 }),
3384 var wip = try self.elementWise(dst_ty);
3385 defer wip.deinit();
3386 for (wip.results, 0..) |*result_id, i| {
3387 const elem_id = try wip.elementAt(src_ty, operand_id, i);
3388 const value_id = self.spv.allocId();
3389 switch (dst_info.signedness) {
3390 .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{
3391 .id_result_type = wip.scalar_ty_id,
3392 .id_result = value_id,
3393 .signed_value = elem_id,
3394 }),
3395 .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
3396 .id_result_type = wip.scalar_ty_id,
3397 .id_result = value_id,
3398 .unsigned_value = elem_id,
3399 }),
3400 }
3401
3402 // Make sure to normalize the result if shrinking.
3403 // Because strange ints are sign extended in their backing
3404 // type, we don't need to normalize when growing the type. The
3405 // representation is already the same.
3406 if (dst_info.bits < src_info.bits) {
3407 result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, dst_info);
3408 } else {
3409 result_id.* = value_id;
3410 }
30333411 }
3034 return result_id;
3412 return try wip.finalize();
30353413 }
30363414
30373415 fn intFromPtr(self: *DeclGen, operand_id: IdRef) !IdRef {
......@@ -3059,7 +3437,7 @@ const DeclGen = struct {
30593437 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
30603438 const operand_ty = self.typeOf(ty_op.operand);
30613439 const operand_id = try self.resolve(ty_op.operand);
3062 const operand_info = try self.arithmeticTypeInfo(operand_ty);
3440 const operand_info = self.arithmeticTypeInfo(operand_ty);
30633441 const dest_ty = self.typeOfIndex(inst);
30643442 const dest_ty_id = try self.resolveTypeId(dest_ty);
30653443
......@@ -3085,7 +3463,7 @@ const DeclGen = struct {
30853463 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
30863464 const operand_id = try self.resolve(ty_op.operand);
30873465 const dest_ty = self.typeOfIndex(inst);
3088 const dest_info = try self.arithmeticTypeInfo(dest_ty);
3466 const dest_info = self.arithmeticTypeInfo(dest_ty);
30893467 const dest_ty_id = try self.resolveTypeId(dest_ty);
30903468
30913469 const result_id = self.spv.allocId();
......@@ -3104,6 +3482,22 @@ const DeclGen = struct {
31043482 return result_id;
31053483 }
31063484
3485 fn airIntFromBool(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3486 if (self.liveness.isUnused(inst)) return null;
3487
3488 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3489 const operand_id = try self.resolve(un_op);
3490 const result_ty = self.typeOfIndex(inst);
3491
3492 var wip = try self.elementWise(result_ty);
3493 defer wip.deinit();
3494 for (wip.results, 0..) |*result_id, i| {
3495 const elem_id = try wip.elementAt(Type.bool, operand_id, i);
3496 result_id.* = try self.intFromBool(wip.scalar_ty_ref, elem_id);
3497 }
3498 return try wip.finalize();
3499 }
3500
31073501 fn airFloatCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
31083502 if (self.liveness.isUnused(inst)) return null;
31093503
......@@ -3126,31 +3520,31 @@ const DeclGen = struct {
31263520 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
31273521 const operand_id = try self.resolve(ty_op.operand);
31283522 const result_ty = self.typeOfIndex(inst);
3129 const result_ty_id = try self.resolveTypeId(result_ty);
3130 const info = try self.arithmeticTypeInfo(result_ty);
3523 const info = self.arithmeticTypeInfo(result_ty);
31313524
3132 const result_id = self.spv.allocId();
3133 switch (info.class) {
3134 .bool => {
3135 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{
3136 .id_result_type = result_ty_id,
3137 .id_result = result_id,
3138 .operand = operand_id,
3139 });
3140 },
3141 .float => unreachable,
3142 .composite_integer => unreachable, // TODO
3143 .strange_integer, .integer => {
3144 // Note: strange integer bits will be masked before operations that do not hold under modulo.
3145 try self.func.body.emit(self.spv.gpa, .OpNot, .{
3146 .id_result_type = result_ty_id,
3147 .id_result = result_id,
3148 .operand = operand_id,
3149 });
3150 },
3525 var wip = try self.elementWise(result_ty);
3526 defer wip.deinit();
3527
3528 for (0..wip.results.len) |i| {
3529 const args = .{
3530 .id_result_type = wip.scalar_ty_id,
3531 .id_result = wip.allocId(i),
3532 .operand = try wip.elementAt(result_ty, operand_id, i),
3533 };
3534 switch (info.class) {
3535 .bool => {
3536 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, args);
3537 },
3538 .float => unreachable,
3539 .composite_integer => unreachable, // TODO
3540 .strange_integer, .integer => {
3541 // Note: strange integer bits will be masked before operations that do not hold under modulo.
3542 try self.func.body.emit(self.spv.gpa, .OpNot, args);
3543 },
3544 }
31513545 }
31523546
3153 return result_id;
3547 return try wip.finalize();
31543548 }
31553549
31563550 fn airArrayToSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
......@@ -3213,7 +3607,6 @@ const DeclGen = struct {
32133607 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]);
32143608
32153609 switch (result_ty.zigTypeTag(mod)) {
3216 .Vector => unreachable, // TODO
32173610 .Struct => {
32183611 if (mod.typeToPackedStruct(result_ty)) |struct_type| {
32193612 _ = struct_type;
......@@ -3261,7 +3654,7 @@ const DeclGen = struct {
32613654 constituents[0..index],
32623655 );
32633656 },
3264 .Array => {
3657 .Vector, .Array => {
32653658 const array_info = result_ty.arrayInfo(mod);
32663659 const n_elems: usize = @intCast(result_ty.arrayLenIncludingSentinel(mod));
32673660 const elem_ids = try self.gpa.alloc(IdRef, n_elems);
......@@ -3433,6 +3826,28 @@ const DeclGen = struct {
34333826 return try self.load(elem_ty, elem_ptr_id, .{ .is_volatile = ptr_ty.isVolatilePtr(mod) });
34343827 }
34353828
3829 fn airVectorStoreElem(self: *DeclGen, inst: Air.Inst.Index) !void {
3830 const mod = self.module;
3831 const data = self.air.instructions.items(.data)[@intFromEnum(inst)].vector_store_elem;
3832 const extra = self.air.extraData(Air.Bin, data.payload).data;
3833
3834 const vector_ptr_ty = self.typeOf(data.vector_ptr);
3835 const vector_ty = vector_ptr_ty.childType(mod);
3836 const scalar_ty = vector_ty.scalarType(mod);
3837
3838 const storage_class = spvStorageClass(vector_ptr_ty.ptrAddressSpace(mod));
3839 const scalar_ptr_ty_ref = try self.ptrType(scalar_ty, storage_class);
3840
3841 const vector_ptr = try self.resolve(data.vector_ptr);
3842 const index = try self.resolve(extra.lhs);
3843 const operand = try self.resolve(extra.rhs);
3844
3845 const elem_ptr_id = try self.accessChainId(scalar_ptr_ty_ref, vector_ptr, &.{index});
3846 try self.store(scalar_ty, elem_ptr_id, operand, .{
3847 .is_volatile = vector_ptr_ty.isVolatilePtr(mod),
3848 });
3849 }
3850
34363851 fn airSetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !void {
34373852 const mod = self.module;
34383853 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
......@@ -4424,20 +4839,24 @@ const DeclGen = struct {
44244839 return try self.constructStruct(err_union_ty, &types, &members);
44254840 }
44264841
4427 fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_null, is_non_null }) !?IdRef {
4842 fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { is_null, is_non_null }) !?IdRef {
44284843 if (self.liveness.isUnused(inst)) return null;
44294844
44304845 const mod = self.module;
44314846 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
44324847 const operand_id = try self.resolve(un_op);
4433 const optional_ty = self.typeOf(un_op);
4434
4848 const operand_ty = self.typeOf(un_op);
4849 const optional_ty = if (is_pointer) operand_ty.childType(mod) else operand_ty;
44354850 const payload_ty = optional_ty.optionalChild(mod);
44364851
44374852 const bool_ty_ref = try self.resolveType(Type.bool, .direct);
44384853
44394854 if (optional_ty.optionalReprIsPayload(mod)) {
44404855 // Pointer payload represents nullability: pointer or slice.
4856 const loaded_id = if (is_pointer)
4857 try self.load(optional_ty, operand_id, .{})
4858 else
4859 operand_id;
44414860
44424861 const ptr_ty = if (payload_ty.isSlice(mod))
44434862 payload_ty.slicePtrFieldType(mod)
......@@ -4445,9 +4864,9 @@ const DeclGen = struct {
44454864 payload_ty;
44464865
44474866 const ptr_id = if (payload_ty.isSlice(mod))
4448 try self.extractField(ptr_ty, operand_id, 0)
4867 try self.extractField(ptr_ty, loaded_id, 0)
44494868 else
4450 operand_id;
4869 loaded_id;
44514870
44524871 const payload_ty_ref = try self.resolveType(ptr_ty, .direct);
44534872 const null_id = try self.spv.constNull(payload_ty_ref);
......@@ -4458,13 +4877,26 @@ const DeclGen = struct {
44584877 return try self.cmp(op, Type.bool, ptr_ty, ptr_id, null_id);
44594878 }
44604879
4461 const is_non_null_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod))
4462 try self.extractField(Type.bool, operand_id, 1)
4463 else
4464 // Optional representation is bool indicating whether the optional is set
4465 // Optionals with no payload are represented as an (indirect) bool, so convert
4466 // it back to the direct bool here.
4467 try self.convertToDirect(Type.bool, operand_id);
4880 const is_non_null_id = blk: {
4881 if (is_pointer) {
4882 if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
4883 const storage_class = spvStorageClass(operand_ty.ptrAddressSpace(mod));
4884 const bool_ptr_ty = try self.ptrType(Type.bool, storage_class);
4885 const tag_ptr_id = try self.accessChain(bool_ptr_ty, operand_id, &.{1});
4886 break :blk try self.load(Type.bool, tag_ptr_id, .{});
4887 }
4888
4889 break :blk try self.load(Type.bool, operand_id, .{});
4890 }
4891
4892 break :blk if (payload_ty.hasRuntimeBitsIgnoreComptime(mod))
4893 try self.extractField(Type.bool, operand_id, 1)
4894 else
4895 // Optional representation is bool indicating whether the optional is set
4896 // Optionals with no payload are represented as an (indirect) bool, so convert
4897 // it back to the direct bool here.
4898 try self.convertToDirect(Type.bool, operand_id);
4899 };
44684900
44694901 return switch (pred) {
44704902 .is_null => blk: {
......@@ -4535,6 +4967,32 @@ const DeclGen = struct {
45354967 return try self.extractField(payload_ty, operand_id, 0);
45364968 }
45374969
4970 fn airUnwrapOptionalPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
4971 if (self.liveness.isUnused(inst)) return null;
4972
4973 const mod = self.module;
4974 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
4975 const operand_id = try self.resolve(ty_op.operand);
4976 const operand_ty = self.typeOf(ty_op.operand);
4977 const optional_ty = operand_ty.childType(mod);
4978 const payload_ty = optional_ty.optionalChild(mod);
4979 const result_ty = self.typeOfIndex(inst);
4980 const result_ty_ref = try self.resolveType(result_ty, .direct);
4981
4982 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
4983 // There is no payload, but we still need to return a valid pointer.
4984 // We can just return anything here, so just return a pointer to the operand.
4985 return try self.bitCast(result_ty, operand_ty, operand_id);
4986 }
4987
4988 if (optional_ty.optionalReprIsPayload(mod)) {
4989 // They are the same value.
4990 return try self.bitCast(result_ty, operand_ty, operand_id);
4991 }
4992
4993 return try self.accessChain(result_ty_ref, operand_id, &.{0});
4994 }
4995
45384996 fn airWrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
45394997 if (self.liveness.isUnused(inst)) return null;
45404998
test/behavior/abs.zig+2-6
......@@ -7,7 +7,6 @@ test "@abs integers" {
77 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
88 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1110
1211 try comptime testAbsIntegers();
1312 try testAbsIntegers();
......@@ -95,7 +94,6 @@ test "@abs floats" {
9594 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9695 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
9796 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
98 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
9997 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
10098
10199 try comptime testAbsFloats(f16);
......@@ -105,9 +103,9 @@ test "@abs floats" {
105103 try comptime testAbsFloats(f64);
106104 try testAbsFloats(f64);
107105 try comptime testAbsFloats(f80);
108 if (builtin.zig_backend != .stage2_wasm) try testAbsFloats(f80);
106 if (builtin.zig_backend != .stage2_wasm and builtin.zig_backend != .stage2_spirv64) try testAbsFloats(f80);
109107 try comptime testAbsFloats(f128);
110 if (builtin.zig_backend != .stage2_wasm) try testAbsFloats(f128);
108 if (builtin.zig_backend != .stage2_wasm and builtin.zig_backend != .stage2_spirv64) try testAbsFloats(f128);
111109}
112110
113111fn testAbsFloats(comptime T: type) !void {
......@@ -155,7 +153,6 @@ test "@abs int vectors" {
155153 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
156154 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
157155 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
158 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
159156
160157 try comptime testAbsIntVectors(1);
161158 try testAbsIntVectors(1);
......@@ -224,7 +221,6 @@ test "@abs unsigned int vectors" {
224221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
225222 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
226223 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
227 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
228224
229225 try comptime testAbsUnsignedIntVectors(1);
230226 try testAbsUnsignedIntVectors(1);
test/behavior/align.zig+3-4
......@@ -18,7 +18,6 @@ test "global variable alignment" {
1818test "large alignment of local constant" {
1919 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2020 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
21 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky
2221
2322 const x: f32 align(128) = 12.34;
2423 try std.testing.expect(@intFromPtr(&x) % 128 == 0);
......@@ -27,7 +26,7 @@ test "large alignment of local constant" {
2726test "slicing array of length 1 can not assume runtime index is always zero" {
2827 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2928 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
30 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
29 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky
3130
3231 var runtime_index: usize = 1;
3332 _ = &runtime_index;
......@@ -512,7 +511,7 @@ test "struct field explicit alignment" {
512511 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
513512 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
514513 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
515 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
514 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky
516515
517516 const S = struct {
518517 const Node = struct {
......@@ -581,7 +580,7 @@ test "comptime alloc alignment" {
581580 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
582581 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
583582 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
584 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
583 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky
585584 if (builtin.zig_backend == .stage2_llvm and builtin.target.cpu.arch == .x86) {
586585 // https://github.com/ziglang/zig/issues/18034
587586 return error.SkipZigTest;
test/behavior/array.zig-2
......@@ -768,8 +768,6 @@ test "array init with no result pointer sets field result types" {
768768}
769769
770770test "runtime side-effects in comptime-known array init" {
771 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
772
773771 var side_effects: u4 = 0;
774772 const init = [4]u4{
775773 blk: {
test/behavior/basic.zig+1
......@@ -1222,6 +1222,7 @@ test "integer compare" {
12221222
12231223test "reference to inferred local variable works as expected" {
12241224 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1225 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
12251226
12261227 const Crasher = struct {
12271228 lets_crash: u64 = 0,
test/behavior/bool.zig-2
......@@ -9,8 +9,6 @@ test "bool literals" {
99}
1010
1111test "cast bool to int" {
12 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13
1412 const t = true;
1513 const f = false;
1614 try expectEqual(@as(u32, 1), @intFromBool(t));
test/behavior/builtin_functions_returning_void_or_noreturn.zig+1
......@@ -11,6 +11,7 @@ test {
1111 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1212 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1313 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
14 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1415
1516 var val: u8 = undefined;
1617 try testing.expectEqual({}, @atomicStore(u8, &val, 0, .Unordered));
test/behavior/cast.zig+1-11
......@@ -605,7 +605,6 @@ test "@intCast on vector" {
605605 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
606606 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
607607 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
608 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
609608
610609 const S = struct {
611610 fn doTheTest() !void {
......@@ -760,6 +759,7 @@ test "peer type resolution: error union and error set" {
760759 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
761760 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
762761 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
762 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
763763
764764 const a: error{Three} = undefined;
765765 const b: error{ One, Two }!u32 = undefined;
......@@ -1247,7 +1247,6 @@ test "implicit cast from *[N]T to ?[*]T" {
12471247 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
12481248 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12491249 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1250 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
12511250
12521251 var x: ?[*]u16 = null;
12531252 var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
......@@ -1732,7 +1731,6 @@ test "peer type resolution: array with smaller child type and vector with larger
17321731 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
17331732 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
17341733 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1735 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
17361734
17371735 var arr: [2]u8 = .{ 0, 1 };
17381736 var vec: @Vector(2, u64) = .{ 2, 3 };
......@@ -2320,7 +2318,6 @@ test "@floatCast on vector" {
23202318 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
23212319 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
23222320 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2323 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
23242321 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
23252322
23262323 const S = struct {
......@@ -2341,7 +2338,6 @@ test "@ptrFromInt on vector" {
23412338 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
23422339 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
23432340 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2344 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
23452341
23462342 const S = struct {
23472343 fn doTheTest() !void {
......@@ -2365,7 +2361,6 @@ test "@intFromPtr on vector" {
23652361 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
23662362 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
23672363 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2368 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
23692364
23702365 const S = struct {
23712366 fn doTheTest() !void {
......@@ -2389,7 +2384,6 @@ test "@floatFromInt on vector" {
23892384 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
23902385 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
23912386 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2392 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
23932387 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
23942388
23952389 const S = struct {
......@@ -2410,7 +2404,6 @@ test "@intFromFloat on vector" {
24102404 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
24112405 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
24122406 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2413 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
24142407
24152408 const S = struct {
24162409 fn doTheTest() !void {
......@@ -2430,7 +2423,6 @@ test "@intFromBool on vector" {
24302423 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
24312424 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
24322425 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2433 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
24342426
24352427 const S = struct {
24362428 fn doTheTest() !void {
......@@ -2468,7 +2460,6 @@ test "@as does not corrupt values with incompatible representations" {
24682460 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
24692461 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
24702462 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2471 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
24722463 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
24732464
24742465 const x: f32 = @as(f16, blk: {
......@@ -2510,7 +2501,6 @@ test "@intCast vector of signed integer" {
25102501 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
25112502 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
25122503 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2513 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
25142504 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
25152505 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
25162506
test/behavior/destructure.zig+1
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const assert = std.debug.assert;
34const expect = std.testing.expect;
45
test/behavior/duplicated_test_names.zig+2
......@@ -15,5 +15,7 @@ comptime {
1515test "thingy" {}
1616
1717test thingy {
18 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
19
1820 if (thingy(1, 2) != 3) unreachable;
1921}
test/behavior/eval.zig-1
......@@ -489,7 +489,6 @@ test "comptime bitwise operators" {
489489
490490test "comptime shlWithOverflow" {
491491 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
492 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
493492
494493 const ct_shifted = @shlWithOverflow(~@as(u64, 0), 16)[0];
495494 var a = ~@as(u64, 0);
test/behavior/export_builtin.zig-3
......@@ -6,7 +6,6 @@ test "exporting enum type and value" {
66 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
77 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
88 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
109
1110 const S = struct {
1211 const E = enum(c_int) { one, two };
......@@ -22,7 +21,6 @@ test "exporting with internal linkage" {
2221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2322 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2423 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
25 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2624
2725 const S = struct {
2826 fn foo() callconv(.C) void {}
......@@ -37,7 +35,6 @@ test "exporting using field access" {
3735 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3836 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3937 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
4138
4239 const S = struct {
4340 const Inner = struct {
test/behavior/export_keyword.zig+1
......@@ -23,6 +23,7 @@ const PackedUnion = packed union {
2323
2424test "packed struct, enum, union parameters in extern function" {
2525 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
26 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2627
2728 testPackedStuff(&(PackedStruct{
2829 .a = 1,
test/behavior/extern.zig+2
......@@ -5,6 +5,7 @@ const expect = std.testing.expect;
55test "anyopaque extern symbol" {
66 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
77 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
89
910 const a = @extern(*anyopaque, .{ .name = "a_mystery_symbol" });
1011 const b: *i32 = @alignCast(@ptrCast(a));
......@@ -17,6 +18,7 @@ test "function extern symbol" {
1718 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1819 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1920 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
21 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2022
2123 const a = @extern(*const fn () callconv(.C) i32, .{ .name = "a_mystery_function" });
2224 try expect(a() == 4567);
test/behavior/floatop.zig-3
......@@ -969,7 +969,6 @@ test "@abs f16" {
969969 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
970970 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
971971 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
972 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
973972
974973 try testFabs(f16);
975974 try comptime testFabs(f16);
......@@ -979,7 +978,6 @@ test "@abs f32/f64" {
979978 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
980979 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
981980 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
982 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
983981
984982 try testFabs(f32);
985983 try comptime testFabs(f32);
......@@ -1070,7 +1068,6 @@ fn testFabs(comptime T: type) !void {
10701068test "@abs with vectors" {
10711069 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10721070 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1073 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10741071 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
10751072
10761073 try testFabsWithVectors();
test/behavior/for.zig+1
......@@ -456,6 +456,7 @@ test "inline for on tuple pointer" {
456456 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
457457 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
458458 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
459 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
459460
460461 const S = struct { u32, u32, u32 };
461462 var s: S = .{ 100, 200, 300 };
test/behavior/globals.zig-2
......@@ -8,7 +8,6 @@ test "store to global array" {
88 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
99 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1010 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
11 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1211
1312 try expect(pos[1] == 0.0);
1413 pos = [2]f32{ 0.0, 1.0 };
......@@ -21,7 +20,6 @@ test "store to global vector" {
2120 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2221 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2322 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
24 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2523
2624 try expect(vpos[1] == 0.0);
2725 vpos = @Vector(2, f32){ 0.0, 1.0 };
test/behavior/hasdecl.zig+5
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const expect = std.testing.expect;
34
45const Foo = @import("hasdecl/foo.zig");
......@@ -11,6 +12,8 @@ const Bar = struct {
1112};
1213
1314test "@hasDecl" {
15 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
16
1417 try expect(@hasDecl(Foo, "public_thing"));
1518 try expect(!@hasDecl(Foo, "private_thing"));
1619 try expect(!@hasDecl(Foo, "no_thing"));
......@@ -21,6 +24,8 @@ test "@hasDecl" {
2124}
2225
2326test "@hasDecl using a sliced string literal" {
27 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
28
2429 try expect(@hasDecl(@This(), "std") == true);
2530 try expect(@hasDecl(@This(), "std"[0..0]) == false);
2631 try expect(@hasDecl(@This(), "std"[0..1]) == false);
test/behavior/import.zig+9
......@@ -1,17 +1,24 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const expect = std.testing.expect;
34const expectEqual = std.testing.expectEqual;
45const a_namespace = @import("import/a_namespace.zig");
56
67test "call fn via namespace lookup" {
8 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
9
710 try expect(@as(i32, 1234) == a_namespace.foo());
811}
912
1013test "importing the same thing gives the same import" {
14 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
15
1116 try expect(@import("std") == @import("std"));
1217}
1318
1419test "import in non-toplevel scope" {
20 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
21
1522 const S = struct {
1623 usingnamespace @import("import/a_namespace.zig");
1724 };
......@@ -19,5 +26,7 @@ test "import in non-toplevel scope" {
1926}
2027
2128test "import empty file" {
29 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
30
2231 _ = @import("import/empty.zig");
2332}
test/behavior/int_div.zig+2
......@@ -6,6 +6,7 @@ test "integer division" {
66 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
77 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
88 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
910
1011 try testDivision();
1112 try comptime testDivision();
......@@ -96,6 +97,7 @@ test "large integer division" {
9697 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9798 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
9899 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
100 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
99101
100102 {
101103 var numerator: u256 = 99999999999999999997315645440;
test/behavior/math.zig-10
......@@ -602,7 +602,6 @@ fn testUnsignedNegationWrappingEval(x: u16) !void {
602602test "negation wrapping" {
603603 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
604604 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
605 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
606605
607606 try expectEqual(@as(u1, 1), negateWrap(u1, 1));
608607}
......@@ -649,8 +648,6 @@ test "bit shift a u1" {
649648}
650649
651650test "truncating shift right" {
652 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
653
654651 try testShrTrunc(maxInt(u16));
655652 try comptime testShrTrunc(maxInt(u16));
656653}
......@@ -772,7 +769,6 @@ test "@addWithOverflow" {
772769test "small int addition" {
773770 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
774771 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
775 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
776772
777773 var x: u2 = 0;
778774 try expect(x == 0);
......@@ -1330,8 +1326,6 @@ fn testShlTrunc(x: u16) !void {
13301326}
13311327
13321328test "exact shift left" {
1333 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1334
13351329 try testShlExact(0b00110101);
13361330 try comptime testShlExact(0b00110101);
13371331
......@@ -1343,8 +1337,6 @@ fn testShlExact(x: u8) !void {
13431337}
13441338
13451339test "exact shift right" {
1346 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1347
13481340 try testShrExact(0b10110100);
13491341 try comptime testShrExact(0b10110100);
13501342}
......@@ -1570,7 +1562,6 @@ test "vector integer addition" {
15701562 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
15711563 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
15721564 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1573 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
15741565
15751566 const S = struct {
15761567 fn doTheTest() !void {
......@@ -1693,7 +1684,6 @@ test "absFloat" {
16931684 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
16941685 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
16951686 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1696 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
16971687
16981688 try testAbsFloat();
16991689 try comptime testAbsFloat();
test/behavior/maximum_minimum.zig-5
......@@ -31,7 +31,6 @@ test "@max on vectors" {
3131 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
3232 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
3333 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
34 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
3534 if (builtin.zig_backend == .stage2_x86_64 and
3635 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;
3736
......@@ -86,7 +85,6 @@ test "@min for vectors" {
8685 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
8786 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
8887 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
89 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
9088 if (builtin.zig_backend == .stage2_x86_64 and
9189 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;
9290
......@@ -199,7 +197,6 @@ test "@min/@max notices vector bounds" {
199197 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
200198 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
201199 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
202 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
203200 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
204201
205202 var x: @Vector(2, u16) = .{ 140, 40 };
......@@ -253,7 +250,6 @@ test "@min/@max notices bounds from vector types" {
253250 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
254251 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
255252 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
256 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
257253 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
258254
259255 var x: @Vector(2, u16) = .{ 30, 67 };
......@@ -295,7 +291,6 @@ test "@min/@max notices bounds from vector types when element of comptime-known
295291 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
296292 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
297293 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
298 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
299294 if (builtin.zig_backend == .stage2_x86_64 and
300295 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .avx)) return error.SkipZigTest;
301296
test/behavior/muladd.zig-5
......@@ -10,7 +10,6 @@ test "@mulAdd" {
1010 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1111 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1212 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
13 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1413
1514 try comptime testMulAdd();
1615 try testMulAdd();
......@@ -37,7 +36,6 @@ test "@mulAdd f16" {
3736 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
3837 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
3938 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
40 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
4139 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
4240
4341 try comptime testMulAdd16();
......@@ -111,7 +109,6 @@ test "vector f16" {
111109 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
112110 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
113111 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
114 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
115112
116113 try comptime vector16();
117114 try vector16();
......@@ -136,7 +133,6 @@ test "vector f32" {
136133 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
137134 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
138135 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
139 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
140136
141137 try comptime vector32();
142138 try vector32();
......@@ -161,7 +157,6 @@ test "vector f64" {
161157 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
162158 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
163159 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
164 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
165160
166161 try comptime vector64();
167162 try vector64();
test/behavior/namespace_depends_on_compile_var.zig+2
......@@ -3,6 +3,8 @@ const builtin = @import("builtin");
33const expect = std.testing.expect;
44
55test "namespace depends on compile var" {
6 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
7
68 if (some_namespace.a_bool) {
79 try expect(some_namespace.a_bool);
810 } else {
test/behavior/null.zig-2
......@@ -32,7 +32,6 @@ test "test maybe object and get a pointer to the inner value" {
3232 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3333 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3434 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
35 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
3635
3736 var maybe_bool: ?bool = true;
3837
......@@ -142,7 +141,6 @@ test "if var maybe pointer" {
142141 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
143142 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
144143 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
145 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
146144
147145 try expect(shouldBeAPlus1(Particle{
148146 .a = 14,
test/behavior/optional.zig+1-3
......@@ -72,7 +72,6 @@ test "address of unwrap optional" {
7272 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7373 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7474 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
75 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
7675
7776 const S = struct {
7877 const Foo = struct {
......@@ -341,7 +340,6 @@ test "optional pointer to zero bit optional payload" {
341340 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
342341 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
343342 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
344 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
345343
346344 const B = struct {
347345 fn foo(_: *@This()) void {}
......@@ -453,6 +451,7 @@ test "Optional slice passed to function" {
453451 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
454452 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
455453 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
454 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
456455
457456 const S = struct {
458457 fn foo(a: ?[]const u8) !void {
......@@ -518,7 +517,6 @@ test "copied optional doesn't alias source" {
518517 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
519518 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
520519 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
521 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
522520
523521 var opt_x: ?[3]f32 = [_]f32{0.0} ** 3;
524522
test/behavior/pub_enum.zig+4
......@@ -3,6 +3,8 @@ const other = @import("pub_enum/other.zig");
33const expect = @import("std").testing.expect;
44
55test "pub enum" {
6 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
7
68 try pubEnumTest(other.APubEnum.Two);
79}
810fn pubEnumTest(foo: other.APubEnum) !void {
......@@ -10,5 +12,7 @@ fn pubEnumTest(foo: other.APubEnum) !void {
1012}
1113
1214test "cast with imported symbol" {
15 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
16
1317 try expect(@as(other.size_t, 42) == 42);
1418}
test/behavior/shuffle.zig-3
......@@ -8,7 +8,6 @@ test "@shuffle int" {
88 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1010 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1211
1312 const S = struct {
1413 fn doTheTest() !void {
......@@ -54,7 +53,6 @@ test "@shuffle bool 1" {
5453 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5554 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
5655 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
57 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
5856
5957 const S = struct {
6058 fn doTheTest() !void {
......@@ -77,7 +75,6 @@ test "@shuffle bool 2" {
7775 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7876 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7977 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
80 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
8178
8279 if (builtin.zig_backend == .stage2_llvm) {
8380 // https://github.com/ziglang/zig/issues/3246
test/behavior/slice_sentinel_comptime.zig+2
......@@ -1,3 +1,5 @@
1const builtin = @import("builtin");
2
13test "comptime slice-sentinel in bounds (unterminated)" {
24 // array
35 comptime {
test/behavior/struct.zig+2-2
......@@ -1744,8 +1744,6 @@ test "struct init with no result pointer sets field result types" {
17441744}
17451745
17461746test "runtime side-effects in comptime-known struct init" {
1747 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1748
17491747 var side_effects: u4 = 0;
17501748 const S = struct { a: u4, b: u4, c: u4, d: u4 };
17511749 const init = S{
......@@ -2056,6 +2054,8 @@ test "struct field default value is a call" {
20562054}
20572055
20582056test "aggregate initializers should allow initializing comptime fields, verifying equality" {
2057 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2058
20592059 var x: u32 = 15;
20602060 _ = &x;
20612061 const T = @TypeOf(.{ @as(i32, -1234), @as(u32, 5678), x });
test/behavior/switch_on_captured_error.zig+4
......@@ -5,6 +5,8 @@ const expectError = std.testing.expectError;
55const expectEqual = std.testing.expectEqual;
66
77test "switch on error union catch capture" {
8 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
9
810 const S = struct {
911 const Error = error{ A, B, C };
1012 fn doTheTest() !void {
......@@ -257,6 +259,8 @@ test "switch on error union catch capture" {
257259}
258260
259261test "switch on error union if else capture" {
262 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
263
260264 const S = struct {
261265 const Error = error{ A, B, C };
262266 fn doTheTest() !void {
test/behavior/truncate.zig-1
......@@ -69,7 +69,6 @@ test "truncate on vectors" {
6969 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7070 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7171 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
72 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
7372
7473 const S = struct {
7574 fn doTheTest() !void {
test/behavior/tuple.zig-1
......@@ -483,7 +483,6 @@ test "empty tuple type" {
483483
484484test "tuple with comptime fields with non empty initializer" {
485485 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
486 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
487486
488487 const a: struct { comptime comptime_int = 0 } = .{0};
489488 _ = a;
test/behavior/union.zig+2-1
......@@ -1119,6 +1119,7 @@ test "@unionInit on union with tag but no fields" {
11191119 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11201120 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11211121 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1122 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11221123
11231124 const S = struct {
11241125 const Type = enum(u8) { no_op = 105 };
......@@ -2059,7 +2060,6 @@ test "store of comptime reinterpreted memory to packed union" {
20592060
20602061test "union field is a pointer to an aligned version of itself" {
20612062 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2062 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
20632063
20642064 const E = union {
20652065 next: *align(1) @This(),
......@@ -2181,6 +2181,7 @@ test "create union(enum) from other union(enum)" {
21812181 if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO
21822182 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
21832183 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2184 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
21842185
21852186 const string = "hello world";
21862187 const TempRef = struct {
test/behavior/vector.zig-15
......@@ -179,7 +179,6 @@ test "array vector coercion - odd sizes" {
179179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
180180 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
181181 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
182 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
183182 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
184183 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
185184
......@@ -219,7 +218,6 @@ test "array to vector with element type coercion" {
219218 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
220219 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
221220 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
222 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
223221 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
224222 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf) return error.SkipZigTest;
225223
......@@ -261,7 +259,6 @@ test "tuple to vector" {
261259 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
262260 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
263261 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
264 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
265262
266263 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
267264 // Regressed with LLVM 14:
......@@ -329,7 +326,6 @@ test "vector @splat" {
329326 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
330327 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
331328 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
332 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
333329
334330 if (builtin.zig_backend == .stage2_llvm and
335331 builtin.os.tag == .macos)
......@@ -628,7 +624,6 @@ test "vector bitwise not operator" {
628624 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
629625 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
630626 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
631 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
632627
633628 const S = struct {
634629 fn doTheTestNot(comptime T: type, x: @Vector(4, T)) !void {
......@@ -660,7 +655,6 @@ test "vector shift operators" {
660655 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
661656 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
662657 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
663 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
664658
665659 const S = struct {
666660 fn doTheTestShift(x: anytype, y: anytype) !void {
......@@ -915,7 +909,6 @@ test "mask parameter of @shuffle is comptime scope" {
915909 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
916910 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
917911 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
918 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
919912
920913 const __v4hi = @Vector(4, i16);
921914 var v4_a = __v4hi{ 0, 0, 0, 0 };
......@@ -1067,7 +1060,6 @@ test "@addWithOverflow" {
10671060 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10681061 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10691062 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1070 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10711063
10721064 const S = struct {
10731065 fn doTheTest() !void {
......@@ -1115,7 +1107,6 @@ test "@subWithOverflow" {
11151107 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11161108 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11171109 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1118 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11191110
11201111 const S = struct {
11211112 fn doTheTest() !void {
......@@ -1169,7 +1160,6 @@ test "@shlWithOverflow" {
11691160 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11701161 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11711162 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1172 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11731163
11741164 const S = struct {
11751165 fn doTheTest() !void {
......@@ -1236,7 +1226,6 @@ test "byte vector initialized in inline function" {
12361226 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12371227 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12381228 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1239 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
12401229
12411230 if (comptime builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .x86_64 and
12421231 builtin.cpu.features.isEnabled(@intFromEnum(std.Target.x86.Feature.avx512f)))
......@@ -1306,7 +1295,6 @@ test "@intCast to u0" {
13061295 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13071296 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
13081297 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1309 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13101298
13111299 var zeros = @Vector(2, u32){ 0, 0 };
13121300 _ = &zeros;
......@@ -1331,7 +1319,6 @@ test "array operands to shuffle are coerced to vectors" {
13311319 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13321320 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
13331321 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1334 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13351322
13361323 const mask = [5]i32{ -1, 0, 1, 2, 3 };
13371324
......@@ -1357,7 +1344,6 @@ test "store packed vector element" {
13571344 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13581345 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
13591346 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1360 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13611347 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
13621348 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
13631349 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
......@@ -1454,7 +1440,6 @@ test "compare vectors with different element types" {
14541440 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
14551441 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14561442 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1457 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
14581443
14591444 var a: @Vector(2, u8) = .{ 1, 2 };
14601445 var b: @Vector(2, u9) = .{ 3, 0 };
test/behavior/wrapping_arithmetic.zig+6
......@@ -5,6 +5,8 @@ const maxInt = std.math.maxInt;
55const expect = std.testing.expect;
66
77test "wrapping add" {
8 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
9
810 const S = struct {
911 fn doTheTest() !void {
1012 try testWrapAdd(i8, -3, 10, 7);
......@@ -40,6 +42,8 @@ test "wrapping add" {
4042}
4143
4244test "wrapping subtraction" {
45 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
46
4347 const S = struct {
4448 fn doTheTest() !void {
4549 try testWrapSub(i8, -3, 10, -13);
......@@ -73,6 +77,8 @@ test "wrapping subtraction" {
7377}
7478
7579test "wrapping multiplication" {
80 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
81
7682 // TODO: once #9660 has been solved, remove this line
7783 if (builtin.cpu.arch == .wasm32) return error.SkipZigTest;
7884