authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-06-11 08:21:04+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-06-11 08:21:04+02:00
logd9bd34fd0533295044ffb4160da41f7873aff905
treeff2c582f019497134ad8d81f7bbb422c87d8b3d3
parentd4bc64038ce40ac3829c3f1d0dc21dfb7484818c
parenta567f3871ec06f3e6a8c0e6424aba556f1069ccc
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20247 from Snektron/spirv-vectors-v3

spirv: vectors v3

22 files changed, 1958 insertions(+), 1077 deletions(-)

lib/std/Build.zig+17-3
...@@ -972,10 +972,24 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run {...@@ -972,10 +972,24 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run {
972 // Consider that this is declarative; the run step may not be run unless a user972 // Consider that this is declarative; the run step may not be run unless a user
973 // option is supplied.973 // option is supplied.
974 const run_step = Step.Run.create(b, b.fmt("run {s}", .{exe.name}));974 const run_step = Step.Run.create(b, b.fmt("run {s}", .{exe.name}));
975 run_step.addArtifactArg(exe);975 if (exe.kind == .@"test") {
976 if (exe.exec_cmd_args) |exec_cmd_args| {
977 for (exec_cmd_args) |cmd_arg| {
978 if (cmd_arg) |arg| {
979 run_step.addArg(arg);
980 } else {
981 run_step.addArtifactArg(exe);
982 }
983 }
984 } else {
985 run_step.addArtifactArg(exe);
986 }
976987
977 if (exe.kind == .@"test" and exe.test_server_mode) {988 if (exe.test_server_mode) {
978 run_step.enableTestRunnerMode();989 run_step.enableTestRunnerMode();
990 }
991 } else {
992 run_step.addArtifactArg(exe);
979 }993 }
980994
981 return run_step;995 return run_step;
src/codegen/spirv.zig+1761-1026
...@@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;...@@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;
3const Target = std.Target;3const Target = std.Target;
4const log = std.log.scoped(.codegen);4const log = std.log.scoped(.codegen);
5const assert = std.debug.assert;5const assert = std.debug.assert;
6const Signedness = std.builtin.Signedness;
67
7const Module = @import("../Module.zig");8const Module = @import("../Module.zig");
8const Decl = Module.Decl;9const Decl = Module.Decl;
...@@ -22,6 +23,7 @@ const IdResultType = spec.IdResultType;...@@ -22,6 +23,7 @@ const IdResultType = spec.IdResultType;
22const StorageClass = spec.StorageClass;23const StorageClass = spec.StorageClass;
2324
24const SpvModule = @import("spirv/Module.zig");25const SpvModule = @import("spirv/Module.zig");
26const IdRange = SpvModule.IdRange;
2527
26const SpvSection = @import("spirv/Section.zig");28const SpvSection = @import("spirv/Section.zig");
27const SpvAssembler = @import("spirv/Assembler.zig");29const SpvAssembler = @import("spirv/Assembler.zig");
...@@ -32,7 +34,7 @@ pub const zig_call_abi_ver = 3;...@@ -32,7 +34,7 @@ pub const zig_call_abi_ver = 3;
3234
33const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, DeclGen.Repr }, IdResult);35const InternMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, DeclGen.Repr }, IdResult);
34const PtrTypeMap = std.AutoHashMapUnmanaged(36const PtrTypeMap = std.AutoHashMapUnmanaged(
35 struct { InternPool.Index, StorageClass },37 struct { InternPool.Index, StorageClass, DeclGen.Repr },
36 struct { ty_id: IdRef, fwd_emitted: bool },38 struct { ty_id: IdRef, fwd_emitted: bool },
37);39);
3840
...@@ -422,6 +424,17 @@ const DeclGen = struct {...@@ -422,6 +424,17 @@ const DeclGen = struct {
422 return self.fail("TODO (SPIR-V): " ++ format, args);424 return self.fail("TODO (SPIR-V): " ++ format, args);
423 }425 }
424426
427 /// This imports the "default" extended instruction set for the target
428 /// For OpenCL, OpenCL.std.100. For Vulkan, GLSL.std.450.
429 fn importExtendedSet(self: *DeclGen) !IdResult {
430 const target = self.getTarget();
431 return switch (target.os.tag) {
432 .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"),
433 .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"),
434 else => unreachable,
435 };
436 }
437
425 /// Fetch the result-id for a previously generated instruction or constant.438 /// Fetch the result-id for a previously generated instruction or constant.
426 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef {439 fn resolve(self: *DeclGen, inst: Air.Inst.Ref) !IdRef {
427 const mod = self.module;440 const mod = self.module;
...@@ -626,10 +639,23 @@ const DeclGen = struct {...@@ -626,10 +639,23 @@ const DeclGen = struct {
626 }639 }
627640
628 /// Checks whether the type can be directly translated to SPIR-V vectors641 /// Checks whether the type can be directly translated to SPIR-V vectors
629 fn isVector(self: *DeclGen, ty: Type) bool {642 fn isSpvVector(self: *DeclGen, ty: Type) bool {
630 const mod = self.module;643 const mod = self.module;
631 const target = self.getTarget();644 const target = self.getTarget();
632 if (ty.zigTypeTag(mod) != .Vector) return false;645 if (ty.zigTypeTag(mod) != .Vector) return false;
646
647 // TODO: This check must be expanded for types that can be represented
648 // as integers (enums / packed structs?) and types that are represented
649 // by multiple SPIR-V values.
650 const scalar_ty = ty.scalarType(mod);
651 switch (scalar_ty.zigTypeTag(mod)) {
652 .Bool,
653 .Int,
654 .Float,
655 => {},
656 else => return false,
657 }
658
633 const elem_ty = ty.childType(mod);659 const elem_ty = ty.childType(mod);
634660
635 const len = ty.vectorLen(mod);661 const len = ty.vectorLen(mod);
...@@ -722,9 +748,13 @@ const DeclGen = struct {...@@ -722,9 +748,13 @@ const DeclGen = struct {
722 // Use backing bits so that negatives are sign extended748 // Use backing bits so that negatives are sign extended
723 const backing_bits = self.backingIntBits(int_info.bits).?; // Assertion failure means big int749 const backing_bits = self.backingIntBits(int_info.bits).?; // Assertion failure means big int
724750
725 const bits: u64 = switch (int_info.signedness) {751 const signedness: Signedness = switch (@typeInfo(@TypeOf(value))) {
726 // Intcast needed to silence compile errors for when the wrong path is compiled.752 .Int => |int| int.signedness,
727 // Lazy fix.753 .ComptimeInt => if (value < 0) .signed else .unsigned,
754 else => unreachable,
755 };
756
757 const bits: u64 = switch (signedness) {
728 .signed => @bitCast(@as(i64, @intCast(value))),758 .signed => @bitCast(@as(i64, @intCast(value))),
729 .unsigned => @as(u64, @intCast(value)),759 .unsigned => @as(u64, @intCast(value)),
730 };760 };
...@@ -779,45 +809,51 @@ const DeclGen = struct {...@@ -779,45 +809,51 @@ const DeclGen = struct {
779 /// Result is in `direct` representation.809 /// Result is in `direct` representation.
780 fn constructStruct(self: *DeclGen, ty: Type, types: []const Type, constituents: []const IdRef) !IdRef {810 fn constructStruct(self: *DeclGen, ty: Type, types: []const Type, constituents: []const IdRef) !IdRef {
781 assert(types.len == constituents.len);811 assert(types.len == constituents.len);
782 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which'812
783 // operands are not constant.813 const result_id = self.spv.allocId();
784 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349814 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
785 // For now, just initialize the struct by setting the fields manually...815 .id_result_type = try self.resolveType(ty, .direct),
786 // TODO: Make this OpCompositeConstruct when we can816 .id_result = result_id,
787 const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function });817 .constituents = constituents,
788 for (constituents, types, 0..) |constitent_id, member_ty, index| {818 });
789 const ptr_member_ty_id = try self.ptrType(member_ty, .Function);819 return result_id;
790 const ptr_id = try self.accessChain(ptr_member_ty_id, ptr_composite_id, &.{@as(u32, @intCast(index))});
791 try self.func.body.emit(self.spv.gpa, .OpStore, .{
792 .pointer = ptr_id,
793 .object = constitent_id,
794 });
795 }
796 return try self.load(ty, ptr_composite_id, .{});
797 }820 }
798821
799 /// Construct a vector at runtime.822 /// Construct a vector at runtime.
800 /// ty must be an vector type.823 /// ty must be an vector type.
801 /// Constituents should be in `indirect` representation (as the elements of an vector should be).
802 /// Result is in `direct` representation.
803 fn constructVector(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef {824 fn constructVector(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef {
804 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which'825 const mod = self.module;
805 // operands are not constant.826 assert(ty.vectorLen(mod) == constituents.len);
827
828 // Note: older versions of the Khronos SPRIV-LLVM translator crash on this instruction
829 // because it cannot construct structs which' operands are not constant.
806 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349830 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349
807 // For now, just initialize the struct by setting the fields manually...831 // Currently this is the case for Intel OpenCL CPU runtime (2023-WW46), but the
808 // TODO: Make this OpCompositeConstruct when we can832 // alternatives dont work properly:
833 // - using temporaries/pointers doesn't work properly with vectors of bool, causes
834 // backends that use llvm to crash
835 // - using OpVectorInsertDynamic doesn't work for non-spirv-vectors of bool.
836
837 const result_id = self.spv.allocId();
838 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
839 .id_result_type = try self.resolveType(ty, .direct),
840 .id_result = result_id,
841 .constituents = constituents,
842 });
843 return result_id;
844 }
845
846 /// Construct a vector at runtime with all lanes set to the same value.
847 /// ty must be an vector type.
848 fn constructVectorSplat(self: *DeclGen, ty: Type, constituent: IdRef) !IdRef {
809 const mod = self.module;849 const mod = self.module;
810 const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function });850 const n = ty.vectorLen(mod);
811 const ptr_elem_ty_id = try self.ptrType(ty.elemType2(mod), .Function);
812 for (constituents, 0..) |constitent_id, index| {
813 const ptr_id = try self.accessChain(ptr_elem_ty_id, ptr_composite_id, &.{@as(u32, @intCast(index))});
814 try self.func.body.emit(self.spv.gpa, .OpStore, .{
815 .pointer = ptr_id,
816 .object = constitent_id,
817 });
818 }
819851
820 return try self.load(ty, ptr_composite_id, .{});852 const constituents = try self.gpa.alloc(IdRef, n);
853 defer self.gpa.free(constituents);
854 @memset(constituents, constituent);
855
856 return try self.constructVector(ty, constituents);
821 }857 }
822858
823 /// Construct an array at runtime.859 /// Construct an array at runtime.
...@@ -825,23 +861,13 @@ const DeclGen = struct {...@@ -825,23 +861,13 @@ const DeclGen = struct {
825 /// Constituents should be in `indirect` representation (as the elements of an array should be).861 /// Constituents should be in `indirect` representation (as the elements of an array should be).
826 /// Result is in `direct` representation.862 /// Result is in `direct` representation.
827 fn constructArray(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef {863 fn constructArray(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef {
828 // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which'864 const result_id = self.spv.allocId();
829 // operands are not constant.865 try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
830 // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349866 .id_result_type = try self.resolveType(ty, .direct),
831 // For now, just initialize the struct by setting the fields manually...867 .id_result = result_id,
832 // TODO: Make this OpCompositeConstruct when we can868 .constituents = constituents,
833 const mod = self.module;869 });
834 const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function });870 return result_id;
835 const ptr_elem_ty_id = try self.ptrType(ty.elemType2(mod), .Function);
836 for (constituents, 0..) |constitent_id, index| {
837 const ptr_id = try self.accessChain(ptr_elem_ty_id, ptr_composite_id, &.{@as(u32, @intCast(index))});
838 try self.func.body.emit(self.spv.gpa, .OpStore, .{
839 .pointer = ptr_id,
840 .object = constitent_id,
841 });
842 }
843
844 return try self.load(ty, ptr_composite_id, .{});
845 }871 }
846872
847 /// This function generates a load for a constant in direct (ie, non-memory) representation.873 /// This function generates a load for a constant in direct (ie, non-memory) representation.
...@@ -1031,21 +1057,27 @@ const DeclGen = struct {...@@ -1031,21 +1057,27 @@ const DeclGen = struct {
1031 const constituents = try self.gpa.alloc(IdRef, @intCast(ty.arrayLenIncludingSentinel(mod)));1057 const constituents = try self.gpa.alloc(IdRef, @intCast(ty.arrayLenIncludingSentinel(mod)));
1032 defer self.gpa.free(constituents);1058 defer self.gpa.free(constituents);
10331059
1060 const child_repr: Repr = switch (tag) {
1061 .array_type => .indirect,
1062 .vector_type => .direct,
1063 else => unreachable,
1064 };
1065
1034 switch (aggregate.storage) {1066 switch (aggregate.storage) {
1035 .bytes => |bytes| {1067 .bytes => |bytes| {
1036 // TODO: This is really space inefficient, perhaps there is a better1068 // TODO: This is really space inefficient, perhaps there is a better
1037 // way to do it?1069 // way to do it?
1038 for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| {1070 for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| {
1039 constituent.* = try self.constInt(elem_ty, byte, .indirect);1071 constituent.* = try self.constInt(elem_ty, byte, child_repr);
1040 }1072 }
1041 },1073 },
1042 .elems => |elems| {1074 .elems => |elems| {
1043 for (constituents, elems) |*constituent, elem| {1075 for (constituents, elems) |*constituent, elem| {
1044 constituent.* = try self.constant(elem_ty, Value.fromInterned(elem), .indirect);1076 constituent.* = try self.constant(elem_ty, Value.fromInterned(elem), child_repr);
1045 }1077 }
1046 },1078 },
1047 .repeated_elem => |elem| {1079 .repeated_elem => |elem| {
1048 @memset(constituents, try self.constant(elem_ty, Value.fromInterned(elem), .indirect));1080 @memset(constituents, try self.constant(elem_ty, Value.fromInterned(elem), child_repr));
1049 },1081 },
1050 }1082 }
10511083
...@@ -1334,7 +1366,11 @@ const DeclGen = struct {...@@ -1334,7 +1366,11 @@ const DeclGen = struct {
1334 }1366 }
13351367
1336 fn ptrType(self: *DeclGen, child_ty: Type, storage_class: StorageClass) !IdRef {1368 fn ptrType(self: *DeclGen, child_ty: Type, storage_class: StorageClass) !IdRef {
1337 const key = .{ child_ty.toIntern(), storage_class };1369 return try self.ptrType2(child_ty, storage_class, .indirect);
1370 }
1371
1372 fn ptrType2(self: *DeclGen, child_ty: Type, storage_class: StorageClass, child_repr: Repr) !IdRef {
1373 const key = .{ child_ty.toIntern(), storage_class, child_repr };
1338 const entry = try self.ptr_types.getOrPut(self.gpa, key);1374 const entry = try self.ptr_types.getOrPut(self.gpa, key);
1339 if (entry.found_existing) {1375 if (entry.found_existing) {
1340 const fwd_id = entry.value_ptr.ty_id;1376 const fwd_id = entry.value_ptr.ty_id;
...@@ -1354,7 +1390,7 @@ const DeclGen = struct {...@@ -1354,7 +1390,7 @@ const DeclGen = struct {
1354 .fwd_emitted = false,1390 .fwd_emitted = false,
1355 };1391 };
13561392
1357 const child_ty_id = try self.resolveType(child_ty, .indirect);1393 const child_ty_id = try self.resolveType(child_ty, child_repr);
13581394
1359 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{1395 try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
1360 .id_result = result_id,1396 .id_result = result_id,
...@@ -1385,6 +1421,19 @@ const DeclGen = struct {...@@ -1385,6 +1421,19 @@ const DeclGen = struct {
1385 return ty_id;1421 return ty_id;
1386 }1422 }
13871423
1424 fn zigScalarOrVectorTypeLike(self: *DeclGen, new_ty: Type, base_ty: Type) !Type {
1425 const mod = self.module;
1426 const new_scalar_ty = new_ty.scalarType(mod);
1427 if (!base_ty.isVector(mod)) {
1428 return new_scalar_ty;
1429 }
1430
1431 return try mod.vectorType(.{
1432 .len = base_ty.vectorLen(mod),
1433 .child = new_scalar_ty.toIntern(),
1434 });
1435 }
1436
1388 /// Generate a union type. Union types are always generated with the1437 /// Generate a union type. Union types are always generated with the
1389 /// most aligned field active. If the tag alignment is greater1438 /// most aligned field active. If the tag alignment is greater
1390 /// than that of the payload, a regular union (non-packed, with both tag and1439 /// than that of the payload, a regular union (non-packed, with both tag and
...@@ -1645,11 +1694,10 @@ const DeclGen = struct {...@@ -1645,11 +1694,10 @@ const DeclGen = struct {
1645 },1694 },
1646 .Vector => {1695 .Vector => {
1647 const elem_ty = ty.childType(mod);1696 const elem_ty = ty.childType(mod);
1648 // TODO: Make `.direct`.1697 const elem_ty_id = try self.resolveType(elem_ty, repr);
1649 const elem_ty_id = try self.resolveType(elem_ty, .indirect);
1650 const len = ty.vectorLen(mod);1698 const len = ty.vectorLen(mod);
16511699
1652 if (self.isVector(ty)) {1700 if (self.isSpvVector(ty)) {
1653 return try self.spv.vectorType(len, elem_ty_id);1701 return try self.spv.vectorType(len, elem_ty_id);
1654 } else {1702 } else {
1655 return try self.arrayType(len, elem_ty_id);1703 return try self.arrayType(len, elem_ty_id);
...@@ -1922,81 +1970,897 @@ const DeclGen = struct {...@@ -1922,81 +1970,897 @@ const DeclGen = struct {
1922 return union_layout;1970 return union_layout;
1923 }1971 }
19241972
1925 /// This structure is used as helper for element-wise operations. It is intended1973 /// This structure represents a "temporary" value: Something we are currently
1926 /// to be used with vectors, fake vectors (arrays) and single elements.1974 /// operating on. It typically lives no longer than the function that
1927 const WipElementWise = struct {1975 /// implements a particular AIR operation. These are used to easier
1928 dg: *DeclGen,1976 /// implement vectorizable operations (see Vectorization and the build*
1929 result_ty: Type,1977 /// functions), and typically are only used for vectors of primitive types.
1978 const Temporary = struct {
1979 /// The type of the temporary. This is here mainly
1980 /// for easier bookkeeping. Because we will never really
1981 /// store Temporaries, they only cause extra stack space,
1982 /// therefore no real storage is wasted.
1930 ty: Type,1983 ty: Type,
1931 /// Always in direct representation.1984 /// The value that this temporary holds. This is not necessarily
1932 ty_id: IdRef,1985 /// a value that is actually usable, or a single value: It is virtual
1933 /// True if the input is an array type.1986 /// until materialize() is called, at which point is turned into
1934 is_array: bool,1987 /// the usual SPIR-V representation of `self.ty`.
1935 /// The element-wise operation should fill these results before calling finalize().1988 value: Temporary.Value,
1936 /// These should all be in **direct** representation! `finalize()` will convert1989
1937 /// them to indirect if required.1990 const Value = union(enum) {
1938 results: []IdRef,1991 singleton: IdResult,
19391992 exploded_vector: IdRange,
1940 fn deinit(wip: *WipElementWise) void {1993 };
1941 wip.dg.gpa.free(wip.results);1994
1942 }1995 fn init(ty: Type, singleton: IdResult) Temporary {
19431996 return .{ .ty = ty, .value = .{ .singleton = singleton } };
1944 /// Utility function to extract the element at a particular index in an1997 }
1945 /// input array. This type is expected to be a fake vector (array) if `wip.is_array`, and1998
1946 /// a vector or scalar otherwise.1999 fn materialize(self: Temporary, dg: *DeclGen) !IdResult {
1947 fn elementAt(wip: WipElementWise, ty: Type, value: IdRef, index: usize) !IdRef {2000 const mod = dg.module;
1948 const mod = wip.dg.module;2001 switch (self.value) {
1949 if (wip.is_array) {2002 .singleton => |id| return id,
1950 assert(ty.isVector(mod));2003 .exploded_vector => |range| {
1951 return try wip.dg.extractField(ty.childType(mod), value, @intCast(index));2004 assert(self.ty.isVector(mod));
2005 assert(self.ty.vectorLen(mod) == range.len);
2006 const consituents = try dg.gpa.alloc(IdRef, range.len);
2007 defer dg.gpa.free(consituents);
2008 for (consituents, 0..range.len) |*id, i| {
2009 id.* = range.at(i);
2010 }
2011 return dg.constructVector(self.ty, consituents);
2012 },
2013 }
2014 }
2015
2016 fn vectorization(self: Temporary, dg: *DeclGen) Vectorization {
2017 return Vectorization.fromType(self.ty, dg);
2018 }
2019
2020 fn pun(self: Temporary, new_ty: Type) Temporary {
2021 return .{
2022 .ty = new_ty,
2023 .value = self.value,
2024 };
2025 }
2026
2027 /// 'Explode' a temporary into separate elements. This turns a vector
2028 /// into a bag of elements.
2029 fn explode(self: Temporary, dg: *DeclGen) !IdRange {
2030 const mod = dg.module;
2031
2032 // If the value is a scalar, then this is a no-op.
2033 if (!self.ty.isVector(mod)) {
2034 return switch (self.value) {
2035 .singleton => |id| IdRange{ .base = @intFromEnum(id), .len = 1 },
2036 .exploded_vector => |range| range,
2037 };
2038 }
2039
2040 const ty_id = try dg.resolveType(self.ty.scalarType(mod), .direct);
2041 const n = self.ty.vectorLen(mod);
2042 const results = dg.spv.allocIds(n);
2043
2044 const id = switch (self.value) {
2045 .singleton => |id| id,
2046 .exploded_vector => |range| return range,
2047 };
2048
2049 for (0..n) |i| {
2050 const indexes = [_]u32{@intCast(i)};
2051 try dg.func.body.emit(dg.spv.gpa, .OpCompositeExtract, .{
2052 .id_result_type = ty_id,
2053 .id_result = results.at(i),
2054 .composite = id,
2055 .indexes = &indexes,
2056 });
2057 }
2058
2059 return results;
2060 }
2061 };
2062
2063 /// Initialize a `Temporary` from an AIR value.
2064 fn temporary(self: *DeclGen, inst: Air.Inst.Ref) !Temporary {
2065 return .{
2066 .ty = self.typeOf(inst),
2067 .value = .{ .singleton = try self.resolve(inst) },
2068 };
2069 }
2070
2071 /// This union describes how a particular operation should be vectorized.
2072 /// That depends on the operation and number of components of the inputs.
2073 const Vectorization = union(enum) {
2074 /// This is an operation between scalars.
2075 scalar,
2076 /// This is an operation between SPIR-V vectors.
2077 /// Value is number of components.
2078 spv_vectorized: u32,
2079 /// This operation is unrolled into separate operations.
2080 /// Inputs may still be SPIR-V vectors, for example,
2081 /// when the operation can't be vectorized in SPIR-V.
2082 /// Value is number of components.
2083 unrolled: u32,
2084
2085 /// Derive a vectorization from a particular type. This usually
2086 /// only checks the size, but the source-of-truth is implemented
2087 /// by `isSpvVector()`.
2088 fn fromType(ty: Type, dg: *DeclGen) Vectorization {
2089 const mod = dg.module;
2090 if (!ty.isVector(mod)) {
2091 return .scalar;
2092 } else if (dg.isSpvVector(ty)) {
2093 return .{ .spv_vectorized = ty.vectorLen(mod) };
1952 } else {2094 } else {
1953 assert(index == 0);2095 return .{ .unrolled = ty.vectorLen(mod) };
1954 return value;
1955 }2096 }
1956 }2097 }
19572098
1958 /// Turns the results of this WipElementWise into a result. This can be2099 /// Given two vectorization methods, compute a "unification": a fallback
1959 /// vectors, fake vectors (arrays) and single elements, depending on `result_ty`.2100 /// that works for both, according to the following rules:
1960 /// After calling this function, this WIP is no longer usable.2101 /// - Scalars may broadcast
1961 /// Results is in `direct` representation.2102 /// - SPIR-V vectorized operations may unroll
1962 fn finalize(wip: *WipElementWise) !IdRef {2103 /// - Prefer scalar > SPIR-V vectorized > unrolled
1963 if (wip.is_array) {2104 fn unify(a: Vectorization, b: Vectorization) Vectorization {
1964 // Convert all the constituents to indirect, as required for the array.2105 if (a == .scalar and b == .scalar) {
1965 for (wip.results) |*result| {2106 return .scalar;
1966 result.* = try wip.dg.convertToIndirect(wip.ty, result.*);2107 } else if (a == .spv_vectorized and b == .spv_vectorized) {
2108 assert(a.components() == b.components());
2109 return .{ .spv_vectorized = a.components() };
2110 } else if (a == .unrolled or b == .unrolled) {
2111 if (a == .unrolled and b == .unrolled) {
2112 assert(a.components() == b.components());
2113 return .{ .unrolled = a.components() };
2114 } else if (a == .unrolled) {
2115 return .{ .unrolled = a.components() };
2116 } else if (b == .unrolled) {
2117 return .{ .unrolled = b.components() };
2118 } else {
2119 unreachable;
1967 }2120 }
1968 return try wip.dg.constructArray(wip.result_ty, wip.results);
1969 } else {2121 } else {
1970 return wip.results[0];2122 if (a == .spv_vectorized) {
2123 return .{ .spv_vectorized = a.components() };
2124 } else if (b == .spv_vectorized) {
2125 return .{ .spv_vectorized = b.components() };
2126 } else {
2127 unreachable;
2128 }
1971 }2129 }
1972 }2130 }
19732131
1974 /// Allocate a result id at a particular index, and return it.2132 /// Force this vectorization to be unrolled, if its
1975 fn allocId(wip: *WipElementWise, index: usize) IdRef {2133 /// an operation involving vectors.
1976 assert(wip.is_array or index == 0);2134 fn unroll(self: Vectorization) Vectorization {
1977 wip.results[index] = wip.dg.spv.allocId();2135 return switch (self) {
1978 return wip.results[index];2136 .scalar, .unrolled => self,
2137 .spv_vectorized => |n| .{ .unrolled = n },
2138 };
1979 }2139 }
2140
2141 /// Query the number of components that inputs of this operation have.
2142 /// Note: for broadcasting scalars, this returns the number of elements
2143 /// that the broadcasted vector would have.
2144 fn components(self: Vectorization) u32 {
2145 return switch (self) {
2146 .scalar => 1,
2147 .spv_vectorized => |n| n,
2148 .unrolled => |n| n,
2149 };
2150 }
2151
2152 /// Query the number of operations involving this vectorization.
2153 /// This is basically the number of components, except that SPIR-V vectorized
2154 /// operations only need a single SPIR-V instruction.
2155 fn operations(self: Vectorization) u32 {
2156 return switch (self) {
2157 .scalar, .spv_vectorized => 1,
2158 .unrolled => |n| n,
2159 };
2160 }
2161
2162 /// Turns `ty` into the result-type of an individual vector operation.
2163 /// `ty` may be a scalar or vector, it doesn't matter.
2164 fn operationType(self: Vectorization, dg: *DeclGen, ty: Type) !Type {
2165 const mod = dg.module;
2166 const scalar_ty = ty.scalarType(mod);
2167 return switch (self) {
2168 .scalar, .unrolled => scalar_ty,
2169 .spv_vectorized => |n| try mod.vectorType(.{
2170 .len = n,
2171 .child = scalar_ty.toIntern(),
2172 }),
2173 };
2174 }
2175
2176 /// Turns `ty` into the result-type of the entire operation.
2177 /// `ty` may be a scalar or vector, it doesn't matter.
2178 fn resultType(self: Vectorization, dg: *DeclGen, ty: Type) !Type {
2179 const mod = dg.module;
2180 const scalar_ty = ty.scalarType(mod);
2181 return switch (self) {
2182 .scalar => scalar_ty,
2183 .unrolled, .spv_vectorized => |n| try mod.vectorType(.{
2184 .len = n,
2185 .child = scalar_ty.toIntern(),
2186 }),
2187 };
2188 }
2189
2190 /// Before a temporary can be used, some setup may need to be one. This function implements
2191 /// this setup, and returns a new type that holds the relevant information on how to access
2192 /// elements of the input.
2193 fn prepare(self: Vectorization, dg: *DeclGen, tmp: Temporary) !PreparedOperand {
2194 const mod = dg.module;
2195 const is_vector = tmp.ty.isVector(mod);
2196 const is_spv_vector = dg.isSpvVector(tmp.ty);
2197 const value: PreparedOperand.Value = switch (tmp.value) {
2198 .singleton => |id| switch (self) {
2199 .scalar => blk: {
2200 assert(!is_vector);
2201 break :blk .{ .scalar = id };
2202 },
2203 .spv_vectorized => blk: {
2204 if (is_vector) {
2205 assert(is_spv_vector);
2206 break :blk .{ .spv_vectorwise = id };
2207 }
2208
2209 // Broadcast scalar into vector.
2210 const vector_ty = try mod.vectorType(.{
2211 .len = self.components(),
2212 .child = tmp.ty.toIntern(),
2213 });
2214
2215 const vector = try dg.constructVectorSplat(vector_ty, id);
2216 return .{
2217 .ty = vector_ty,
2218 .value = .{ .spv_vectorwise = vector },
2219 };
2220 },
2221 .unrolled => blk: {
2222 if (is_vector) {
2223 break :blk .{ .vector_exploded = try tmp.explode(dg) };
2224 } else {
2225 break :blk .{ .scalar_broadcast = id };
2226 }
2227 },
2228 },
2229 .exploded_vector => |range| switch (self) {
2230 .scalar => unreachable,
2231 .spv_vectorized => |n| blk: {
2232 // We can vectorize this operation, but we have an exploded vector. This can happen
2233 // when a vectorizable operation succeeds a non-vectorizable operation. In this case,
2234 // pack up the IDs into a SPIR-V vector. This path should not be able to be hit with
2235 // a type that cannot do that.
2236 assert(is_spv_vector);
2237 assert(range.len == n);
2238 const vec = try tmp.materialize(dg);
2239 break :blk .{ .spv_vectorwise = vec };
2240 },
2241 .unrolled => |n| blk: {
2242 assert(range.len == n);
2243 break :blk .{ .vector_exploded = range };
2244 },
2245 },
2246 };
2247
2248 return .{
2249 .ty = tmp.ty,
2250 .value = value,
2251 };
2252 }
2253
2254 /// Finalize the results of an operation back into a temporary. `results` is
2255 /// a list of result-ids of the operation.
2256 fn finalize(self: Vectorization, ty: Type, results: IdRange) Temporary {
2257 assert(self.operations() == results.len);
2258 const value: Temporary.Value = switch (self) {
2259 .scalar, .spv_vectorized => blk: {
2260 break :blk .{ .singleton = results.at(0) };
2261 },
2262 .unrolled => blk: {
2263 break :blk .{ .exploded_vector = results };
2264 },
2265 };
2266
2267 return .{ .ty = ty, .value = value };
2268 }
2269
2270 /// This struct represents an operand that has gone through some setup, and is
2271 /// ready to be used as part of an operation.
2272 const PreparedOperand = struct {
2273 ty: Type,
2274 value: PreparedOperand.Value,
2275
2276 /// The types of value that a prepared operand can hold internally. Depends
2277 /// on the operation and input value.
2278 const Value = union(enum) {
2279 /// A single scalar value that is used by a scalar operation.
2280 scalar: IdResult,
2281 /// A single scalar that is broadcasted in an unrolled operation.
2282 scalar_broadcast: IdResult,
2283 /// A SPIR-V vector that is used in SPIR-V vectorize operation.
2284 spv_vectorwise: IdResult,
2285 /// A vector represented by a consecutive list of IDs that is used in an unrolled operation.
2286 vector_exploded: IdRange,
2287 };
2288
2289 /// Query the value at a particular index of the operation. Note that
2290 /// the index is *not* the component/lane, but the index of the *operation*. When
2291 /// this operation is vectorized, the return value of this function is a SPIR-V vector.
2292 /// See also `Vectorization.operations()`.
2293 fn at(self: PreparedOperand, i: usize) IdResult {
2294 switch (self.value) {
2295 .scalar => |id| {
2296 assert(i == 0);
2297 return id;
2298 },
2299 .scalar_broadcast => |id| {
2300 return id;
2301 },
2302 .spv_vectorwise => |id| {
2303 assert(i == 0);
2304 return id;
2305 },
2306 .vector_exploded => |range| {
2307 return range.at(i);
2308 },
2309 }
2310 }
2311 };
1980 };2312 };
19812313
1982 /// Create a new element-wise operation.2314 /// A utility function to compute the vectorization style of
1983 fn elementWise(self: *DeclGen, result_ty: Type, force_element_wise: bool) !WipElementWise {2315 /// a list of values. These values may be any of the following:
2316 /// - A `Vectorization` instance
2317 /// - A Type, in which case the vectorization is computed via `Vectorization.fromType`.
2318 /// - A Temporary, in which case the vectorization is computed via `Temporary.vectorization`.
2319 fn vectorization(self: *DeclGen, args: anytype) Vectorization {
2320 var v: Vectorization = undefined;
2321 assert(args.len >= 1);
2322 inline for (args, 0..) |arg, i| {
2323 const iv: Vectorization = switch (@TypeOf(arg)) {
2324 Vectorization => arg,
2325 Type => Vectorization.fromType(arg, self),
2326 Temporary => arg.vectorization(self),
2327 else => @compileError("invalid type"),
2328 };
2329 if (i == 0) {
2330 v = iv;
2331 } else {
2332 v = v.unify(iv);
2333 }
2334 }
2335 return v;
2336 }
2337
2338 /// This function builds an OpSConvert of OpUConvert depending on the
2339 /// signedness of the types.
2340 fn buildIntConvert(self: *DeclGen, dst_ty: Type, src: Temporary) !Temporary {
1984 const mod = self.module;2341 const mod = self.module;
1985 const is_array = result_ty.isVector(mod) and (!self.isVector(result_ty) or force_element_wise);
1986 const num_results = if (is_array) result_ty.vectorLen(mod) else 1;
1987 const results = try self.gpa.alloc(IdRef, num_results);
1988 @memset(results, undefined);
19892342
1990 const ty = if (is_array) result_ty.scalarType(mod) else result_ty;2343 const dst_ty_id = try self.resolveType(dst_ty.scalarType(mod), .direct);
1991 const ty_id = try self.resolveType(ty, .direct);2344 const src_ty_id = try self.resolveType(src.ty.scalarType(mod), .direct);
2345
2346 const v = self.vectorization(.{ dst_ty, src });
2347 const result_ty = try v.resultType(self, dst_ty);
2348
2349 // We can directly compare integers, because those type-IDs are cached.
2350 if (dst_ty_id == src_ty_id) {
2351 // Nothing to do, type-pun to the right value.
2352 // Note, Caller guarantees that the types fit (or caller will normalize after),
2353 // so we don't have to normalize here.
2354 // Note, dst_ty may be a scalar type even if we expect a vector, so we have to
2355 // convert to the right type here.
2356 return src.pun(result_ty);
2357 }
2358
2359 const ops = v.operations();
2360 const results = self.spv.allocIds(ops);
2361
2362 const op_result_ty = try v.operationType(self, dst_ty);
2363 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2364
2365 const opcode: Opcode = if (dst_ty.isSignedInt(mod)) .OpSConvert else .OpUConvert;
2366
2367 const op_src = try v.prepare(self, src);
2368
2369 for (0..ops) |i| {
2370 try self.func.body.emitRaw(self.spv.gpa, opcode, 3);
2371 self.func.body.writeOperand(spec.IdResultType, op_result_ty_id);
2372 self.func.body.writeOperand(IdResult, results.at(i));
2373 self.func.body.writeOperand(IdResult, op_src.at(i));
2374 }
2375
2376 return v.finalize(result_ty, results);
2377 }
2378
2379 fn buildFma(self: *DeclGen, a: Temporary, b: Temporary, c: Temporary) !Temporary {
2380 const target = self.getTarget();
2381
2382 const v = self.vectorization(.{ a, b, c });
2383 const ops = v.operations();
2384 const results = self.spv.allocIds(ops);
2385
2386 const op_result_ty = try v.operationType(self, a.ty);
2387 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2388 const result_ty = try v.resultType(self, a.ty);
2389
2390 const op_a = try v.prepare(self, a);
2391 const op_b = try v.prepare(self, b);
2392 const op_c = try v.prepare(self, c);
2393
2394 const set = try self.importExtendedSet();
2395
2396 // TODO: Put these numbers in some definition
2397 const instruction: u32 = switch (target.os.tag) {
2398 .opencl => 26, // fma
2399 // NOTE: Vulkan's FMA instruction does *NOT* produce the right values!
2400 // its precision guarantees do NOT match zigs and it does NOT match OpenCLs!
2401 // it needs to be emulated!
2402 .vulkan => unreachable, // TODO: See above
2403 else => unreachable,
2404 };
2405
2406 for (0..ops) |i| {
2407 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{
2408 .id_result_type = op_result_ty_id,
2409 .id_result = results.at(i),
2410 .set = set,
2411 .instruction = .{ .inst = instruction },
2412 .id_ref_4 = &.{ op_a.at(i), op_b.at(i), op_c.at(i) },
2413 });
2414 }
2415
2416 return v.finalize(result_ty, results);
2417 }
2418
2419 fn buildSelect(self: *DeclGen, condition: Temporary, lhs: Temporary, rhs: Temporary) !Temporary {
2420 const mod = self.module;
2421
2422 const v = self.vectorization(.{ condition, lhs, rhs });
2423 const ops = v.operations();
2424 const results = self.spv.allocIds(ops);
2425
2426 const op_result_ty = try v.operationType(self, lhs.ty);
2427 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2428 const result_ty = try v.resultType(self, lhs.ty);
2429
2430 assert(condition.ty.scalarType(mod).zigTypeTag(mod) == .Bool);
2431
2432 const cond = try v.prepare(self, condition);
2433 const object_1 = try v.prepare(self, lhs);
2434 const object_2 = try v.prepare(self, rhs);
2435
2436 for (0..ops) |i| {
2437 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
2438 .id_result_type = op_result_ty_id,
2439 .id_result = results.at(i),
2440 .condition = cond.at(i),
2441 .object_1 = object_1.at(i),
2442 .object_2 = object_2.at(i),
2443 });
2444 }
2445
2446 return v.finalize(result_ty, results);
2447 }
2448
2449 const CmpPredicate = enum {
2450 l_eq,
2451 l_ne,
2452 i_ne,
2453 i_eq,
2454 s_lt,
2455 s_gt,
2456 s_le,
2457 s_ge,
2458 u_lt,
2459 u_gt,
2460 u_le,
2461 u_ge,
2462 f_oeq,
2463 f_une,
2464 f_olt,
2465 f_ole,
2466 f_ogt,
2467 f_oge,
2468 };
2469
2470 fn buildCmp(self: *DeclGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary {
2471 const v = self.vectorization(.{ lhs, rhs });
2472 const ops = v.operations();
2473 const results = self.spv.allocIds(ops);
2474
2475 const op_result_ty = try v.operationType(self, Type.bool);
2476 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2477 const result_ty = try v.resultType(self, Type.bool);
2478
2479 const op_lhs = try v.prepare(self, lhs);
2480 const op_rhs = try v.prepare(self, rhs);
2481
2482 const opcode: Opcode = switch (pred) {
2483 .l_eq => .OpLogicalEqual,
2484 .l_ne => .OpLogicalNotEqual,
2485 .i_eq => .OpIEqual,
2486 .i_ne => .OpINotEqual,
2487 .s_lt => .OpSLessThan,
2488 .s_gt => .OpSGreaterThan,
2489 .s_le => .OpSLessThanEqual,
2490 .s_ge => .OpSGreaterThanEqual,
2491 .u_lt => .OpULessThan,
2492 .u_gt => .OpUGreaterThan,
2493 .u_le => .OpULessThanEqual,
2494 .u_ge => .OpUGreaterThanEqual,
2495 .f_oeq => .OpFOrdEqual,
2496 .f_une => .OpFUnordNotEqual,
2497 .f_olt => .OpFOrdLessThan,
2498 .f_ole => .OpFOrdLessThanEqual,
2499 .f_ogt => .OpFOrdGreaterThan,
2500 .f_oge => .OpFOrdGreaterThanEqual,
2501 };
2502
2503 for (0..ops) |i| {
2504 try self.func.body.emitRaw(self.spv.gpa, opcode, 4);
2505 self.func.body.writeOperand(spec.IdResultType, op_result_ty_id);
2506 self.func.body.writeOperand(IdResult, results.at(i));
2507 self.func.body.writeOperand(IdResult, op_lhs.at(i));
2508 self.func.body.writeOperand(IdResult, op_rhs.at(i));
2509 }
2510
2511 return v.finalize(result_ty, results);
2512 }
2513
2514 const UnaryOp = enum {
2515 l_not,
2516 bit_not,
2517 i_neg,
2518 f_neg,
2519 i_abs,
2520 f_abs,
2521 clz,
2522 ctz,
2523 floor,
2524 ceil,
2525 trunc,
2526 round,
2527 sqrt,
2528 sin,
2529 cos,
2530 tan,
2531 exp,
2532 exp2,
2533 log,
2534 log2,
2535 log10,
2536 };
2537
2538 fn buildUnary(self: *DeclGen, op: UnaryOp, operand: Temporary) !Temporary {
2539 const target = self.getTarget();
2540 const v = blk: {
2541 const v = self.vectorization(.{operand});
2542 break :blk switch (op) {
2543 // TODO: These instructions don't seem to be working
2544 // properly for LLVM-based backends on OpenCL for 8- and
2545 // 16-component vectors.
2546 .i_abs => if (target.os.tag == .opencl and v.components() >= 8) v.unroll() else v,
2547 else => v,
2548 };
2549 };
2550
2551 const ops = v.operations();
2552 const results = self.spv.allocIds(ops);
2553
2554 const op_result_ty = try v.operationType(self, operand.ty);
2555 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2556 const result_ty = try v.resultType(self, operand.ty);
2557
2558 const op_operand = try v.prepare(self, operand);
2559
2560 if (switch (op) {
2561 .l_not => .OpLogicalNot,
2562 .bit_not => .OpNot,
2563 .i_neg => .OpSNegate,
2564 .f_neg => .OpFNegate,
2565 else => @as(?Opcode, null),
2566 }) |opcode| {
2567 for (0..ops) |i| {
2568 try self.func.body.emitRaw(self.spv.gpa, opcode, 3);
2569 self.func.body.writeOperand(spec.IdResultType, op_result_ty_id);
2570 self.func.body.writeOperand(IdResult, results.at(i));
2571 self.func.body.writeOperand(IdResult, op_operand.at(i));
2572 }
2573 } else {
2574 const set = try self.importExtendedSet();
2575 const extinst: u32 = switch (target.os.tag) {
2576 .opencl => switch (op) {
2577 .i_abs => 141, // s_abs
2578 .f_abs => 23, // fabs
2579 .clz => 151, // clz
2580 .ctz => 152, // ctz
2581 .floor => 25, // floor
2582 .ceil => 12, // ceil
2583 .trunc => 66, // trunc
2584 .round => 55, // round
2585 .sqrt => 61, // sqrt
2586 .sin => 57, // sin
2587 .cos => 14, // cos
2588 .tan => 62, // tan
2589 .exp => 19, // exp
2590 .exp2 => 20, // exp2
2591 .log => 37, // log
2592 .log2 => 38, // log2
2593 .log10 => 39, // log10
2594 else => unreachable,
2595 },
2596 // Note: We'll need to check these for floating point accuracy
2597 // Vulkan does not put tight requirements on these, for correction
2598 // we might want to emulate them at some point.
2599 .vulkan => switch (op) {
2600 .i_abs => 5, // SAbs
2601 .f_abs => 4, // FAbs
2602 .clz => unreachable, // TODO
2603 .ctz => unreachable, // TODO
2604 .floor => 8, // Floor
2605 .ceil => 9, // Ceil
2606 .trunc => 3, // Trunc
2607 .round => 1, // Round
2608 .sqrt,
2609 .sin,
2610 .cos,
2611 .tan,
2612 .exp,
2613 .exp2,
2614 .log,
2615 .log2,
2616 .log10,
2617 => unreachable, // TODO
2618 else => unreachable,
2619 },
2620 else => unreachable,
2621 };
2622
2623 for (0..ops) |i| {
2624 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{
2625 .id_result_type = op_result_ty_id,
2626 .id_result = results.at(i),
2627 .set = set,
2628 .instruction = .{ .inst = extinst },
2629 .id_ref_4 = &.{op_operand.at(i)},
2630 });
2631 }
2632 }
2633
2634 return v.finalize(result_ty, results);
2635 }
2636
2637 const BinaryOp = enum {
2638 i_add,
2639 f_add,
2640 i_sub,
2641 f_sub,
2642 i_mul,
2643 f_mul,
2644 s_div,
2645 u_div,
2646 f_div,
2647 s_rem,
2648 f_rem,
2649 s_mod,
2650 u_mod,
2651 f_mod,
2652 srl,
2653 sra,
2654 sll,
2655 bit_and,
2656 bit_or,
2657 bit_xor,
2658 f_max,
2659 s_max,
2660 u_max,
2661 f_min,
2662 s_min,
2663 u_min,
2664 l_and,
2665 l_or,
2666 };
2667
2668 fn buildBinary(self: *DeclGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary {
2669 const target = self.getTarget();
19922670
2671 const v = self.vectorization(.{ lhs, rhs });
2672 const ops = v.operations();
2673 const results = self.spv.allocIds(ops);
2674
2675 const op_result_ty = try v.operationType(self, lhs.ty);
2676 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2677 const result_ty = try v.resultType(self, lhs.ty);
2678
2679 const op_lhs = try v.prepare(self, lhs);
2680 const op_rhs = try v.prepare(self, rhs);
2681
2682 if (switch (op) {
2683 .i_add => .OpIAdd,
2684 .f_add => .OpFAdd,
2685 .i_sub => .OpISub,
2686 .f_sub => .OpFSub,
2687 .i_mul => .OpIMul,
2688 .f_mul => .OpFMul,
2689 .s_div => .OpSDiv,
2690 .u_div => .OpUDiv,
2691 .f_div => .OpFDiv,
2692 .s_rem => .OpSRem,
2693 .f_rem => .OpFRem,
2694 .s_mod => .OpSMod,
2695 .u_mod => .OpUMod,
2696 .f_mod => .OpFMod,
2697 .srl => .OpShiftRightLogical,
2698 .sra => .OpShiftRightArithmetic,
2699 .sll => .OpShiftLeftLogical,
2700 .bit_and => .OpBitwiseAnd,
2701 .bit_or => .OpBitwiseOr,
2702 .bit_xor => .OpBitwiseXor,
2703 .l_and => .OpLogicalAnd,
2704 .l_or => .OpLogicalOr,
2705 else => @as(?Opcode, null),
2706 }) |opcode| {
2707 for (0..ops) |i| {
2708 try self.func.body.emitRaw(self.spv.gpa, opcode, 4);
2709 self.func.body.writeOperand(spec.IdResultType, op_result_ty_id);
2710 self.func.body.writeOperand(IdResult, results.at(i));
2711 self.func.body.writeOperand(IdResult, op_lhs.at(i));
2712 self.func.body.writeOperand(IdResult, op_rhs.at(i));
2713 }
2714 } else {
2715 const set = try self.importExtendedSet();
2716
2717 // TODO: Put these numbers in some definition
2718 const extinst: u32 = switch (target.os.tag) {
2719 .opencl => switch (op) {
2720 .f_max => 27, // fmax
2721 .s_max => 156, // s_max
2722 .u_max => 157, // u_max
2723 .f_min => 28, // fmin
2724 .s_min => 158, // s_min
2725 .u_min => 159, // u_min
2726 else => unreachable,
2727 },
2728 .vulkan => switch (op) {
2729 .f_max => 40, // FMax
2730 .s_max => 42, // SMax
2731 .u_max => 41, // UMax
2732 .f_min => 37, // FMin
2733 .s_min => 39, // SMin
2734 .u_min => 38, // UMin
2735 else => unreachable,
2736 },
2737 else => unreachable,
2738 };
2739
2740 for (0..ops) |i| {
2741 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{
2742 .id_result_type = op_result_ty_id,
2743 .id_result = results.at(i),
2744 .set = set,
2745 .instruction = .{ .inst = extinst },
2746 .id_ref_4 = &.{ op_lhs.at(i), op_rhs.at(i) },
2747 });
2748 }
2749 }
2750
2751 return v.finalize(result_ty, results);
2752 }
2753
2754 /// This function builds an extended multiplication, either OpSMulExtended or OpUMulExtended on Vulkan,
2755 /// or OpIMul and s_mul_hi or u_mul_hi on OpenCL.
2756 fn buildWideMul(
2757 self: *DeclGen,
2758 op: enum {
2759 s_mul_extended,
2760 u_mul_extended,
2761 },
2762 lhs: Temporary,
2763 rhs: Temporary,
2764 ) !struct { Temporary, Temporary } {
2765 const mod = self.module;
2766 const target = self.getTarget();
2767 const ip = &mod.intern_pool;
2768
2769 const v = lhs.vectorization(self).unify(rhs.vectorization(self));
2770 const ops = v.operations();
2771
2772 const arith_op_ty = try v.operationType(self, lhs.ty);
2773 const arith_op_ty_id = try self.resolveType(arith_op_ty, .direct);
2774
2775 const lhs_op = try v.prepare(self, lhs);
2776 const rhs_op = try v.prepare(self, rhs);
2777
2778 const value_results = self.spv.allocIds(ops);
2779 const overflow_results = self.spv.allocIds(ops);
2780
2781 switch (target.os.tag) {
2782 .opencl => {
2783 // Currently, SPIRV-LLVM-Translator based backends cannot deal with OpSMulExtended and
2784 // OpUMulExtended. For these we will use the OpenCL s_mul_hi to compute the high-order bits
2785 // instead.
2786 const set = try self.importExtendedSet();
2787 const overflow_inst: u32 = switch (op) {
2788 .s_mul_extended => 160, // s_mul_hi
2789 .u_mul_extended => 203, // u_mul_hi
2790 };
2791
2792 for (0..ops) |i| {
2793 try self.func.body.emit(self.spv.gpa, .OpIMul, .{
2794 .id_result_type = arith_op_ty_id,
2795 .id_result = value_results.at(i),
2796 .operand_1 = lhs_op.at(i),
2797 .operand_2 = rhs_op.at(i),
2798 });
2799
2800 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{
2801 .id_result_type = arith_op_ty_id,
2802 .id_result = overflow_results.at(i),
2803 .set = set,
2804 .instruction = .{ .inst = overflow_inst },
2805 .id_ref_4 = &.{ lhs_op.at(i), rhs_op.at(i) },
2806 });
2807 }
2808 },
2809 .vulkan => {
2810 const op_result_ty = blk: {
2811 // Operations return a struct{T, T}
2812 // where T is maybe vectorized.
2813 const types = [2]InternPool.Index{ arith_op_ty.toIntern(), arith_op_ty.toIntern() };
2814 const values = [2]InternPool.Index{ .none, .none };
2815 const index = try ip.getAnonStructType(mod.gpa, .{
2816 .types = &types,
2817 .values = &values,
2818 .names = &.{},
2819 });
2820 break :blk Type.fromInterned(index);
2821 };
2822 const op_result_ty_id = try self.resolveType(op_result_ty, .direct);
2823
2824 const opcode: Opcode = switch (op) {
2825 .s_mul_extended => .OpSMulExtended,
2826 .u_mul_extended => .OpUMulExtended,
2827 };
2828
2829 for (0..ops) |i| {
2830 const op_result = self.spv.allocId();
2831
2832 try self.func.body.emitRaw(self.spv.gpa, opcode, 4);
2833 self.func.body.writeOperand(spec.IdResultType, op_result_ty_id);
2834 self.func.body.writeOperand(IdResult, op_result);
2835 self.func.body.writeOperand(IdResult, lhs_op.at(i));
2836 self.func.body.writeOperand(IdResult, rhs_op.at(i));
2837
2838 // The above operation returns a struct. We might want to expand
2839 // Temporary to deal with the fact that these are structs eventually,
2840 // but for now, take the struct apart and return two separate vectors.
2841
2842 try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{
2843 .id_result_type = arith_op_ty_id,
2844 .id_result = value_results.at(i),
2845 .composite = op_result,
2846 .indexes = &.{0},
2847 });
2848
2849 try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{
2850 .id_result_type = arith_op_ty_id,
2851 .id_result = overflow_results.at(i),
2852 .composite = op_result,
2853 .indexes = &.{1},
2854 });
2855 }
2856 },
2857 else => unreachable,
2858 }
2859
2860 const result_ty = try v.resultType(self, lhs.ty);
1993 return .{2861 return .{
1994 .dg = self,2862 v.finalize(result_ty, value_results),
1995 .result_ty = result_ty,2863 v.finalize(result_ty, overflow_results),
1996 .ty = ty,
1997 .ty_id = ty_id,
1998 .is_array = is_array,
1999 .results = results,
2000 };2864 };
2001 }2865 }
20022866
...@@ -2235,47 +3099,58 @@ const DeclGen = struct {...@@ -2235,47 +3099,58 @@ const DeclGen = struct {
2235 }3099 }
2236 }3100 }
22373101
2238 fn intFromBool(self: *DeclGen, ty: Type, condition_id: IdRef) !IdRef {3102 fn intFromBool(self: *DeclGen, value: Temporary) !Temporary {
2239 const zero_id = try self.constInt(ty, 0, .direct);3103 return try self.intFromBool2(value, Type.u1);
2240 const one_id = try self.constInt(ty, 1, .direct);3104 }
2241 const result_id = self.spv.allocId();3105
2242 try self.func.body.emit(self.spv.gpa, .OpSelect, .{3106 fn intFromBool2(self: *DeclGen, value: Temporary, result_ty: Type) !Temporary {
2243 .id_result_type = try self.resolveType(ty, .direct),3107 const zero_id = try self.constInt(result_ty, 0, .direct);
2244 .id_result = result_id,3108 const one_id = try self.constInt(result_ty, 1, .direct);
2245 .condition = condition_id,3109
2246 .object_1 = one_id,3110 return try self.buildSelect(
2247 .object_2 = zero_id,3111 value,
2248 });3112 Temporary.init(result_ty, one_id),
2249 return result_id;3113 Temporary.init(result_ty, zero_id),
3114 );
2250 }3115 }
22513116
2252 /// Convert representation from indirect (in memory) to direct (in 'register')3117 /// Convert representation from indirect (in memory) to direct (in 'register')
2253 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct).3118 /// This converts the argument type from resolveType(ty, .indirect) to resolveType(ty, .direct).
2254 fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {3119 fn convertToDirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {
2255 const mod = self.module;3120 const mod = self.module;
2256 return switch (ty.zigTypeTag(mod)) {3121 switch (ty.scalarType(mod).zigTypeTag(mod)) {
2257 .Bool => blk: {3122 .Bool => {
2258 const result_id = self.spv.allocId();3123 const false_id = try self.constBool(false, .indirect);
2259 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{3124 // The operation below requires inputs in direct representation, but the operand
2260 .id_result_type = try self.resolveType(Type.bool, .direct),3125 // is actually in indirect representation.
2261 .id_result = result_id,3126 // Cheekily swap out the type to the direct equivalent of the indirect type here, they have the
2262 .operand_1 = operand_id,3127 // same representation when converted to SPIR-V.
2263 .operand_2 = try self.constBool(false, .indirect),3128 const operand_ty = try self.zigScalarOrVectorTypeLike(Type.u1, ty);
2264 });3129 // Note: We can guarantee that these are the same ID due to the SPIR-V Module's `vector_types` cache!
2265 break :blk result_id;3130 assert(try self.resolveType(operand_ty, .direct) == try self.resolveType(ty, .indirect));
3131
3132 const result = try self.buildCmp(
3133 .i_ne,
3134 Temporary.init(operand_ty, operand_id),
3135 Temporary.init(Type.u1, false_id),
3136 );
3137 return try result.materialize(self);
2266 },3138 },
2267 else => operand_id,3139 else => return operand_id,
2268 };3140 }
2269 }3141 }
22703142
2271 /// Convert representation from direct (in 'register) to direct (in memory)3143 /// Convert representation from direct (in 'register) to direct (in memory)
2272 /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect).3144 /// This converts the argument type from resolveType(ty, .direct) to resolveType(ty, .indirect).
2273 fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {3145 fn convertToIndirect(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {
2274 const mod = self.module;3146 const mod = self.module;
2275 return switch (ty.zigTypeTag(mod)) {3147 switch (ty.scalarType(mod).zigTypeTag(mod)) {
2276 .Bool => try self.intFromBool(Type.u1, operand_id),3148 .Bool => {
2277 else => operand_id,3149 const result = try self.intFromBool(Temporary.init(ty, operand_id));
2278 };3150 return try result.materialize(self);
3151 },
3152 else => return operand_id,
3153 }
2279 }3154 }
22803155
2281 fn extractField(self: *DeclGen, result_ty: Type, object: IdRef, field: u32) !IdRef {3156 fn extractField(self: *DeclGen, result_ty: Type, object: IdRef, field: u32) !IdRef {
...@@ -2292,6 +3167,21 @@ const DeclGen = struct {...@@ -2292,6 +3167,21 @@ const DeclGen = struct {
2292 return try self.convertToDirect(result_ty, result_id);3167 return try self.convertToDirect(result_ty, result_id);
2293 }3168 }
22943169
3170 fn extractVectorComponent(self: *DeclGen, result_ty: Type, vector_id: IdRef, field: u32) !IdRef {
3171 // Whether this is an OpTypeVector or OpTypeArray, we need to emit the same instruction regardless.
3172 const result_ty_id = try self.resolveType(result_ty, .direct);
3173 const result_id = self.spv.allocId();
3174 const indexes = [_]u32{field};
3175 try self.func.body.emit(self.spv.gpa, .OpCompositeExtract, .{
3176 .id_result_type = result_ty_id,
3177 .id_result = result_id,
3178 .composite = vector_id,
3179 .indexes = &indexes,
3180 });
3181 // Vector components are already stored in direct representation.
3182 return result_id;
3183 }
3184
2295 const MemoryOptions = struct {3185 const MemoryOptions = struct {
2296 is_volatile: bool = false,3186 is_volatile: bool = false,
2297 };3187 };
...@@ -2338,26 +3228,35 @@ const DeclGen = struct {...@@ -2338,26 +3228,35 @@ const DeclGen = struct {
2338 const air_tags = self.air.instructions.items(.tag);3228 const air_tags = self.air.instructions.items(.tag);
2339 const maybe_result_id: ?IdRef = switch (air_tags[@intFromEnum(inst)]) {3229 const maybe_result_id: ?IdRef = switch (air_tags[@intFromEnum(inst)]) {
2340 // zig fmt: off3230 // zig fmt: off
2341 .add, .add_wrap, .add_optimized => try self.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),3231 .add, .add_wrap, .add_optimized => try self.airArithOp(inst, .f_add, .i_add, .i_add),
2342 .sub, .sub_wrap, .sub_optimized => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),3232 .sub, .sub_wrap, .sub_optimized => try self.airArithOp(inst, .f_sub, .i_sub, .i_sub),
2343 .mul, .mul_wrap, .mul_optimized => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),3233 .mul, .mul_wrap, .mul_optimized => try self.airArithOp(inst, .f_mul, .i_mul, .i_mul),
23443234
23453235 .sqrt => try self.airUnOpSimple(inst, .sqrt),
3236 .sin => try self.airUnOpSimple(inst, .sin),
3237 .cos => try self.airUnOpSimple(inst, .cos),
3238 .tan => try self.airUnOpSimple(inst, .tan),
3239 .exp => try self.airUnOpSimple(inst, .exp),
3240 .exp2 => try self.airUnOpSimple(inst, .exp2),
3241 .log => try self.airUnOpSimple(inst, .log),
3242 .log2 => try self.airUnOpSimple(inst, .log2),
3243 .log10 => try self.airUnOpSimple(inst, .log10),
2346 .abs => try self.airAbs(inst),3244 .abs => try self.airAbs(inst),
2347 .floor => try self.airFloor(inst),3245 .floor => try self.airUnOpSimple(inst, .floor),
3246 .ceil => try self.airUnOpSimple(inst, .ceil),
3247 .round => try self.airUnOpSimple(inst, .round),
3248 .trunc_float => try self.airUnOpSimple(inst, .trunc),
3249 .neg, .neg_optimized => try self.airUnOpSimple(inst, .f_neg),
23483250
2349 .div_floor => try self.airDivFloor(inst),3251 .div_float, .div_float_optimized => try self.airArithOp(inst, .f_div, .s_div, .u_div),
3252 .div_floor, .div_floor_optimized => try self.airDivFloor(inst),
3253 .div_trunc, .div_trunc_optimized => try self.airDivTrunc(inst),
23503254
2351 .div_float,3255 .rem, .rem_optimized => try self.airArithOp(inst, .f_rem, .s_rem, .u_mod),
2352 .div_float_optimized,3256 .mod, .mod_optimized => try self.airArithOp(inst, .f_mod, .s_mod, .u_mod),
2353 .div_trunc,
2354 .div_trunc_optimized => try self.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv),
2355 .rem, .rem_optimized => try self.airArithOp(inst, .OpFRem, .OpSRem, .OpSRem),
2356 .mod, .mod_optimized => try self.airArithOp(inst, .OpFMod, .OpSMod, .OpSMod),
23573257
23583258 .add_with_overflow => try self.airAddSubOverflow(inst, .i_add, .u_lt, .s_lt),
2359 .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),3259 .sub_with_overflow => try self.airAddSubOverflow(inst, .i_sub, .u_gt, .s_gt),
2360 .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),
2361 .mul_with_overflow => try self.airMulOverflow(inst),3260 .mul_with_overflow => try self.airMulOverflow(inst),
2362 .shl_with_overflow => try self.airShlOverflow(inst),3261 .shl_with_overflow => try self.airShlOverflow(inst),
23633262
...@@ -2366,6 +3265,8 @@ const DeclGen = struct {...@@ -2366,6 +3265,8 @@ const DeclGen = struct {
2366 .ctz => try self.airClzCtz(inst, .ctz),3265 .ctz => try self.airClzCtz(inst, .ctz),
2367 .clz => try self.airClzCtz(inst, .clz),3266 .clz => try self.airClzCtz(inst, .clz),
23683267
3268 .select => try self.airSelect(inst),
3269
2369 .splat => try self.airSplat(inst),3270 .splat => try self.airSplat(inst),
2370 .reduce, .reduce_optimized => try self.airReduce(inst),3271 .reduce, .reduce_optimized => try self.airReduce(inst),
2371 .shuffle => try self.airShuffle(inst),3272 .shuffle => try self.airShuffle(inst),
...@@ -2373,17 +3274,17 @@ const DeclGen = struct {...@@ -2373,17 +3274,17 @@ const DeclGen = struct {
2373 .ptr_add => try self.airPtrAdd(inst),3274 .ptr_add => try self.airPtrAdd(inst),
2374 .ptr_sub => try self.airPtrSub(inst),3275 .ptr_sub => try self.airPtrSub(inst),
23753276
2376 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd),3277 .bit_and => try self.airBinOpSimple(inst, .bit_and),
2377 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr),3278 .bit_or => try self.airBinOpSimple(inst, .bit_or),
2378 .xor => try self.airBinOpSimple(inst, .OpBitwiseXor),3279 .xor => try self.airBinOpSimple(inst, .bit_xor),
2379 .bool_and => try self.airBinOpSimple(inst, .OpLogicalAnd),3280 .bool_and => try self.airBinOpSimple(inst, .l_and),
2380 .bool_or => try self.airBinOpSimple(inst, .OpLogicalOr),3281 .bool_or => try self.airBinOpSimple(inst, .l_or),
23813282
2382 .shl, .shl_exact => try self.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical),3283 .shl, .shl_exact => try self.airShift(inst, .sll, .sll),
2383 .shr, .shr_exact => try self.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic),3284 .shr, .shr_exact => try self.airShift(inst, .srl, .sra),
23843285
2385 .min => try self.airMinMax(inst, .lt),3286 .min => try self.airMinMax(inst, .min),
2386 .max => try self.airMinMax(inst, .gt),3287 .max => try self.airMinMax(inst, .max),
23873288
2388 .bitcast => try self.airBitCast(inst),3289 .bitcast => try self.airBitCast(inst),
2389 .intcast, .trunc => try self.airIntCast(inst),3290 .intcast, .trunc => try self.airIntCast(inst),
...@@ -2484,39 +3385,23 @@ const DeclGen = struct {...@@ -2484,39 +3385,23 @@ const DeclGen = struct {
2484 try self.inst_results.putNoClobber(self.gpa, inst, result_id);3385 try self.inst_results.putNoClobber(self.gpa, inst, result_id);
2485 }3386 }
24863387
2487 fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef {3388 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, op: BinaryOp) !?IdRef {
2488 var wip = try self.elementWise(ty, false);
2489 defer wip.deinit();
2490 for (0..wip.results.len) |i| {
2491 try self.func.body.emit(self.spv.gpa, opcode, .{
2492 .id_result_type = wip.ty_id,
2493 .id_result = wip.allocId(i),
2494 .operand_1 = try wip.elementAt(ty, lhs_id, i),
2495 .operand_2 = try wip.elementAt(ty, rhs_id, i),
2496 });
2497 }
2498 return try wip.finalize();
2499 }
2500
2501 fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef {
2502 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3389 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2503 const lhs_id = try self.resolve(bin_op.lhs);3390 const lhs = try self.temporary(bin_op.lhs);
2504 const rhs_id = try self.resolve(bin_op.rhs);3391 const rhs = try self.temporary(bin_op.rhs);
2505 const ty = self.typeOf(bin_op.lhs);
25063392
2507 return try self.binOpSimple(ty, lhs_id, rhs_id, opcode);3393 const result = try self.buildBinary(op, lhs, rhs);
3394 return try result.materialize(self);
2508 }3395 }
25093396
2510 fn airShift(self: *DeclGen, inst: Air.Inst.Index, comptime unsigned: Opcode, comptime signed: Opcode) !?IdRef {3397 fn airShift(self: *DeclGen, inst: Air.Inst.Index, unsigned: BinaryOp, signed: BinaryOp) !?IdRef {
2511 const mod = self.module;3398 const mod = self.module;
2512 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3399 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2513 const lhs_id = try self.resolve(bin_op.lhs);3400
2514 const rhs_id = try self.resolve(bin_op.rhs);3401 const base = try self.temporary(bin_op.lhs);
3402 const shift = try self.temporary(bin_op.rhs);
25153403
2516 const result_ty = self.typeOfIndex(inst);3404 const result_ty = self.typeOfIndex(inst);
2517 const shift_ty = self.typeOf(bin_op.rhs);
2518 const scalar_result_ty_id = try self.resolveType(result_ty.scalarType(mod), .direct);
2519 const scalar_shift_ty_id = try self.resolveType(shift_ty.scalarType(mod), .direct);
25203405
2521 const info = self.arithmeticTypeInfo(result_ty);3406 const info = self.arithmeticTypeInfo(result_ty);
2522 switch (info.class) {3407 switch (info.class) {
...@@ -2525,121 +3410,58 @@ const DeclGen = struct {...@@ -2525,121 +3410,58 @@ const DeclGen = struct {
2525 .float, .bool => unreachable,3410 .float, .bool => unreachable,
2526 }3411 }
25273412
2528 var wip = try self.elementWise(result_ty, false);3413 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
2529 defer wip.deinit();3414 // so just manually upcast it if required.
2530 for (wip.results, 0..) |*result_id, i| {
2531 const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i);
2532 const rhs_elem_id = try wip.elementAt(shift_ty, rhs_id, i);
2533
2534 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
2535 // so just manually upcast it if required.
2536 const shift_id = if (scalar_shift_ty_id != scalar_result_ty_id) blk: {
2537 const shift_id = self.spv.allocId();
2538 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
2539 .id_result_type = wip.ty_id,
2540 .id_result = shift_id,
2541 .unsigned_value = rhs_elem_id,
2542 });
2543 break :blk shift_id;
2544 } else rhs_elem_id;
2545
2546 const value_id = self.spv.allocId();
2547 const args = .{
2548 .id_result_type = wip.ty_id,
2549 .id_result = value_id,
2550 .base = lhs_elem_id,
2551 .shift = shift_id,
2552 };
25533415
2554 if (result_ty.isSignedInt(mod)) {3416 // Note: The sign may differ here between the shift and the base type, in case
2555 try self.func.body.emit(self.spv.gpa, signed, args);3417 // of an arithmetic right shift. SPIR-V still expects the same type,
2556 } else {3418 // so in that case we have to cast convert to signed.
2557 try self.func.body.emit(self.spv.gpa, unsigned, args);3419 const casted_shift = try self.buildIntConvert(base.ty.scalarType(mod), shift);
2558 }
25593420
2560 result_id.* = try self.normalize(wip.ty, value_id, info);3421 const shifted = switch (info.signedness) {
2561 }3422 .unsigned => try self.buildBinary(unsigned, base, casted_shift),
2562 return try wip.finalize();3423 .signed => try self.buildBinary(signed, base, casted_shift),
3424 };
3425
3426 const result = try self.normalize(shifted, info);
3427 return try result.materialize(self);
2563 }3428 }
25643429
2565 fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef {3430 const MinMax = enum { min, max };
3431
3432 fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: MinMax) !?IdRef {
2566 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3433 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2567 const lhs_id = try self.resolve(bin_op.lhs);
2568 const rhs_id = try self.resolve(bin_op.rhs);
2569 const result_ty = self.typeOfIndex(inst);
25703434
2571 return try self.minMax(result_ty, op, lhs_id, rhs_id);3435 const lhs = try self.temporary(bin_op.lhs);
3436 const rhs = try self.temporary(bin_op.rhs);
3437
3438 const result = try self.minMax(lhs, rhs, op);
3439 return try result.materialize(self);
2572 }3440 }
25733441
2574 fn minMax(self: *DeclGen, result_ty: Type, op: std.math.CompareOperator, lhs_id: IdRef, rhs_id: IdRef) !IdRef {3442 fn minMax(self: *DeclGen, lhs: Temporary, rhs: Temporary, op: MinMax) !Temporary {
2575 const info = self.arithmeticTypeInfo(result_ty);3443 const info = self.arithmeticTypeInfo(lhs.ty);
2576 const target = self.getTarget();
25773444
2578 const use_backup_codegen = target.os.tag == .opencl and info.class != .float;3445 const binop: BinaryOp = switch (info.class) {
2579 var wip = try self.elementWise(result_ty, use_backup_codegen);3446 .float => switch (op) {
2580 defer wip.deinit();3447 .min => .f_min,
25813448 .max => .f_max,
2582 for (wip.results, 0..) |*result_id, i| {3449 },
2583 const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i);3450 .integer, .strange_integer => switch (info.signedness) {
2584 const rhs_elem_id = try wip.elementAt(result_ty, rhs_id, i);3451 .signed => switch (op) {
25853452 .min => .s_min,
2586 if (use_backup_codegen) {3453 .max => .s_max,
2587 const cmp_id = try self.cmp(op, Type.bool, wip.ty, lhs_elem_id, rhs_elem_id);3454 },
2588 result_id.* = self.spv.allocId();3455 .unsigned => switch (op) {
2589 try self.func.body.emit(self.spv.gpa, .OpSelect, .{3456 .min => .u_min,
2590 .id_result_type = wip.ty_id,3457 .max => .u_max,
2591 .id_result = result_id.*,3458 },
2592 .condition = cmp_id,3459 },
2593 .object_1 = lhs_elem_id,3460 .composite_integer => unreachable, // TODO
2594 .object_2 = rhs_elem_id,3461 .bool => unreachable,
2595 });3462 };
2596 } else {
2597 const ext_inst: Word = switch (target.os.tag) {
2598 .opencl => switch (op) {
2599 .lt => 28, // fmin
2600 .gt => 27, // fmax
2601 else => unreachable,
2602 },
2603 .vulkan => switch (info.class) {
2604 .float => switch (op) {
2605 .lt => 37, // FMin
2606 .gt => 40, // FMax
2607 else => unreachable,
2608 },
2609 .integer, .strange_integer => switch (info.signedness) {
2610 .signed => switch (op) {
2611 .lt => 39, // SMin
2612 .gt => 42, // SMax
2613 else => unreachable,
2614 },
2615 .unsigned => switch (op) {
2616 .lt => 38, // UMin
2617 .gt => 41, // UMax
2618 else => unreachable,
2619 },
2620 },
2621 .composite_integer => unreachable, // TODO
2622 .bool => unreachable,
2623 },
2624 else => unreachable,
2625 };
2626 const set_id = switch (target.os.tag) {
2627 .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"),
2628 .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"),
2629 else => unreachable,
2630 };
26313463
2632 result_id.* = self.spv.allocId();3464 return try self.buildBinary(binop, lhs, rhs);
2633 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{
2634 .id_result_type = wip.ty_id,
2635 .id_result = result_id.*,
2636 .set = set_id,
2637 .instruction = .{ .inst = ext_inst },
2638 .id_ref_4 = &.{ lhs_elem_id, rhs_elem_id },
2639 });
2640 }
2641 }
2642 return wip.finalize();
2643 }3465 }
26443466
2645 /// This function normalizes values to a canonical representation3467 /// This function normalizes values to a canonical representation
...@@ -2650,41 +3472,24 @@ const DeclGen = struct {...@@ -2650,41 +3472,24 @@ const DeclGen = struct {
2650 /// - Signed integers are also sign extended if they are negative.3472 /// - Signed integers are also sign extended if they are negative.
2651 /// All other values are returned unmodified (this makes strange integer3473 /// All other values are returned unmodified (this makes strange integer
2652 /// wrapping easier to use in generic operations).3474 /// wrapping easier to use in generic operations).
2653 fn normalize(self: *DeclGen, ty: Type, value_id: IdRef, info: ArithmeticTypeInfo) !IdRef {3475 fn normalize(self: *DeclGen, value: Temporary, info: ArithmeticTypeInfo) !Temporary {
3476 const mod = self.module;
3477 const ty = value.ty;
2654 switch (info.class) {3478 switch (info.class) {
2655 .integer, .bool, .float => return value_id,3479 .integer, .bool, .float => return value,
2656 .composite_integer => unreachable, // TODO3480 .composite_integer => unreachable, // TODO
2657 .strange_integer => switch (info.signedness) {3481 .strange_integer => switch (info.signedness) {
2658 .unsigned => {3482 .unsigned => {
2659 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;3483 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
2660 const result_id = self.spv.allocId();3484 const mask_id = try self.constInt(ty.scalarType(mod), mask_value, .direct);
2661 const mask_id = try self.constInt(ty, mask_value, .direct);3485 return try self.buildBinary(.bit_and, value, Temporary.init(ty.scalarType(mod), mask_id));
2662 try self.func.body.emit(self.spv.gpa, .OpBitwiseAnd, .{
2663 .id_result_type = try self.resolveType(ty, .direct),
2664 .id_result = result_id,
2665 .operand_1 = value_id,
2666 .operand_2 = mask_id,
2667 });
2668 return result_id;
2669 },3486 },
2670 .signed => {3487 .signed => {
2671 // Shift left and right so that we can copy the sight bit that way.3488 // Shift left and right so that we can copy the sight bit that way.
2672 const shift_amt_id = try self.constInt(ty, info.backing_bits - info.bits, .direct);3489 const shift_amt_id = try self.constInt(ty.scalarType(mod), info.backing_bits - info.bits, .direct);
2673 const left_id = self.spv.allocId();3490 const shift_amt = Temporary.init(ty.scalarType(mod), shift_amt_id);
2674 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{3491 const left = try self.buildBinary(.sll, value, shift_amt);
2675 .id_result_type = try self.resolveType(ty, .direct),3492 return try self.buildBinary(.sra, left, shift_amt);
2676 .id_result = left_id,
2677 .base = value_id,
2678 .shift = shift_amt_id,
2679 });
2680 const right_id = self.spv.allocId();
2681 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{
2682 .id_result_type = try self.resolveType(ty, .direct),
2683 .id_result = right_id,
2684 .base = left_id,
2685 .shift = shift_amt_id,
2686 });
2687 return right_id;
2688 },3493 },
2689 },3494 },
2690 }3495 }
...@@ -2692,491 +3497,438 @@ const DeclGen = struct {...@@ -2692,491 +3497,438 @@ const DeclGen = struct {
26923497
2693 fn airDivFloor(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3498 fn airDivFloor(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2694 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3499 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2695 const lhs_id = try self.resolve(bin_op.lhs);3500
2696 const rhs_id = try self.resolve(bin_op.rhs);3501 const lhs = try self.temporary(bin_op.lhs);
2697 const ty = self.typeOfIndex(inst);3502 const rhs = try self.temporary(bin_op.rhs);
2698 const ty_id = try self.resolveType(ty, .direct);3503
2699 const info = self.arithmeticTypeInfo(ty);3504 const info = self.arithmeticTypeInfo(lhs.ty);
2700 switch (info.class) {3505 switch (info.class) {
2701 .composite_integer => unreachable, // TODO3506 .composite_integer => unreachable, // TODO
2702 .integer, .strange_integer => {3507 .integer, .strange_integer => {
2703 const zero_id = try self.constInt(ty, 0, .direct);3508 switch (info.signedness) {
2704 const one_id = try self.constInt(ty, 1, .direct);3509 .unsigned => {
27053510 const result = try self.buildBinary(.u_div, lhs, rhs);
2706 // (a ^ b) > 03511 return try result.materialize(self);
2707 const bin_bitwise_id = try self.binOpSimple(ty, lhs_id, rhs_id, .OpBitwiseXor);3512 },
2708 const is_positive_id = try self.cmp(.gt, Type.bool, ty, bin_bitwise_id, zero_id);3513 .signed => {},
27093514 }
2710 // a / b3515
2711 const positive_div_id = try self.arithOp(ty, lhs_id, rhs_id, .OpFDiv, .OpSDiv, .OpUDiv);3516 // For signed integers:
27123517 // (a / b) - (a % b != 0 && a < 0 != b < 0);
2713 // - (abs(a) + abs(b) - 1) / abs(b)3518 // There shouldn't be any overflow issues.
2714 const lhs_abs = try self.abs(ty, ty, lhs_id);3519
2715 const rhs_abs = try self.abs(ty, ty, rhs_id);3520 const div = try self.buildBinary(.s_div, lhs, rhs);
2716 const negative_div_lhs = try self.arithOp(3521 const rem = try self.buildBinary(.s_rem, lhs, rhs);
2717 ty,3522
2718 try self.arithOp(ty, lhs_abs, rhs_abs, .OpFAdd, .OpIAdd, .OpIAdd),3523 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
2719 one_id,3524
2720 .OpFSub,3525 const rem_is_not_zero = try self.buildCmp(.i_ne, rem, zero);
2721 .OpISub,3526
2722 .OpISub,3527 const result_negative = try self.buildCmp(
3528 .l_ne,
3529 try self.buildCmp(.s_lt, lhs, zero),
3530 try self.buildCmp(.s_lt, rhs, zero),
3531 );
3532 const rem_is_not_zero_and_result_is_negative = try self.buildBinary(
3533 .l_and,
3534 rem_is_not_zero,
3535 result_negative,
2723 );3536 );
2724 const negative_div_id = try self.arithOp(ty, negative_div_lhs, rhs_abs, .OpFDiv, .OpSDiv, .OpUDiv);
2725 const negated_negative_div_id = self.spv.allocId();
2726 try self.func.body.emit(self.spv.gpa, .OpSNegate, .{
2727 .id_result_type = ty_id,
2728 .id_result = negated_negative_div_id,
2729 .operand = negative_div_id,
2730 });
27313537
2732 const result_id = self.spv.allocId();3538 const result = try self.buildBinary(
2733 try self.func.body.emit(self.spv.gpa, .OpSelect, .{3539 .i_sub,
2734 .id_result_type = ty_id,3540 div,
2735 .id_result = result_id,3541 try self.intFromBool2(rem_is_not_zero_and_result_is_negative, div.ty),
2736 .condition = is_positive_id,3542 );
2737 .object_1 = positive_div_id,3543
2738 .object_2 = negated_negative_div_id,3544 return try result.materialize(self);
2739 });
2740 return result_id;
2741 },3545 },
2742 .float => {3546 .float => {
2743 const div_id = try self.arithOp(ty, lhs_id, rhs_id, .OpFDiv, .OpSDiv, .OpUDiv);3547 const div = try self.buildBinary(.f_div, lhs, rhs);
2744 return try self.floor(ty, div_id);3548 const result = try self.buildUnary(.floor, div);
3549 return try result.materialize(self);
2745 },3550 },
2746 .bool => unreachable,3551 .bool => unreachable,
2747 }3552 }
2748 }3553 }
27493554
2750 fn airFloor(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3555 fn airDivTrunc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2751 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;3556 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2752 const operand_id = try self.resolve(un_op);
2753 const result_ty = self.typeOfIndex(inst);
2754 return try self.floor(result_ty, operand_id);
2755 }
27563557
2757 fn floor(self: *DeclGen, ty: Type, operand_id: IdRef) !IdRef {3558 const lhs = try self.temporary(bin_op.lhs);
2758 const target = self.getTarget();3559 const rhs = try self.temporary(bin_op.rhs);
2759 const ty_id = try self.resolveType(ty, .direct);
2760 const ext_inst: Word = switch (target.os.tag) {
2761 .opencl => 25,
2762 .vulkan => 8,
2763 else => unreachable,
2764 };
2765 const set_id = switch (target.os.tag) {
2766 .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"),
2767 .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"),
2768 else => unreachable,
2769 };
27703560
2771 const result_id = self.spv.allocId();3561 const info = self.arithmeticTypeInfo(lhs.ty);
2772 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{3562 switch (info.class) {
2773 .id_result_type = ty_id,3563 .composite_integer => unreachable, // TODO
2774 .id_result = result_id,3564 .integer, .strange_integer => switch (info.signedness) {
2775 .set = set_id,3565 .unsigned => {
2776 .instruction = .{ .inst = ext_inst },3566 const result = try self.buildBinary(.u_div, lhs, rhs);
2777 .id_ref_4 = &.{operand_id},3567 return try result.materialize(self);
2778 });3568 },
2779 return result_id;3569 .signed => {
3570 const result = try self.buildBinary(.s_div, lhs, rhs);
3571 return try result.materialize(self);
3572 },
3573 },
3574 .float => {
3575 const div = try self.buildBinary(.f_div, lhs, rhs);
3576 const result = try self.buildUnary(.trunc, div);
3577 return try result.materialize(self);
3578 },
3579 .bool => unreachable,
3580 }
3581 }
3582
3583 fn airUnOpSimple(self: *DeclGen, inst: Air.Inst.Index, op: UnaryOp) !?IdRef {
3584 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3585 const operand = try self.temporary(un_op);
3586 const result = try self.buildUnary(op, operand);
3587 return try result.materialize(self);
2780 }3588 }
27813589
2782 fn airArithOp(3590 fn airArithOp(
2783 self: *DeclGen,3591 self: *DeclGen,
2784 inst: Air.Inst.Index,3592 inst: Air.Inst.Index,
2785 comptime fop: Opcode,3593 comptime fop: BinaryOp,
2786 comptime sop: Opcode,3594 comptime sop: BinaryOp,
2787 comptime uop: Opcode,3595 comptime uop: BinaryOp,
2788 ) !?IdRef {3596 ) !?IdRef {
2789 // LHS and RHS are guaranteed to have the same type, and AIR guarantees
2790 // the result to be the same as the LHS and RHS, which matches SPIR-V.
2791 const ty = self.typeOfIndex(inst);
2792 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;3597 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
2793 const lhs_id = try self.resolve(bin_op.lhs);
2794 const rhs_id = try self.resolve(bin_op.rhs);
27953598
2796 assert(self.typeOf(bin_op.lhs).eql(ty, self.module));3599 const lhs = try self.temporary(bin_op.lhs);
2797 assert(self.typeOf(bin_op.rhs).eql(ty, self.module));3600 const rhs = try self.temporary(bin_op.rhs);
27983601
2799 return try self.arithOp(ty, lhs_id, rhs_id, fop, sop, uop);3602 const info = self.arithmeticTypeInfo(lhs.ty);
2800 }
28013603
2802 fn arithOp(3604 const result = switch (info.class) {
2803 self: *DeclGen,3605 .composite_integer => unreachable, // TODO
2804 ty: Type,
2805 lhs_id: IdRef,
2806 rhs_id: IdRef,
2807 comptime fop: Opcode,
2808 comptime sop: Opcode,
2809 comptime uop: Opcode,
2810 ) !IdRef {
2811 // Binary operations are generally applicable to both scalar and vector operations
2812 // in SPIR-V, but int and float versions of operations require different opcodes.
2813 const info = self.arithmeticTypeInfo(ty);
2814
2815 const opcode_index: usize = switch (info.class) {
2816 .composite_integer => {
2817 return self.todo("binary operations for composite integers", .{});
2818 },
2819 .integer, .strange_integer => switch (info.signedness) {3606 .integer, .strange_integer => switch (info.signedness) {
2820 .signed => 1,3607 .signed => try self.buildBinary(sop, lhs, rhs),
2821 .unsigned => 2,3608 .unsigned => try self.buildBinary(uop, lhs, rhs),
2822 },3609 },
2823 .float => 0,3610 .float => try self.buildBinary(fop, lhs, rhs),
2824 .bool => unreachable,3611 .bool => unreachable,
2825 };3612 };
28263613
2827 var wip = try self.elementWise(ty, false);3614 return try result.materialize(self);
2828 defer wip.deinit();
2829 for (wip.results, 0..) |*result_id, i| {
2830 const lhs_elem_id = try wip.elementAt(ty, lhs_id, i);
2831 const rhs_elem_id = try wip.elementAt(ty, rhs_id, i);
2832
2833 const value_id = self.spv.allocId();
2834 const operands = .{
2835 .id_result_type = wip.ty_id,
2836 .id_result = value_id,
2837 .operand_1 = lhs_elem_id,
2838 .operand_2 = rhs_elem_id,
2839 };
2840
2841 switch (opcode_index) {
2842 0 => try self.func.body.emit(self.spv.gpa, fop, operands),
2843 1 => try self.func.body.emit(self.spv.gpa, sop, operands),
2844 2 => try self.func.body.emit(self.spv.gpa, uop, operands),
2845 else => unreachable,
2846 }
2847
2848 // TODO: Trap on overflow? Probably going to be annoying.
2849 // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap.
2850 result_id.* = try self.normalize(wip.ty, value_id, info);
2851 }
2852
2853 return try wip.finalize();
2854 }3615 }
28553616
2856 fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3617 fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2857 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3618 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
2858 const operand_id = try self.resolve(ty_op.operand);3619 const operand = try self.temporary(ty_op.operand);
2859 // Note: operand_ty may be signed, while ty is always unsigned!3620 // Note: operand_ty may be signed, while ty is always unsigned!
2860 const operand_ty = self.typeOf(ty_op.operand);
2861 const result_ty = self.typeOfIndex(inst);3621 const result_ty = self.typeOfIndex(inst);
2862 return try self.abs(result_ty, operand_ty, operand_id);3622 const result = try self.abs(result_ty, operand);
3623 return try result.materialize(self);
2863 }3624 }
28643625
2865 fn abs(self: *DeclGen, result_ty: Type, operand_ty: Type, operand_id: IdRef) !IdRef {3626 fn abs(self: *DeclGen, result_ty: Type, value: Temporary) !Temporary {
2866 const target = self.getTarget();3627 const target = self.getTarget();
2867 const operand_info = self.arithmeticTypeInfo(operand_ty);3628 const operand_info = self.arithmeticTypeInfo(value.ty);
2868
2869 var wip = try self.elementWise(result_ty, false);
2870 defer wip.deinit();
28713629
2872 for (wip.results, 0..) |*result_id, i| {3630 switch (operand_info.class) {
2873 const elem_id = try wip.elementAt(operand_ty, operand_id, i);3631 .float => return try self.buildUnary(.f_abs, value),
28743632 .integer, .strange_integer => {
2875 const ext_inst: Word = switch (target.os.tag) {3633 const abs_value = try self.buildUnary(.i_abs, value);
2876 .opencl => switch (operand_info.class) {
2877 .float => 23, // fabs
2878 .integer, .strange_integer => switch (operand_info.signedness) {
2879 .signed => 141, // s_abs
2880 .unsigned => 201, // u_abs
2881 },
2882 .composite_integer => unreachable, // TODO
2883 .bool => unreachable,
2884 },
2885 .vulkan => switch (operand_info.class) {
2886 .float => 4, // FAbs
2887 .integer, .strange_integer => 5, // SAbs
2888 .composite_integer => unreachable, // TODO
2889 .bool => unreachable,
2890 },
2891 else => unreachable,
2892 };
2893 const set_id = switch (target.os.tag) {
2894 .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"),
2895 .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"),
2896 else => unreachable,
2897 };
28983634
2899 result_id.* = self.spv.allocId();3635 // TODO: We may need to bitcast the result to a uint
2900 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{3636 // depending on the result type. Do that when
2901 .id_result_type = wip.ty_id,3637 // bitCast is implemented for vectors.
2902 .id_result = result_id.*,3638 // This is only relevant for Vulkan
2903 .set = set_id,3639 assert(target.os.tag != .vulkan); // TODO
2904 .instruction = .{ .inst = ext_inst },3640
2905 .id_ref_4 = &.{elem_id},3641 return try self.normalize(abs_value, self.arithmeticTypeInfo(result_ty));
2906 });3642 },
3643 .composite_integer => unreachable, // TODO
3644 .bool => unreachable,
2907 }3645 }
2908 return try wip.finalize();
2909 }3646 }
29103647
2911 fn airAddSubOverflow(3648 fn airAddSubOverflow(
2912 self: *DeclGen,3649 self: *DeclGen,
2913 inst: Air.Inst.Index,3650 inst: Air.Inst.Index,
2914 comptime add: Opcode,3651 comptime add: BinaryOp,
2915 comptime ucmp: Opcode,3652 comptime ucmp: CmpPredicate,
2916 comptime scmp: Opcode,3653 comptime scmp: CmpPredicate,
2917 ) !?IdRef {3654 ) !?IdRef {
2918 const mod = self.module;3655 // Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers,
3656 // there is in both cases only one extra operation required. For signed operations,
3657 // the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually
3658 // normally set a carry bit. So the SPIR-V overflow operations are not particularly
3659 // useful here.
3660
2919 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3661 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
2920 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3662 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2921 const lhs = try self.resolve(extra.lhs);
2922 const rhs = try self.resolve(extra.rhs);
29233663
2924 const result_ty = self.typeOfIndex(inst);3664 const lhs = try self.temporary(extra.lhs);
2925 const operand_ty = self.typeOf(extra.lhs);3665 const rhs = try self.temporary(extra.rhs);
2926 const ov_ty = result_ty.structFieldType(1, self.module);
29273666
2928 const bool_ty_id = try self.resolveType(Type.bool, .direct);3667 const result_ty = self.typeOfIndex(inst);
2929 const cmp_ty_id = if (self.isVector(operand_ty))
2930 // TODO: Resolving a vector type with .direct should return a SPIR-V vector
2931 try self.spv.vectorType(operand_ty.vectorLen(mod), try self.resolveType(Type.bool, .direct))
2932 else
2933 bool_ty_id;
29343668
2935 const info = self.arithmeticTypeInfo(operand_ty);3669 const info = self.arithmeticTypeInfo(lhs.ty);
2936 switch (info.class) {3670 switch (info.class) {
2937 .composite_integer => return self.todo("overflow ops for composite integers", .{}),3671 .composite_integer => unreachable, // TODO
2938 .strange_integer, .integer => {},3672 .strange_integer, .integer => {},
2939 .float, .bool => unreachable,3673 .float, .bool => unreachable,
2940 }3674 }
29413675
2942 var wip_result = try self.elementWise(operand_ty, false);3676 const sum = try self.buildBinary(add, lhs, rhs);
2943 defer wip_result.deinit();3677 const result = try self.normalize(sum, info);
2944 var wip_ov = try self.elementWise(ov_ty, false);3678
2945 defer wip_ov.deinit();3679 const overflowed = switch (info.signedness) {
2946 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| {3680 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.
2947 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i);3681 // For subtraction the conditions need to be swapped.
2948 const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i);3682 .unsigned => try self.buildCmp(ucmp, result, lhs),
29493683 // For addition, overflow happened if:
2950 // Normalize both so that we can properly check for overflow3684 // - rhs is negative and value > lhs
2951 const value_id = self.spv.allocId();3685 // - rhs is positive and value < lhs
29523686 // This can be shortened to:
2953 try self.func.body.emit(self.spv.gpa, add, .{3687 // (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs)
2954 .id_result_type = wip_result.ty_id,3688 // = (rhs < 0) == (value > lhs)
2955 .id_result = value_id,3689 // = (rhs < 0) == (lhs < value)
2956 .operand_1 = lhs_elem_id,3690 // Note that signed overflow is also wrapping in spir-v.
2957 .operand_2 = rhs_elem_id,3691 // For subtraction, overflow happened if:
2958 });3692 // - rhs is negative and value < lhs
29593693 // - rhs is positive and value > lhs
2960 // Normalize the result so that the comparisons go well3694 // This can be shortened to:
2961 result_id.* = try self.normalize(wip_result.ty, value_id, info);3695 // (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs)
29623696 // = (rhs < 0) == (value < lhs)
2963 const overflowed_id = switch (info.signedness) {3697 // = (rhs < 0) == (lhs > value)
2964 .unsigned => blk: {3698 .signed => blk: {
2965 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.3699 const zero = Temporary.init(rhs.ty, try self.constInt(rhs.ty, 0, .direct));
2966 // For subtraction the conditions need to be swapped.3700 const rhs_lt_zero = try self.buildCmp(.s_lt, rhs, zero);
2967 const overflowed_id = self.spv.allocId();3701 const result_gt_lhs = try self.buildCmp(scmp, lhs, result);
2968 try self.func.body.emit(self.spv.gpa, ucmp, .{3702 break :blk try self.buildCmp(.l_eq, rhs_lt_zero, result_gt_lhs);
2969 .id_result_type = cmp_ty_id,3703 },
2970 .id_result = overflowed_id,3704 };
2971 .operand_1 = result_id.*,
2972 .operand_2 = lhs_elem_id,
2973 });
2974 break :blk overflowed_id;
2975 },
2976 .signed => blk: {
2977 // lhs - rhs
2978 // For addition, overflow happened if:
2979 // - rhs is negative and value > lhs
2980 // - rhs is positive and value < lhs
2981 // This can be shortened to:
2982 // (rhs < 0 and value > lhs) or (rhs >= 0 and value <= lhs)
2983 // = (rhs < 0) == (value > lhs)
2984 // = (rhs < 0) == (lhs < value)
2985 // Note that signed overflow is also wrapping in spir-v.
2986 // For subtraction, overflow happened if:
2987 // - rhs is negative and value < lhs
2988 // - rhs is positive and value > lhs
2989 // This can be shortened to:
2990 // (rhs < 0 and value < lhs) or (rhs >= 0 and value >= lhs)
2991 // = (rhs < 0) == (value < lhs)
2992 // = (rhs < 0) == (lhs > value)
2993
2994 const rhs_lt_zero_id = self.spv.allocId();
2995 const zero_id = try self.constInt(wip_result.ty, 0, .direct);
2996 try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{
2997 .id_result_type = cmp_ty_id,
2998 .id_result = rhs_lt_zero_id,
2999 .operand_1 = rhs_elem_id,
3000 .operand_2 = zero_id,
3001 });
3002
3003 const value_gt_lhs_id = self.spv.allocId();
3004 try self.func.body.emit(self.spv.gpa, scmp, .{
3005 .id_result_type = cmp_ty_id,
3006 .id_result = value_gt_lhs_id,
3007 .operand_1 = lhs_elem_id,
3008 .operand_2 = result_id.*,
3009 });
3010
3011 const overflowed_id = self.spv.allocId();
3012 try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{
3013 .id_result_type = cmp_ty_id,
3014 .id_result = overflowed_id,
3015 .operand_1 = rhs_lt_zero_id,
3016 .operand_2 = value_gt_lhs_id,
3017 });
3018 break :blk overflowed_id;
3019 },
3020 };
30213705
3022 ov_id.* = try self.intFromBool(wip_ov.ty, overflowed_id);3706 const ov = try self.intFromBool(overflowed);
3023 }
30243707
3025 return try self.constructStruct(3708 return try self.constructStruct(
3026 result_ty,3709 result_ty,
3027 &.{ operand_ty, ov_ty },3710 &.{ result.ty, ov.ty },
3028 &.{ try wip_result.finalize(), try wip_ov.finalize() },3711 &.{ try result.materialize(self), try ov.materialize(self) },
3029 );3712 );
3030 }3713 }
30313714
3032 fn airMulOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3715 fn airMulOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3716 const target = self.getTarget();
3717 const mod = self.module;
3718
3033 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3719 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3034 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3720 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3035 const lhs = try self.resolve(extra.lhs);3721
3036 const rhs = try self.resolve(extra.rhs);3722 const lhs = try self.temporary(extra.lhs);
3723 const rhs = try self.temporary(extra.rhs);
30373724
3038 const result_ty = self.typeOfIndex(inst);3725 const result_ty = self.typeOfIndex(inst);
3039 const operand_ty = self.typeOf(extra.lhs);
3040 const ov_ty = result_ty.structFieldType(1, self.module);
30413726
3042 const info = self.arithmeticTypeInfo(operand_ty);3727 const info = self.arithmeticTypeInfo(lhs.ty);
3043 switch (info.class) {3728 switch (info.class) {
3044 .composite_integer => return self.todo("overflow ops for composite integers", .{}),3729 .composite_integer => unreachable, // TODO
3045 .strange_integer, .integer => {},3730 .strange_integer, .integer => {},
3046 .float, .bool => unreachable,3731 .float, .bool => unreachable,
3047 }3732 }
30483733
3049 var wip_result = try self.elementWise(operand_ty, true);3734 // There are 3 cases which we have to deal with:
3050 defer wip_result.deinit();3735 // - If info.bits < 32 / 2, we will upcast to 32 and check the higher bits
3051 var wip_ov = try self.elementWise(ov_ty, true);3736 // - If info.bits > 32 / 2, we have to use extended multiplication
3052 defer wip_ov.deinit();3737 // - Additionally, if info.bits != 32, we'll have to check the high bits
3738 // of the result too.
3739
3740 const largest_int_bits: u16 = if (Target.spirv.featureSetHas(target.cpu.features, .Int64)) 64 else 32;
3741 // If non-null, the number of bits that the multiplication should be performed in. If
3742 // null, we have to use wide multiplication.
3743 const maybe_op_ty_bits: ?u16 = switch (info.bits) {
3744 0 => unreachable,
3745 1...16 => 32,
3746 17...32 => if (largest_int_bits > 32) 64 else null, // Upcast if we can.
3747 33...64 => null, // Always use wide multiplication.
3748 else => unreachable, // TODO: Composite integers
3749 };
30533750
3054 const zero_id = try self.constInt(wip_result.ty, 0, .direct);3751 const result, const overflowed = switch (info.signedness) {
3055 const zero_ov_id = try self.constInt(wip_ov.ty, 0, .direct);3752 .unsigned => blk: {
3056 const one_ov_id = try self.constInt(wip_ov.ty, 1, .direct);3753 if (maybe_op_ty_bits) |op_ty_bits| {
3754 const op_ty = try mod.intType(.unsigned, op_ty_bits);
3755 const casted_lhs = try self.buildIntConvert(op_ty, lhs);
3756 const casted_rhs = try self.buildIntConvert(op_ty, rhs);
30573757
3058 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| {3758 const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs);
3059 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i);
3060 const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i);
30613759
3062 result_id.* = try self.arithOp(wip_result.ty, lhs_elem_id, rhs_elem_id, .OpFMul, .OpIMul, .OpIMul);3760 const low_bits = try self.buildIntConvert(lhs.ty, full_result);
3761 const result = try self.normalize(low_bits, info);
30633762
3064 // (a != 0) and (x / a != b)3763 // Shift the result bits away to get the overflow bits.
3065 const not_zero_id = try self.cmp(.neq, Type.bool, wip_result.ty, lhs_elem_id, zero_id);3764 const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits, .direct));
3066 const res_rhs_id = try self.arithOp(wip_result.ty, result_id.*, lhs_elem_id, .OpFDiv, .OpSDiv, .OpUDiv);3765 const overflow = try self.buildBinary(.srl, full_result, shift);
3067 const res_rhs_not_rhs_id = try self.cmp(.neq, Type.bool, wip_result.ty, res_rhs_id, rhs_elem_id);
3068 const cond_id = try self.binOpSimple(Type.bool, not_zero_id, res_rhs_not_rhs_id, .OpLogicalAnd);
30693766
3070 ov_id.* = self.spv.allocId();3767 // Directly check if its zero in the op_ty without converting first.
3071 try self.func.body.emit(self.spv.gpa, .OpSelect, .{3768 const zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0, .direct));
3072 .id_result_type = wip_ov.ty_id,3769 const overflowed = try self.buildCmp(.i_ne, zero, overflow);
3073 .id_result = ov_id.*,3770
3074 .condition = cond_id,3771 break :blk .{ result, overflowed };
3075 .object_1 = one_ov_id,3772 }
3076 .object_2 = zero_ov_id,3773
3077 });3774 const low_bits, const high_bits = try self.buildWideMul(.u_mul_extended, lhs, rhs);
3078 }3775
3776 // Truncate the result, if required.
3777 const result = try self.normalize(low_bits, info);
3778
3779 // Overflow happened if the high-bits of the result are non-zero OR if the
3780 // high bits of the low word of the result (those outside the range of the
3781 // int) are nonzero.
3782 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
3783 const high_overflowed = try self.buildCmp(.i_ne, zero, high_bits);
3784
3785 // If no overflow bits in low_bits, no extra work needs to be done.
3786 if (info.backing_bits == info.bits) {
3787 break :blk .{ result, high_overflowed };
3788 }
3789
3790 // Shift the result bits away to get the overflow bits.
3791 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits, .direct));
3792 const low_overflow = try self.buildBinary(.srl, low_bits, shift);
3793 const low_overflowed = try self.buildCmp(.i_ne, zero, low_overflow);
3794
3795 const overflowed = try self.buildBinary(.l_or, low_overflowed, high_overflowed);
3796
3797 break :blk .{ result, overflowed };
3798 },
3799 .signed => blk: {
3800 // - lhs >= 0, rhxs >= 0: expect positive; overflow should be 0
3801 // - lhs == 0 : expect positive; overflow should be 0
3802 // - rhs == 0: expect positive; overflow should be 0
3803 // - lhs > 0, rhs < 0: expect negative; overflow should be -1
3804 // - lhs < 0, rhs > 0: expect negative; overflow should be -1
3805 // - lhs <= 0, rhs <= 0: expect positive; overflow should be 0
3806 // ------
3807 // overflow should be -1 when
3808 // (lhs > 0 && rhs < 0) || (lhs < 0 && rhs > 0)
3809
3810 const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
3811 const lhs_negative = try self.buildCmp(.s_lt, lhs, zero);
3812 const rhs_negative = try self.buildCmp(.s_lt, rhs, zero);
3813 const lhs_positive = try self.buildCmp(.s_gt, lhs, zero);
3814 const rhs_positive = try self.buildCmp(.s_gt, rhs, zero);
3815
3816 // Set to `true` if we expect -1.
3817 const expected_overflow_bit = try self.buildBinary(
3818 .l_or,
3819 try self.buildBinary(.l_and, lhs_positive, rhs_negative),
3820 try self.buildBinary(.l_and, lhs_negative, rhs_positive),
3821 );
3822
3823 if (maybe_op_ty_bits) |op_ty_bits| {
3824 const op_ty = try mod.intType(.signed, op_ty_bits);
3825 // Assume normalized; sign bit is set. We want a sign extend.
3826 const casted_lhs = try self.buildIntConvert(op_ty, lhs);
3827 const casted_rhs = try self.buildIntConvert(op_ty, rhs);
3828
3829 const full_result = try self.buildBinary(.i_mul, casted_lhs, casted_rhs);
3830
3831 // Truncate to the result type.
3832 const low_bits = try self.buildIntConvert(lhs.ty, full_result);
3833 const result = try self.normalize(low_bits, info);
3834
3835 // Now, we need to check the overflow bits AND the sign
3836 // bit for the expceted overflow bits.
3837 // To do that, shift out everything bit the sign bit and
3838 // then check what remains.
3839 const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits - 1, .direct));
3840 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
3841 // for negative cases.
3842 const overflow = try self.buildBinary(.sra, full_result, shift);
3843
3844 const long_all_set = Temporary.init(full_result.ty, try self.constInt(full_result.ty, -1, .direct));
3845 const long_zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0, .direct));
3846 const mask = try self.buildSelect(expected_overflow_bit, long_all_set, long_zero);
3847
3848 const overflowed = try self.buildCmp(.i_ne, mask, overflow);
3849
3850 break :blk .{ result, overflowed };
3851 }
3852
3853 const low_bits, const high_bits = try self.buildWideMul(.s_mul_extended, lhs, rhs);
3854
3855 // Truncate result if required.
3856 const result = try self.normalize(low_bits, info);
3857
3858 const all_set = Temporary.init(lhs.ty, try self.constInt(lhs.ty, -1, .direct));
3859 const mask = try self.buildSelect(expected_overflow_bit, all_set, zero);
3860
3861 // Like with unsigned, overflow happened if high_bits are not the ones we expect,
3862 // and we also need to check some ones from the low bits.
3863
3864 const high_overflowed = try self.buildCmp(.i_ne, mask, high_bits);
3865
3866 // If no overflow bits in low_bits, no extra work needs to be done.
3867 // Careful, we still have to check the sign bit, so this branch
3868 // only goes for i33 and such.
3869 if (info.backing_bits == info.bits + 1) {
3870 break :blk .{ result, high_overflowed };
3871 }
3872
3873 // Shift the result bits away to get the overflow bits.
3874 const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1, .direct));
3875 // Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
3876 // for negative cases.
3877 const low_overflow = try self.buildBinary(.sra, low_bits, shift);
3878 const low_overflowed = try self.buildCmp(.i_ne, mask, low_overflow);
3879
3880 const overflowed = try self.buildBinary(.l_or, low_overflowed, high_overflowed);
3881
3882 break :blk .{ result, overflowed };
3883 },
3884 };
3885
3886 const ov = try self.intFromBool(overflowed);
30793887
3080 return try self.constructStruct(3888 return try self.constructStruct(
3081 result_ty,3889 result_ty,
3082 &.{ operand_ty, ov_ty },3890 &.{ result.ty, ov.ty },
3083 &.{ try wip_result.finalize(), try wip_ov.finalize() },3891 &.{ try result.materialize(self), try ov.materialize(self) },
3084 );3892 );
3085 }3893 }
30863894
3087 fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3895 fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3088 const mod = self.module;3896 const mod = self.module;
3897
3089 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3898 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3090 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;3899 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3091 const lhs = try self.resolve(extra.lhs);
3092 const rhs = try self.resolve(extra.rhs);
30933900
3094 const result_ty = self.typeOfIndex(inst);3901 const base = try self.temporary(extra.lhs);
3095 const operand_ty = self.typeOf(extra.lhs);3902 const shift = try self.temporary(extra.rhs);
3096 const shift_ty = self.typeOf(extra.rhs);
3097 const scalar_shift_ty_id = try self.resolveType(shift_ty.scalarType(mod), .direct);
3098 const scalar_operand_ty_id = try self.resolveType(operand_ty.scalarType(mod), .direct);
3099
3100 const ov_ty = result_ty.structFieldType(1, self.module);
31013903
3102 const bool_ty_id = try self.resolveType(Type.bool, .direct);3904 const result_ty = self.typeOfIndex(inst);
3103 const cmp_ty_id = if (self.isVector(operand_ty))
3104 // TODO: Resolving a vector type with .direct should return a SPIR-V vector
3105 try self.spv.vectorType(operand_ty.vectorLen(mod), try self.resolveType(Type.bool, .direct))
3106 else
3107 bool_ty_id;
31083905
3109 const info = self.arithmeticTypeInfo(operand_ty);3906 const info = self.arithmeticTypeInfo(base.ty);
3110 switch (info.class) {3907 switch (info.class) {
3111 .composite_integer => return self.todo("overflow shift for composite integers", .{}),3908 .composite_integer => unreachable, // TODO
3112 .integer, .strange_integer => {},3909 .integer, .strange_integer => {},
3113 .float, .bool => unreachable,3910 .float, .bool => unreachable,
3114 }3911 }
31153912
3116 var wip_result = try self.elementWise(operand_ty, false);3913 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
3117 defer wip_result.deinit();3914 // so just manually upcast it if required.
3118 var wip_ov = try self.elementWise(ov_ty, false);3915 const casted_shift = try self.buildIntConvert(base.ty.scalarType(mod), shift);
3119 defer wip_ov.deinit();
3120 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| {
3121 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i);
3122 const rhs_elem_id = try wip_result.elementAt(shift_ty, rhs, i);
3123
3124 // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that,
3125 // so just manually upcast it if required.
3126 const shift_id = if (scalar_shift_ty_id != scalar_operand_ty_id) blk: {
3127 const shift_id = self.spv.allocId();
3128 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
3129 .id_result_type = wip_result.ty_id,
3130 .id_result = shift_id,
3131 .unsigned_value = rhs_elem_id,
3132 });
3133 break :blk shift_id;
3134 } else rhs_elem_id;
3135
3136 const value_id = self.spv.allocId();
3137 try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{
3138 .id_result_type = wip_result.ty_id,
3139 .id_result = value_id,
3140 .base = lhs_elem_id,
3141 .shift = shift_id,
3142 });
3143 result_id.* = try self.normalize(wip_result.ty, value_id, info);
31443916
3145 const right_shift_id = self.spv.allocId();3917 const left = try self.buildBinary(.sll, base, casted_shift);
3146 switch (info.signedness) {3918 const result = try self.normalize(left, info);
3147 .signed => {
3148 try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{
3149 .id_result_type = wip_result.ty_id,
3150 .id_result = right_shift_id,
3151 .base = result_id.*,
3152 .shift = shift_id,
3153 });
3154 },
3155 .unsigned => {
3156 try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{
3157 .id_result_type = wip_result.ty_id,
3158 .id_result = right_shift_id,
3159 .base = result_id.*,
3160 .shift = shift_id,
3161 });
3162 },
3163 }
31643919
3165 const overflowed_id = self.spv.allocId();3920 const right = switch (info.signedness) {
3166 try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{3921 .unsigned => try self.buildBinary(.srl, result, casted_shift),
3167 .id_result_type = cmp_ty_id,3922 .signed => try self.buildBinary(.sra, result, casted_shift),
3168 .id_result = overflowed_id,3923 };
3169 .operand_1 = lhs_elem_id,
3170 .operand_2 = right_shift_id,
3171 });
31723924
3173 ov_id.* = try self.intFromBool(wip_ov.ty, overflowed_id);3925 const overflowed = try self.buildCmp(.i_ne, base, right);
3174 }3926 const ov = try self.intFromBool(overflowed);
31753927
3176 return try self.constructStruct(3928 return try self.constructStruct(
3177 result_ty,3929 result_ty,
3178 &.{ operand_ty, ov_ty },3930 &.{ result.ty, ov.ty },
3179 &.{ try wip_result.finalize(), try wip_ov.finalize() },3931 &.{ try result.materialize(self), try ov.materialize(self) },
3180 );3932 );
3181 }3933 }
31823934
...@@ -3184,122 +3936,67 @@ const DeclGen = struct {...@@ -3184,122 +3936,67 @@ const DeclGen = struct {
3184 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;3936 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
3185 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;3937 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
31863938
3187 const mulend1 = try self.resolve(extra.lhs);3939 const a = try self.temporary(extra.lhs);
3188 const mulend2 = try self.resolve(extra.rhs);3940 const b = try self.temporary(extra.rhs);
3189 const addend = try self.resolve(pl_op.operand);3941 const c = try self.temporary(pl_op.operand);
31903942
3191 const ty = self.typeOfIndex(inst);3943 const result_ty = self.typeOfIndex(inst);
31923944 const info = self.arithmeticTypeInfo(result_ty);
3193 const info = self.arithmeticTypeInfo(ty);
3194 assert(info.class == .float); // .mul_add is only emitted for floats3945 assert(info.class == .float); // .mul_add is only emitted for floats
31953946
3196 var wip = try self.elementWise(ty, false);3947 const result = try self.buildFma(a, b, c);
3197 defer wip.deinit();3948 return try result.materialize(self);
3198 for (0..wip.results.len) |i| {
3199 const mul_result = self.spv.allocId();
3200 try self.func.body.emit(self.spv.gpa, .OpFMul, .{
3201 .id_result_type = wip.ty_id,
3202 .id_result = mul_result,
3203 .operand_1 = try wip.elementAt(ty, mulend1, i),
3204 .operand_2 = try wip.elementAt(ty, mulend2, i),
3205 });
3206
3207 try self.func.body.emit(self.spv.gpa, .OpFAdd, .{
3208 .id_result_type = wip.ty_id,
3209 .id_result = wip.allocId(i),
3210 .operand_1 = mul_result,
3211 .operand_2 = try wip.elementAt(ty, addend, i),
3212 });
3213 }
3214 return try wip.finalize();
3215 }3949 }
32163950
3217 fn airClzCtz(self: *DeclGen, inst: Air.Inst.Index, op: enum { clz, ctz }) !?IdRef {3951 fn airClzCtz(self: *DeclGen, inst: Air.Inst.Index, op: UnaryOp) !?IdRef {
3218 if (self.liveness.isUnused(inst)) return null;3952 if (self.liveness.isUnused(inst)) return null;
32193953
3220 const mod = self.module;3954 const mod = self.module;
3221 const target = self.getTarget();3955 const target = self.getTarget();
3222 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3956 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3223 const result_ty = self.typeOfIndex(inst);3957 const operand = try self.temporary(ty_op.operand);
3224 const operand_ty = self.typeOf(ty_op.operand);
3225 const operand = try self.resolve(ty_op.operand);
32263958
3227 const info = self.arithmeticTypeInfo(operand_ty);3959 const scalar_result_ty = self.typeOfIndex(inst).scalarType(mod);
3960
3961 const info = self.arithmeticTypeInfo(operand.ty);
3228 switch (info.class) {3962 switch (info.class) {
3229 .composite_integer => unreachable, // TODO3963 .composite_integer => unreachable, // TODO
3230 .integer, .strange_integer => {},3964 .integer, .strange_integer => {},
3231 .float, .bool => unreachable,3965 .float, .bool => unreachable,
3232 }3966 }
32333967
3234 var wip = try self.elementWise(result_ty, false);3968 switch (target.os.tag) {
3235 defer wip.deinit();3969 .vulkan => unreachable, // TODO
32363970 else => {},
3237 const elem_ty = if (wip.is_array) operand_ty.scalarType(mod) else operand_ty;3971 }
3238 const elem_ty_id = try self.resolveType(elem_ty, .direct);
3239
3240 for (wip.results, 0..) |*result_id, i| {
3241 const elem = try wip.elementAt(operand_ty, operand, i);
3242
3243 switch (target.os.tag) {
3244 .opencl => {
3245 const set = try self.spv.importInstructionSet(.@"OpenCL.std");
3246 const ext_inst: u32 = switch (op) {
3247 .clz => 151, // clz
3248 .ctz => 152, // ctz
3249 };
32503972
3251 // Note: result of OpenCL ctz/clz returns operand_ty, and we want result_ty.3973 const count = try self.buildUnary(op, operand);
3252 // result_ty is always large enough to hold the result, so we might have to down
3253 // cast it.
3254 const tmp = self.spv.allocId();
3255 try self.func.body.emit(self.spv.gpa, .OpExtInst, .{
3256 .id_result_type = elem_ty_id,
3257 .id_result = tmp,
3258 .set = set,
3259 .instruction = .{ .inst = ext_inst },
3260 .id_ref_4 = &.{elem},
3261 });
32623974
3263 // TODO: Comparison should be removed..3975 // Result of OpenCL ctz/clz returns operand.ty, and we want result_ty.
3264 // Its valid because SpvModule caches numeric types3976 // result_ty is always large enough to hold the result, so we might have to down
3265 if (wip.ty_id == elem_ty_id) {3977 // cast it.
3266 result_id.* = tmp;3978 const result = try self.buildIntConvert(scalar_result_ty, count);
3267 continue;3979 return try result.materialize(self);
3268 }3980 }
32693981
3270 result_id.* = self.spv.allocId();3982 fn airSelect(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3271 if (result_ty.scalarType(mod).isSignedInt(mod)) {3983 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
3272 assert(elem_ty.scalarType(mod).isSignedInt(mod));3984 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
3273 try self.func.body.emit(self.spv.gpa, .OpSConvert, .{3985 const pred = try self.temporary(pl_op.operand);
3274 .id_result_type = wip.ty_id,3986 const a = try self.temporary(extra.lhs);
3275 .id_result = result_id.*,3987 const b = try self.temporary(extra.rhs);
3276 .signed_value = tmp,
3277 });
3278 } else {
3279 assert(elem_ty.scalarType(mod).isUnsignedInt(mod));
3280 try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
3281 .id_result_type = wip.ty_id,
3282 .id_result = result_id.*,
3283 .unsigned_value = tmp,
3284 });
3285 }
3286 },
3287 .vulkan => unreachable, // TODO
3288 else => unreachable,
3289 }
3290 }
32913988
3292 return try wip.finalize();3989 const result = try self.buildSelect(pred, a, b);
3990 return try result.materialize(self);
3293 }3991 }
32943992
3295 fn airSplat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3993 fn airSplat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3296 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;3994 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3995
3297 const operand_id = try self.resolve(ty_op.operand);3996 const operand_id = try self.resolve(ty_op.operand);
3298 const result_ty = self.typeOfIndex(inst);3997 const result_ty = self.typeOfIndex(inst);
3299 var wip = try self.elementWise(result_ty, true);3998
3300 defer wip.deinit();3999 return try self.constructVectorSplat(result_ty, operand_id);
3301 @memset(wip.results, operand_id);
3302 return try wip.finalize();
3303 }4000 }
33044001
3305 fn airReduce(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4002 fn airReduce(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
...@@ -3312,23 +4009,33 @@ const DeclGen = struct {...@@ -3312,23 +4009,33 @@ const DeclGen = struct {
33124009
3313 const info = self.arithmeticTypeInfo(operand_ty);4010 const info = self.arithmeticTypeInfo(operand_ty);
33144011
3315 var result_id = try self.extractField(scalar_ty, operand, 0);
3316 const len = operand_ty.vectorLen(mod);4012 const len = operand_ty.vectorLen(mod);
33174013
4014 const first = try self.extractVectorComponent(scalar_ty, operand, 0);
4015
3318 switch (reduce.operation) {4016 switch (reduce.operation) {
3319 .Min, .Max => |op| {4017 .Min, .Max => |op| {
3320 const cmp_op: std.math.CompareOperator = if (op == .Max) .gt else .lt;4018 var result = Temporary.init(scalar_ty, first);
4019 const cmp_op: MinMax = switch (op) {
4020 .Max => .max,
4021 .Min => .min,
4022 else => unreachable,
4023 };
3321 for (1..len) |i| {4024 for (1..len) |i| {
3322 const lhs = result_id;4025 const lhs = result;
3323 const rhs = try self.extractField(scalar_ty, operand, @intCast(i));4026 const rhs_id = try self.extractVectorComponent(scalar_ty, operand, @intCast(i));
3324 result_id = try self.minMax(scalar_ty, cmp_op, lhs, rhs);4027 const rhs = Temporary.init(scalar_ty, rhs_id);
4028
4029 result = try self.minMax(lhs, rhs, cmp_op);
3325 }4030 }
33264031
3327 return result_id;4032 return try result.materialize(self);
3328 },4033 },
3329 else => {},4034 else => {},
3330 }4035 }
33314036
4037 var result_id = first;
4038
3332 const opcode: Opcode = switch (info.class) {4039 const opcode: Opcode = switch (info.class) {
3333 .bool => switch (reduce.operation) {4040 .bool => switch (reduce.operation) {
3334 .And => .OpLogicalAnd,4041 .And => .OpLogicalAnd,
...@@ -3354,7 +4061,7 @@ const DeclGen = struct {...@@ -3354,7 +4061,7 @@ const DeclGen = struct {
33544061
3355 for (1..len) |i| {4062 for (1..len) |i| {
3356 const lhs = result_id;4063 const lhs = result_id;
3357 const rhs = try self.extractField(scalar_ty, operand, @intCast(i));4064 const rhs = try self.extractVectorComponent(scalar_ty, operand, @intCast(i));
3358 result_id = self.spv.allocId();4065 result_id = self.spv.allocId();
33594066
3360 try self.func.body.emitRaw(self.spv.gpa, opcode, 4);4067 try self.func.body.emitRaw(self.spv.gpa, opcode, 4);
...@@ -3375,25 +4082,72 @@ const DeclGen = struct {...@@ -3375,25 +4082,72 @@ const DeclGen = struct {
3375 const b = try self.resolve(extra.b);4082 const b = try self.resolve(extra.b);
3376 const mask = Value.fromInterned(extra.mask);4083 const mask = Value.fromInterned(extra.mask);
33774084
3378 const ty = self.typeOfIndex(inst);4085 // Note: number of components in the result, a, and b may differ.
4086 const result_ty = self.typeOfIndex(inst);
4087 const a_ty = self.typeOf(extra.a);
4088 const b_ty = self.typeOf(extra.b);
4089
4090 const scalar_ty = result_ty.scalarType(mod);
4091 const scalar_ty_id = try self.resolveType(scalar_ty, .direct);
4092
4093 // If all of the types are SPIR-V vectors, we can use OpVectorShuffle.
4094 if (self.isSpvVector(result_ty) and self.isSpvVector(a_ty) and self.isSpvVector(b_ty)) {
4095 // The SPIR-V shuffle instruction is similar to the Air instruction, except that the elements are
4096 // numbered consecutively instead of using negatives.
4097
4098 const components = try self.gpa.alloc(Word, result_ty.vectorLen(mod));
4099 defer self.gpa.free(components);
4100
4101 const a_len = a_ty.vectorLen(mod);
4102
4103 for (components, 0..) |*component, i| {
4104 const elem = try mask.elemValue(mod, i);
4105 if (elem.isUndef(mod)) {
4106 // This is explicitly valid for OpVectorShuffle, it indicates undefined.
4107 component.* = 0xFFFF_FFFF;
4108 continue;
4109 }
33794110
3380 var wip = try self.elementWise(ty, true);4111 const index = elem.toSignedInt(mod);
3381 defer wip.deinit();4112 if (index >= 0) {
3382 for (wip.results, 0..) |*result_id, i| {4113 component.* = @intCast(index);
4114 } else {
4115 component.* = @intCast(~index + a_len);
4116 }
4117 }
4118
4119 const result_id = self.spv.allocId();
4120 try self.func.body.emit(self.spv.gpa, .OpVectorShuffle, .{
4121 .id_result_type = try self.resolveType(result_ty, .direct),
4122 .id_result = result_id,
4123 .vector_1 = a,
4124 .vector_2 = b,
4125 .components = components,
4126 });
4127 return result_id;
4128 }
4129
4130 // Fall back to manually extracting and inserting components.
4131
4132 const components = try self.gpa.alloc(IdRef, result_ty.vectorLen(mod));
4133 defer self.gpa.free(components);
4134
4135 for (components, 0..) |*id, i| {
3383 const elem = try mask.elemValue(mod, i);4136 const elem = try mask.elemValue(mod, i);
3384 if (elem.isUndef(mod)) {4137 if (elem.isUndef(mod)) {
3385 result_id.* = try self.spv.constUndef(wip.ty_id);4138 id.* = try self.spv.constUndef(scalar_ty_id);
3386 continue;4139 continue;
3387 }4140 }
33884141
3389 const index = elem.toSignedInt(mod);4142 const index = elem.toSignedInt(mod);
3390 if (index >= 0) {4143 if (index >= 0) {
3391 result_id.* = try self.extractField(wip.ty, a, @intCast(index));4144 id.* = try self.extractVectorComponent(scalar_ty, a, @intCast(index));
3392 } else {4145 } else {
3393 result_id.* = try self.extractField(wip.ty, b, @intCast(~index));4146 id.* = try self.extractVectorComponent(scalar_ty, b, @intCast(~index));
3394 }4147 }
3395 }4148 }
3396 return try wip.finalize();4149
4150 return try self.constructVector(result_ty, components);
3397 }4151 }
33984152
3399 fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef {4153 fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef {
...@@ -3512,50 +4266,66 @@ const DeclGen = struct {...@@ -3512,50 +4266,66 @@ const DeclGen = struct {
3512 fn cmp(4266 fn cmp(
3513 self: *DeclGen,4267 self: *DeclGen,
3514 op: std.math.CompareOperator,4268 op: std.math.CompareOperator,
3515 result_ty: Type,4269 lhs: Temporary,
3516 ty: Type,4270 rhs: Temporary,
3517 lhs_id: IdRef,4271 ) !Temporary {
3518 rhs_id: IdRef,
3519 ) !IdRef {
3520 const mod = self.module;4272 const mod = self.module;
3521 var cmp_lhs_id = lhs_id;4273 const scalar_ty = lhs.ty.scalarType(mod);
3522 var cmp_rhs_id = rhs_id;4274 const is_vector = lhs.ty.isVector(mod);
3523 const bool_ty_id = try self.resolveType(Type.bool, .direct);4275
3524 const op_ty = switch (ty.zigTypeTag(mod)) {4276 switch (scalar_ty.zigTypeTag(mod)) {
3525 .Int, .Bool, .Float => ty,4277 .Int, .Bool, .Float => {},
3526 .Enum => ty.intTagType(mod),4278 .Enum => {
3527 .ErrorSet => Type.u16,4279 assert(!is_vector);
3528 .Pointer => blk: {4280 const ty = lhs.ty.intTagType(mod);
4281 return try self.cmp(op, lhs.pun(ty), rhs.pun(ty));
4282 },
4283 .ErrorSet => {
4284 assert(!is_vector);
4285 return try self.cmp(op, lhs.pun(Type.u16), rhs.pun(Type.u16));
4286 },
4287 .Pointer => {
4288 assert(!is_vector);
3529 // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are4289 // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are
3530 // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using4290 // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using
3531 // OpConvertPtrToU...4291 // OpConvertPtrToU...
3532 cmp_lhs_id = self.spv.allocId();
3533 cmp_rhs_id = self.spv.allocId();
35344292
3535 const usize_ty_id = try self.resolveType(Type.usize, .direct);4293 const usize_ty_id = try self.resolveType(Type.usize, .direct);
35364294
4295 const lhs_int_id = self.spv.allocId();
3537 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{4296 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{
3538 .id_result_type = usize_ty_id,4297 .id_result_type = usize_ty_id,
3539 .id_result = cmp_lhs_id,4298 .id_result = lhs_int_id,
3540 .pointer = lhs_id,4299 .pointer = try lhs.materialize(self),
3541 });4300 });
35424301
4302 const rhs_int_id = self.spv.allocId();
3543 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{4303 try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{
3544 .id_result_type = usize_ty_id,4304 .id_result_type = usize_ty_id,
3545 .id_result = cmp_rhs_id,4305 .id_result = rhs_int_id,
3546 .pointer = rhs_id,4306 .pointer = try rhs.materialize(self),
3547 });4307 });
35484308
3549 break :blk Type.usize;4309 const lhs_int = Temporary.init(Type.usize, lhs_int_id);
4310 const rhs_int = Temporary.init(Type.usize, rhs_int_id);
4311 return try self.cmp(op, lhs_int, rhs_int);
3550 },4312 },
3551 .Optional => {4313 .Optional => {
4314 assert(!is_vector);
4315
4316 const ty = lhs.ty;
4317
3552 const payload_ty = ty.optionalChild(mod);4318 const payload_ty = ty.optionalChild(mod);
3553 if (ty.optionalReprIsPayload(mod)) {4319 if (ty.optionalReprIsPayload(mod)) {
3554 assert(payload_ty.hasRuntimeBitsIgnoreComptime(mod));4320 assert(payload_ty.hasRuntimeBitsIgnoreComptime(mod));
3555 assert(!payload_ty.isSlice(mod));4321 assert(!payload_ty.isSlice(mod));
3556 return self.cmp(op, Type.bool, payload_ty, lhs_id, rhs_id);4322
4323 return try self.cmp(op, lhs.pun(payload_ty), rhs.pun(payload_ty));
3557 }4324 }
35584325
4326 const lhs_id = try lhs.materialize(self);
4327 const rhs_id = try rhs.materialize(self);
4328
3559 const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod))4329 const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod))
3560 try self.extractField(Type.bool, lhs_id, 1)4330 try self.extractField(Type.bool, lhs_id, 1)
3561 else4331 else
...@@ -3566,8 +4336,11 @@ const DeclGen = struct {...@@ -3566,8 +4336,11 @@ const DeclGen = struct {
3566 else4336 else
3567 try self.convertToDirect(Type.bool, rhs_id);4337 try self.convertToDirect(Type.bool, rhs_id);
35684338
4339 const lhs_valid = Temporary.init(Type.bool, lhs_valid_id);
4340 const rhs_valid = Temporary.init(Type.bool, rhs_valid_id);
4341
3569 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {4342 if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
3570 return try self.cmp(op, Type.bool, Type.bool, lhs_valid_id, rhs_valid_id);4343 return try self.cmp(op, lhs_valid, rhs_valid);
3571 }4344 }
35724345
3573 // a = lhs_valid4346 // a = lhs_valid
...@@ -3588,118 +4361,71 @@ const DeclGen = struct {...@@ -3588,118 +4361,71 @@ const DeclGen = struct {
3588 const lhs_pl_id = try self.extractField(payload_ty, lhs_id, 0);4361 const lhs_pl_id = try self.extractField(payload_ty, lhs_id, 0);
3589 const rhs_pl_id = try self.extractField(payload_ty, rhs_id, 0);4362 const rhs_pl_id = try self.extractField(payload_ty, rhs_id, 0);
35904363
3591 switch (op) {4364 const lhs_pl = Temporary.init(payload_ty, lhs_pl_id);
3592 .eq => {4365 const rhs_pl = Temporary.init(payload_ty, rhs_pl_id);
3593 const valid_eq_id = try self.cmp(.eq, Type.bool, Type.bool, lhs_valid_id, rhs_valid_id);4366
3594 const pl_eq_id = try self.cmp(op, Type.bool, payload_ty, lhs_pl_id, rhs_pl_id);4367 return switch (op) {
3595 const lhs_not_valid_id = self.spv.allocId();4368 .eq => try self.buildBinary(
3596 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, .{4369 .l_and,
3597 .id_result_type = bool_ty_id,4370 try self.cmp(.eq, lhs_valid, rhs_valid),
3598 .id_result = lhs_not_valid_id,4371 try self.buildBinary(
3599 .operand = lhs_valid_id,4372 .l_or,
3600 });4373 try self.buildUnary(.l_not, lhs_valid),
3601 const impl_id = self.spv.allocId();4374 try self.cmp(.eq, lhs_pl, rhs_pl),
3602 try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{4375 ),
3603 .id_result_type = bool_ty_id,4376 ),
3604 .id_result = impl_id,4377 .neq => try self.buildBinary(
3605 .operand_1 = lhs_not_valid_id,4378 .l_or,
3606 .operand_2 = pl_eq_id,4379 try self.cmp(.neq, lhs_valid, rhs_valid),
3607 });4380 try self.buildBinary(
3608 const result_id = self.spv.allocId();4381 .l_and,
3609 try self.func.body.emit(self.spv.gpa, .OpLogicalAnd, .{4382 lhs_valid,
3610 .id_result_type = bool_ty_id,4383 try self.cmp(.neq, lhs_pl, rhs_pl),
3611 .id_result = result_id,4384 ),
3612 .operand_1 = valid_eq_id,4385 ),
3613 .operand_2 = impl_id,
3614 });
3615 return result_id;
3616 },
3617 .neq => {
3618 const valid_neq_id = try self.cmp(.neq, Type.bool, Type.bool, lhs_valid_id, rhs_valid_id);
3619 const pl_neq_id = try self.cmp(op, Type.bool, payload_ty, lhs_pl_id, rhs_pl_id);
3620
3621 const impl_id = self.spv.allocId();
3622 try self.func.body.emit(self.spv.gpa, .OpLogicalAnd, .{
3623 .id_result_type = bool_ty_id,
3624 .id_result = impl_id,
3625 .operand_1 = lhs_valid_id,
3626 .operand_2 = pl_neq_id,
3627 });
3628 const result_id = self.spv.allocId();
3629 try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{
3630 .id_result_type = bool_ty_id,
3631 .id_result = result_id,
3632 .operand_1 = valid_neq_id,
3633 .operand_2 = impl_id,
3634 });
3635 return result_id;
3636 },
3637 else => unreachable,4386 else => unreachable,
3638 }4387 };
3639 },
3640 .Vector => {
3641 var wip = try self.elementWise(result_ty, true);
3642 defer wip.deinit();
3643 const scalar_ty = ty.scalarType(mod);
3644 for (wip.results, 0..) |*result_id, i| {
3645 const lhs_elem_id = try wip.elementAt(ty, lhs_id, i);
3646 const rhs_elem_id = try wip.elementAt(ty, rhs_id, i);
3647 result_id.* = try self.cmp(op, Type.bool, scalar_ty, lhs_elem_id, rhs_elem_id);
3648 }
3649 return wip.finalize();
3650 },4388 },
3651 else => unreachable,4389 else => unreachable,
3652 };4390 }
36534391
3654 const opcode: Opcode = opcode: {4392 const info = self.arithmeticTypeInfo(scalar_ty);
3655 const info = self.arithmeticTypeInfo(op_ty);4393 const pred: CmpPredicate = switch (info.class) {
3656 const signedness = switch (info.class) {4394 .composite_integer => unreachable, // TODO
3657 .composite_integer => {4395 .float => switch (op) {
3658 return self.todo("binary operations for composite integers", .{});4396 .eq => .f_oeq,
3659 },4397 .neq => .f_une,
3660 .float => break :opcode switch (op) {4398 .lt => .f_olt,
3661 .eq => .OpFOrdEqual,4399 .lte => .f_ole,
3662 .neq => .OpFUnordNotEqual,4400 .gt => .f_ogt,
3663 .lt => .OpFOrdLessThan,4401 .gte => .f_oge,
3664 .lte => .OpFOrdLessThanEqual,4402 },
3665 .gt => .OpFOrdGreaterThan,4403 .bool => switch (op) {
3666 .gte => .OpFOrdGreaterThanEqual,4404 .eq => .l_eq,
3667 },4405 .neq => .l_ne,
3668 .bool => break :opcode switch (op) {4406 else => unreachable,
3669 .eq => .OpLogicalEqual,4407 },
3670 .neq => .OpLogicalNotEqual,4408 .integer, .strange_integer => switch (info.signedness) {
3671 else => unreachable,4409 .signed => switch (op) {
4410 .eq => .i_eq,
4411 .neq => .i_ne,
4412 .lt => .s_lt,
4413 .lte => .s_le,
4414 .gt => .s_gt,
4415 .gte => .s_ge,
3672 },4416 },
3673 .integer, .strange_integer => info.signedness,
3674 };
3675
3676 break :opcode switch (signedness) {
3677 .unsigned => switch (op) {4417 .unsigned => switch (op) {
3678 .eq => .OpIEqual,4418 .eq => .i_eq,
3679 .neq => .OpINotEqual,4419 .neq => .i_ne,
3680 .lt => .OpULessThan,4420 .lt => .u_lt,
3681 .lte => .OpULessThanEqual,4421 .lte => .u_le,
3682 .gt => .OpUGreaterThan,4422 .gt => .u_gt,
3683 .gte => .OpUGreaterThanEqual,4423 .gte => .u_ge,
3684 },
3685 .signed => switch (op) {
3686 .eq => .OpIEqual,
3687 .neq => .OpINotEqual,
3688 .lt => .OpSLessThan,
3689 .lte => .OpSLessThanEqual,
3690 .gt => .OpSGreaterThan,
3691 .gte => .OpSGreaterThanEqual,
3692 },4424 },
3693 };4425 },
3694 };4426 };
36954427
3696 const result_id = self.spv.allocId();4428 return try self.buildCmp(pred, lhs, rhs);
3697 try self.func.body.emitRaw(self.spv.gpa, opcode, 4);
3698 self.func.body.writeOperand(spec.IdResultType, bool_ty_id);
3699 self.func.body.writeOperand(spec.IdResult, result_id);
3700 self.func.body.writeOperand(spec.IdResultType, cmp_lhs_id);
3701 self.func.body.writeOperand(spec.IdResultType, cmp_rhs_id);
3702 return result_id;
3703 }4429 }
37044430
3705 fn airCmp(4431 fn airCmp(
...@@ -3708,24 +4434,22 @@ const DeclGen = struct {...@@ -3708,24 +4434,22 @@ const DeclGen = struct {
3708 comptime op: std.math.CompareOperator,4434 comptime op: std.math.CompareOperator,
3709 ) !?IdRef {4435 ) !?IdRef {
3710 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;4436 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3711 const lhs_id = try self.resolve(bin_op.lhs);4437 const lhs = try self.temporary(bin_op.lhs);
3712 const rhs_id = try self.resolve(bin_op.rhs);4438 const rhs = try self.temporary(bin_op.rhs);
3713 const ty = self.typeOf(bin_op.lhs);
3714 const result_ty = self.typeOfIndex(inst);
37154439
3716 return try self.cmp(op, result_ty, ty, lhs_id, rhs_id);4440 const result = try self.cmp(op, lhs, rhs);
4441 return try result.materialize(self);
3717 }4442 }
37184443
3719 fn airVectorCmp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4444 fn airVectorCmp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3720 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;4445 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3721 const vec_cmp = self.air.extraData(Air.VectorCmp, ty_pl.payload).data;4446 const vec_cmp = self.air.extraData(Air.VectorCmp, ty_pl.payload).data;
3722 const lhs_id = try self.resolve(vec_cmp.lhs);4447 const lhs = try self.temporary(vec_cmp.lhs);
3723 const rhs_id = try self.resolve(vec_cmp.rhs);4448 const rhs = try self.temporary(vec_cmp.rhs);
3724 const op = vec_cmp.compareOperator();4449 const op = vec_cmp.compareOperator();
3725 const ty = self.typeOf(vec_cmp.lhs);
3726 const result_ty = self.typeOfIndex(inst);
37274450
3728 return try self.cmp(op, result_ty, ty, lhs_id, rhs_id);4451 const result = try self.cmp(op, lhs, rhs);
4452 return try result.materialize(self);
3729 }4453 }
37304454
3731 /// Bitcast one type to another. Note: both types, input, output are expected in **direct** representation.4455 /// Bitcast one type to another. Note: both types, input, output are expected in **direct** representation.
...@@ -3791,7 +4515,8 @@ const DeclGen = struct {...@@ -3791,7 +4515,8 @@ const DeclGen = struct {
3791 // should we change the representation of strange integers?4515 // should we change the representation of strange integers?
3792 if (dst_ty.zigTypeTag(mod) == .Int) {4516 if (dst_ty.zigTypeTag(mod) == .Int) {
3793 const info = self.arithmeticTypeInfo(dst_ty);4517 const info = self.arithmeticTypeInfo(dst_ty);
3794 return try self.normalize(dst_ty, result_id, info);4518 const result = try self.normalize(Temporary.init(dst_ty, result_id), info);
4519 return try result.materialize(self);
3795 }4520 }
37964521
3797 return result_id;4522 return result_id;
...@@ -3807,46 +4532,28 @@ const DeclGen = struct {...@@ -3807,46 +4532,28 @@ const DeclGen = struct {
38074532
3808 fn airIntCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4533 fn airIntCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3809 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4534 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3810 const operand_id = try self.resolve(ty_op.operand);4535 const src = try self.temporary(ty_op.operand);
3811 const src_ty = self.typeOf(ty_op.operand);
3812 const dst_ty = self.typeOfIndex(inst);4536 const dst_ty = self.typeOfIndex(inst);
38134537
3814 const src_info = self.arithmeticTypeInfo(src_ty);4538 const src_info = self.arithmeticTypeInfo(src.ty);
3815 const dst_info = self.arithmeticTypeInfo(dst_ty);4539 const dst_info = self.arithmeticTypeInfo(dst_ty);
38164540
3817 if (src_info.backing_bits == dst_info.backing_bits) {4541 if (src_info.backing_bits == dst_info.backing_bits) {
3818 return operand_id;4542 return try src.materialize(self);
3819 }4543 }
38204544
3821 var wip = try self.elementWise(dst_ty, false);4545 const converted = try self.buildIntConvert(dst_ty, src);
3822 defer wip.deinit();
3823 for (wip.results, 0..) |*result_id, i| {
3824 const elem_id = try wip.elementAt(src_ty, operand_id, i);
3825 const value_id = self.spv.allocId();
3826 switch (dst_info.signedness) {
3827 .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{
3828 .id_result_type = wip.ty_id,
3829 .id_result = value_id,
3830 .signed_value = elem_id,
3831 }),
3832 .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{
3833 .id_result_type = wip.ty_id,
3834 .id_result = value_id,
3835 .unsigned_value = elem_id,
3836 }),
3837 }
38384546
3839 // Make sure to normalize the result if shrinking.4547 // Make sure to normalize the result if shrinking.
3840 // Because strange ints are sign extended in their backing4548 // Because strange ints are sign extended in their backing
3841 // type, we don't need to normalize when growing the type. The4549 // type, we don't need to normalize when growing the type. The
3842 // representation is already the same.4550 // representation is already the same.
3843 if (dst_info.bits < src_info.bits) {4551 const result = if (dst_info.bits < src_info.bits)
3844 result_id.* = try self.normalize(wip.ty, value_id, dst_info);4552 try self.normalize(converted, dst_info)
3845 } else {4553 else
3846 result_id.* = value_id;4554 converted;
3847 }4555
3848 }4556 return try result.materialize(self);
3849 return try wip.finalize();
3850 }4557 }
38514558
3852 fn intFromPtr(self: *DeclGen, operand_id: IdRef) !IdRef {4559 fn intFromPtr(self: *DeclGen, operand_id: IdRef) !IdRef {
...@@ -3921,16 +4628,9 @@ const DeclGen = struct {...@@ -3921,16 +4628,9 @@ const DeclGen = struct {
39214628
3922 fn airIntFromBool(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4629 fn airIntFromBool(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3923 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;4630 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
3924 const operand_id = try self.resolve(un_op);4631 const operand = try self.temporary(un_op);
3925 const result_ty = self.typeOfIndex(inst);4632 const result = try self.intFromBool(operand);
39264633 return try result.materialize(self);
3927 var wip = try self.elementWise(result_ty, false);
3928 defer wip.deinit();
3929 for (wip.results, 0..) |*result_id, i| {
3930 const elem_id = try wip.elementAt(Type.bool, operand_id, i);
3931 result_id.* = try self.intFromBool(wip.ty, elem_id);
3932 }
3933 return try wip.finalize();
3934 }4634 }
39354635
3936 fn airFloatCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4636 fn airFloatCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
...@@ -3950,33 +4650,21 @@ const DeclGen = struct {...@@ -3950,33 +4650,21 @@ const DeclGen = struct {
39504650
3951 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4651 fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3952 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;4652 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
3953 const operand_id = try self.resolve(ty_op.operand);4653 const operand = try self.temporary(ty_op.operand);
3954 const result_ty = self.typeOfIndex(inst);4654 const result_ty = self.typeOfIndex(inst);
3955 const info = self.arithmeticTypeInfo(result_ty);4655 const info = self.arithmeticTypeInfo(result_ty);
39564656
3957 var wip = try self.elementWise(result_ty, false);4657 const result = switch (info.class) {
3958 defer wip.deinit();4658 .bool => try self.buildUnary(.l_not, operand),
39594659 .float => unreachable,
3960 for (0..wip.results.len) |i| {4660 .composite_integer => unreachable, // TODO
3961 const args = .{4661 .strange_integer, .integer => blk: {
3962 .id_result_type = wip.ty_id,4662 const complement = try self.buildUnary(.bit_not, operand);
3963 .id_result = wip.allocId(i),4663 break :blk try self.normalize(complement, info);
3964 .operand = try wip.elementAt(result_ty, operand_id, i),4664 },
3965 };4665 };
3966 switch (info.class) {
3967 .bool => {
3968 try self.func.body.emit(self.spv.gpa, .OpLogicalNot, args);
3969 },
3970 .float => unreachable,
3971 .composite_integer => unreachable, // TODO
3972 .strange_integer, .integer => {
3973 // Note: strange integer bits will be masked before operations that do not hold under modulo.
3974 try self.func.body.emit(self.spv.gpa, .OpNot, args);
3975 },
3976 }
3977 }
39784666
3979 return try wip.finalize();4667 return try result.materialize(self);
3980 }4668 }
39814669
3982 fn airArrayToSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4670 fn airArrayToSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
...@@ -4086,8 +4774,7 @@ const DeclGen = struct {...@@ -4086,8 +4774,7 @@ const DeclGen = struct {
4086 defer self.gpa.free(elem_ids);4774 defer self.gpa.free(elem_ids);
40874775
4088 for (elements, 0..) |element, i| {4776 for (elements, 0..) |element, i| {
4089 const id = try self.resolve(element);4777 elem_ids[i] = try self.resolve(element);
4090 elem_ids[i] = try self.convertToIndirect(result_ty.childType(mod), id);
4091 }4778 }
40924779
4093 return try self.constructVector(result_ty, elem_ids);4780 return try self.constructVector(result_ty, elem_ids);
...@@ -4234,16 +4921,57 @@ const DeclGen = struct {...@@ -4234,16 +4921,57 @@ const DeclGen = struct {
4234 const array_id = try self.resolve(bin_op.lhs);4921 const array_id = try self.resolve(bin_op.lhs);
4235 const index_id = try self.resolve(bin_op.rhs);4922 const index_id = try self.resolve(bin_op.rhs);
42364923
4924 if (self.isSpvVector(array_ty)) {
4925 const result_id = self.spv.allocId();
4926 try self.func.body.emit(self.spv.gpa, .OpVectorExtractDynamic, .{
4927 .id_result_type = try self.resolveType(elem_ty, .direct),
4928 .id_result = result_id,
4929 .vector = array_id,
4930 .index = index_id,
4931 });
4932 return result_id;
4933 }
4934
4237 // SPIR-V doesn't have an array indexing function for some damn reason.4935 // SPIR-V doesn't have an array indexing function for some damn reason.
4238 // For now, just generate a temporary and use that.4936 // For now, just generate a temporary and use that.
4239 // TODO: This backend probably also should use isByRef from llvm...4937 // TODO: This backend probably also should use isByRef from llvm...
42404938
4241 const elem_ptr_ty_id = try self.ptrType(elem_ty, .Function);4939 const is_vector = array_ty.isVector(mod);
4940
4941 const elem_repr: Repr = if (is_vector) .direct else .indirect;
4942 const ptr_array_ty_id = try self.ptrType2(array_ty, .Function, .direct);
4943 const ptr_elem_ty_id = try self.ptrType2(elem_ty, .Function, elem_repr);
4944
4945 const tmp_id = self.spv.allocId();
4946 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
4947 .id_result_type = ptr_array_ty_id,
4948 .id_result = tmp_id,
4949 .storage_class = .Function,
4950 });
4951
4952 try self.func.body.emit(self.spv.gpa, .OpStore, .{
4953 .pointer = tmp_id,
4954 .object = array_id,
4955 });
4956
4957 const elem_ptr_id = try self.accessChainId(ptr_elem_ty_id, tmp_id, &.{index_id});
4958
4959 const result_id = self.spv.allocId();
4960 try self.func.body.emit(self.spv.gpa, .OpLoad, .{
4961 .id_result_type = try self.resolveType(elem_ty, elem_repr),
4962 .id_result = result_id,
4963 .pointer = elem_ptr_id,
4964 });
4965
4966 if (is_vector) {
4967 // Result is already in direct representation
4968 return result_id;
4969 }
42424970
4243 const tmp_id = try self.alloc(array_ty, .{ .storage_class = .Function });4971 // This is an array type; the elements are stored in indirect representation.
4244 try self.store(array_ty, tmp_id, array_id, .{});4972 // We have to convert the type to direct.
4245 const elem_ptr_id = try self.accessChainId(elem_ptr_ty_id, tmp_id, &.{index_id});4973
4246 return try self.load(elem_ty, elem_ptr_id, .{});4974 return try self.convertToDirect(elem_ty, result_id);
4247 }4975 }
42484976
4249 fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {4977 fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
...@@ -4458,7 +5186,10 @@ const DeclGen = struct {...@@ -4458,7 +5186,10 @@ const DeclGen = struct {
4458 if (field_offset == 0) break :base_ptr_int field_ptr_int;5186 if (field_offset == 0) break :base_ptr_int field_ptr_int;
44595187
4460 const field_offset_id = try self.constInt(Type.usize, field_offset, .direct);5188 const field_offset_id = try self.constInt(Type.usize, field_offset, .direct);
4461 break :base_ptr_int try self.binOpSimple(Type.usize, field_ptr_int, field_offset_id, .OpISub);5189 const field_ptr_tmp = Temporary.init(Type.usize, field_ptr_int);
5190 const field_offset_tmp = Temporary.init(Type.usize, field_offset_id);
5191 const result = try self.buildBinary(.i_sub, field_ptr_tmp, field_offset_tmp);
5192 break :base_ptr_int try result.materialize(self);
4462 };5193 };
44635194
4464 const base_ptr = self.spv.allocId();5195 const base_ptr = self.spv.allocId();
...@@ -5273,13 +6004,17 @@ const DeclGen = struct {...@@ -5273,13 +6004,17 @@ const DeclGen = struct {
5273 else6004 else
5274 loaded_id;6005 loaded_id;
52756006
5276 const payload_ty_id = try self.resolveType(ptr_ty, .direct);6007 const ptr_ty_id = try self.resolveType(ptr_ty, .direct);
5277 const null_id = try self.spv.constNull(payload_ty_id);6008 const null_id = try self.spv.constNull(ptr_ty_id);
6009 const null_tmp = Temporary.init(ptr_ty, null_id);
6010 const ptr = Temporary.init(ptr_ty, ptr_id);
6011
5278 const op: std.math.CompareOperator = switch (pred) {6012 const op: std.math.CompareOperator = switch (pred) {
5279 .is_null => .eq,6013 .is_null => .eq,
5280 .is_non_null => .neq,6014 .is_non_null => .neq,
5281 };6015 };
5282 return try self.cmp(op, Type.bool, ptr_ty, ptr_id, null_id);6016 const result = try self.cmp(op, ptr, null_tmp);
6017 return try result.materialize(self);
5283 }6018 }
52846019
5285 const is_non_null_id = blk: {6020 const is_non_null_id = blk: {
src/codegen/spirv/Module.zig+15-7
...@@ -155,6 +155,9 @@ cache: struct {...@@ -155,6 +155,9 @@ cache: struct {
155 void_type: ?IdRef = null,155 void_type: ?IdRef = null,
156 int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, IdRef) = .{},156 int_types: std.AutoHashMapUnmanaged(std.builtin.Type.Int, IdRef) = .{},
157 float_types: std.AutoHashMapUnmanaged(std.builtin.Type.Float, IdRef) = .{},157 float_types: std.AutoHashMapUnmanaged(std.builtin.Type.Float, IdRef) = .{},
158 // This cache is required so that @Vector(X, u1) in direct representation has the
159 // same ID as @Vector(X, bool) in indirect representation.
160 vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .{},
158} = .{},161} = .{},
159162
160/// Set of Decls, referred to by Decl.Index.163/// Set of Decls, referred to by Decl.Index.
...@@ -194,6 +197,7 @@ pub fn deinit(self: *Module) void {...@@ -194,6 +197,7 @@ pub fn deinit(self: *Module) void {
194197
195 self.cache.int_types.deinit(self.gpa);198 self.cache.int_types.deinit(self.gpa);
196 self.cache.float_types.deinit(self.gpa);199 self.cache.float_types.deinit(self.gpa);
200 self.cache.vector_types.deinit(self.gpa);
197201
198 self.decls.deinit(self.gpa);202 self.decls.deinit(self.gpa);
199 self.decl_deps.deinit(self.gpa);203 self.decl_deps.deinit(self.gpa);
...@@ -474,13 +478,17 @@ pub fn floatType(self: *Module, bits: u16) !IdRef {...@@ -474,13 +478,17 @@ pub fn floatType(self: *Module, bits: u16) !IdRef {
474}478}
475479
476pub fn vectorType(self: *Module, len: u32, child_id: IdRef) !IdRef {480pub fn vectorType(self: *Module, len: u32, child_id: IdRef) !IdRef {
477 const result_id = self.allocId();481 const entry = try self.cache.vector_types.getOrPut(self.gpa, .{ child_id, len });
478 try self.sections.types_globals_constants.emit(self.gpa, .OpTypeVector, .{482 if (!entry.found_existing) {
479 .id_result = result_id,483 const result_id = self.allocId();
480 .component_type = child_id,484 entry.value_ptr.* = result_id;
481 .component_count = len,485 try self.sections.types_globals_constants.emit(self.gpa, .OpTypeVector, .{
482 });486 .id_result = result_id,
483 return result_id;487 .component_type = child_id,
488 .component_count = len,
489 });
490 }
491 return entry.value_ptr.*;
484}492}
485493
486pub fn constUndef(self: *Module, ty_id: IdRef) !IdRef {494pub fn constUndef(self: *Module, ty_id: IdRef) !IdRef {
src/link/SpirV.zig+1
...@@ -232,6 +232,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: std.Progress.Node)...@@ -232,6 +232,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: std.Progress.Node)
232 // name if it contains no strange characters is nice for debugging. URI encoding fits the bill.232 // name if it contains no strange characters is nice for debugging. URI encoding fits the bill.
233 // We're using : as separator, which is a reserved character.233 // We're using : as separator, which is a reserved character.
234234
235 try error_info.append(':');
235 try std.Uri.Component.percentEncode(236 try std.Uri.Component.percentEncode(
236 error_info.writer(),237 error_info.writer(),
237 name.toSlice(&mod.intern_pool),238 name.toSlice(&mod.intern_pool),
test/behavior/array.zig+1
...@@ -768,6 +768,7 @@ test "slicing array of zero-sized values" {...@@ -768,6 +768,7 @@ test "slicing array of zero-sized values" {
768 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;768 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
769 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;769 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
770 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;770 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
771 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
771772
772 var arr: [32]u0 = undefined;773 var arr: [32]u0 = undefined;
773 for (arr[0..]) |*zero|774 for (arr[0..]) |*zero|
test/behavior/byval_arg_var.zig+1
...@@ -6,6 +6,7 @@ var result: []const u8 = "wrong";...@@ -6,6 +6,7 @@ var result: []const u8 = "wrong";
6test "pass string literal byvalue to a generic var param" {6test "pass string literal byvalue to a generic var param" {
7 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;8 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
910
10 start();11 start();
11 blowUpStack(10);12 blowUpStack(10);
test/behavior/cast.zig+1
...@@ -1378,6 +1378,7 @@ test "assignment to optional pointer result loc" {...@@ -1378,6 +1378,7 @@ test "assignment to optional pointer result loc" {
13781378
1379test "cast between *[N]void and []void" {1379test "cast between *[N]void and []void" {
1380 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1380 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1381 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13811382
1382 var a: [4]void = undefined;1383 var a: [4]void = undefined;
1383 const b: []void = &a;1384 const b: []void = &a;
test/behavior/enum.zig+1
...@@ -1286,6 +1286,7 @@ test "matching captures causes enum equivalence" {...@@ -1286,6 +1286,7 @@ test "matching captures causes enum equivalence" {
1286test "large enum field values" {1286test "large enum field values" {
1287 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;1287 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1288 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1288 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1289 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
12891290
1290 {1291 {
1291 const E = enum(u64) { min = std.math.minInt(u64), max = std.math.maxInt(u64) };1292 const E = enum(u64) { min = std.math.minInt(u64), max = std.math.maxInt(u64) };
test/behavior/error.zig+3
...@@ -997,6 +997,7 @@ test "try used in recursive function with inferred error set" {...@@ -997,6 +997,7 @@ test "try used in recursive function with inferred error set" {
997 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO997 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
998 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO998 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
999 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;999 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1000 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10001001
1001 const Value = union(enum) {1002 const Value = union(enum) {
1002 values: []const @This(),1003 values: []const @This(),
...@@ -1103,6 +1104,7 @@ test "result location initialization of error union with OPV payload" {...@@ -1103,6 +1104,7 @@ test "result location initialization of error union with OPV payload" {
1103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1104 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1104 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1105 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1105 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1106 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1107 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11061108
1107 const S = struct {1109 const S = struct {
1108 x: u0,1110 x: u0,
...@@ -1125,6 +1127,7 @@ test "result location initialization of error union with OPV payload" {...@@ -1125,6 +1127,7 @@ test "result location initialization of error union with OPV payload" {
1125test "return error union with i65" {1127test "return error union with i65" {
1126 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;1128 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1127 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1129 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1130 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11281131
1129 try expect(try add(1000, 234) == 1234);1132 try expect(try add(1000, 234) == 1234);
1130}1133}
test/behavior/floatop.zig-34
...@@ -275,7 +275,6 @@ test "@sqrt f16" {...@@ -275,7 +275,6 @@ test "@sqrt f16" {
275 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO275 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO276 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
277 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO277 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
278 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
279 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;278 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
280 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;279 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
281280
...@@ -287,7 +286,6 @@ test "@sqrt f32/f64" {...@@ -287,7 +286,6 @@ test "@sqrt f32/f64" {
287 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO286 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
288 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO287 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
289 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO288 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
290 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
291 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;289 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
292 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;290 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
293291
...@@ -389,7 +387,6 @@ test "@sqrt with vectors" {...@@ -389,7 +387,6 @@ test "@sqrt with vectors" {
389 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO387 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
390 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO388 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
391 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO389 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
392 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
393 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;390 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
394391
395 try testSqrtWithVectors();392 try testSqrtWithVectors();
...@@ -410,7 +407,6 @@ test "@sin f16" {...@@ -410,7 +407,6 @@ test "@sin f16" {
410 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO407 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
411 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO408 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
412 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO409 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
413 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
414 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;410 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
415 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;411 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
416412
...@@ -422,7 +418,6 @@ test "@sin f32/f64" {...@@ -422,7 +418,6 @@ test "@sin f32/f64" {
422 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO418 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
423 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO419 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
424 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO420 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
425 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
426 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;421 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
427 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;422 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
428423
...@@ -464,7 +459,6 @@ test "@sin with vectors" {...@@ -464,7 +459,6 @@ test "@sin with vectors" {
464 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO459 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
465 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO460 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
466 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO461 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
467 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
468 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;462 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
469 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;463 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
470464
...@@ -486,7 +480,6 @@ test "@cos f16" {...@@ -486,7 +480,6 @@ test "@cos f16" {
486 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO480 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
487 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO481 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
488 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO482 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
489 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
490 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;483 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
491 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;484 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
492485
...@@ -498,7 +491,6 @@ test "@cos f32/f64" {...@@ -498,7 +491,6 @@ test "@cos f32/f64" {
498 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO491 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
499 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO492 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
500 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO493 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
501 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
502 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;494 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
503 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;495 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
504496
...@@ -540,7 +532,6 @@ test "@cos with vectors" {...@@ -540,7 +532,6 @@ test "@cos with vectors" {
540 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO532 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
541 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO533 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
542 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO534 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
543 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
544 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;535 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
545 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;536 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
546537
...@@ -574,7 +565,6 @@ test "@tan f32/f64" {...@@ -574,7 +565,6 @@ test "@tan f32/f64" {
574 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO565 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
575 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO566 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
576 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO567 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
577 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
578 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;568 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
579 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;569 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
580570
...@@ -616,7 +606,6 @@ test "@tan with vectors" {...@@ -616,7 +606,6 @@ test "@tan with vectors" {
616 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO606 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
617 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO607 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
618 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO608 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
619 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
620 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;609 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
621 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;610 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
622611
...@@ -638,7 +627,6 @@ test "@exp f16" {...@@ -638,7 +627,6 @@ test "@exp f16" {
638 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO627 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
639 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO628 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
640 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO629 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
641 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
642 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;630 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
643 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;631 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
644632
...@@ -650,7 +638,6 @@ test "@exp f32/f64" {...@@ -650,7 +638,6 @@ test "@exp f32/f64" {
650 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO638 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
651 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO639 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
652 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO640 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
653 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
654 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;641 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
655 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;642 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
656643
...@@ -696,7 +683,6 @@ test "@exp with vectors" {...@@ -696,7 +683,6 @@ test "@exp with vectors" {
696 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO683 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
697 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO684 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
698 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO685 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
699 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
700 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;686 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
701 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;687 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
702688
...@@ -718,7 +704,6 @@ test "@exp2 f16" {...@@ -718,7 +704,6 @@ test "@exp2 f16" {
718 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO704 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
719 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO705 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
720 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO706 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
721 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
722 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;707 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
723 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;708 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
724709
...@@ -730,7 +715,6 @@ test "@exp2 f32/f64" {...@@ -730,7 +715,6 @@ test "@exp2 f32/f64" {
730 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO715 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
731 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO716 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
732 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO717 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
733 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
734 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;718 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
735 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;719 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
736720
...@@ -771,7 +755,6 @@ test "@exp2 with @vectors" {...@@ -771,7 +755,6 @@ test "@exp2 with @vectors" {
771 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO755 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
772 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO756 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
773 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO757 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
774 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
775 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;758 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
776 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;759 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
777760
...@@ -793,7 +776,6 @@ test "@log f16" {...@@ -793,7 +776,6 @@ test "@log f16" {
793 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO776 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
794 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO777 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
795 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO778 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
796 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
797 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;779 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
798 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;780 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
799781
...@@ -805,7 +787,6 @@ test "@log f32/f64" {...@@ -805,7 +787,6 @@ test "@log f32/f64" {
805 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO787 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
806 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO788 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
807 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO789 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
808 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
809 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;790 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
810 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;791 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
811792
...@@ -847,7 +828,6 @@ test "@log with @vectors" {...@@ -847,7 +828,6 @@ test "@log with @vectors" {
847 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO828 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
848 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO829 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
849 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO830 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
850 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
851 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;831 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
852 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;832 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
853833
...@@ -866,7 +846,6 @@ test "@log2 f16" {...@@ -866,7 +846,6 @@ test "@log2 f16" {
866 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO846 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
867 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO847 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
868 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO848 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
869 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
870 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;849 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
871 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;850 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
872851
...@@ -878,7 +857,6 @@ test "@log2 f32/f64" {...@@ -878,7 +857,6 @@ test "@log2 f32/f64" {
878 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO857 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
879 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO858 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
880 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO859 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
881 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
882 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;860 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
883 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;861 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
884862
...@@ -919,7 +897,6 @@ test "@log2 with vectors" {...@@ -919,7 +897,6 @@ test "@log2 with vectors" {
919 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO897 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
920 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO898 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
921 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO899 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
922 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
923 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;900 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
924 // https://github.com/ziglang/zig/issues/13681901 // https://github.com/ziglang/zig/issues/13681
925 if (builtin.zig_backend == .stage2_llvm and902 if (builtin.zig_backend == .stage2_llvm and
...@@ -945,7 +922,6 @@ test "@log10 f16" {...@@ -945,7 +922,6 @@ test "@log10 f16" {
945 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO922 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
946 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO923 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
947 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO924 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
948 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
949 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;925 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
950 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;926 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
951927
...@@ -957,7 +933,6 @@ test "@log10 f32/f64" {...@@ -957,7 +933,6 @@ test "@log10 f32/f64" {
957 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO933 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
958 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO934 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
959 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO935 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
960 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
961 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;936 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
962 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;937 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
963938
...@@ -998,7 +973,6 @@ test "@log10 with vectors" {...@@ -998,7 +973,6 @@ test "@log10 with vectors" {
998 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO973 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
999 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO974 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1000 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO975 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1001 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1002 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;976 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1003 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;977 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1004978
...@@ -1243,7 +1217,6 @@ test "@ceil f16" {...@@ -1243,7 +1217,6 @@ test "@ceil f16" {
1243 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1217 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1244 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1218 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1245 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1219 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1246 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1247 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1220 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
12481221
1249 try testCeil(f16);1222 try testCeil(f16);
...@@ -1255,7 +1228,6 @@ test "@ceil f32/f64" {...@@ -1255,7 +1228,6 @@ test "@ceil f32/f64" {
1255 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1228 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1256 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1229 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1257 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1230 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1258 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1259 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1231 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
12601232
1261 try testCeil(f32);1233 try testCeil(f32);
...@@ -1320,7 +1292,6 @@ test "@ceil with vectors" {...@@ -1320,7 +1292,6 @@ test "@ceil with vectors" {
1320 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1292 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1321 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1293 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1322 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1294 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1323 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1324 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1295 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1325 if (builtin.zig_backend == .stage2_x86_64 and1296 if (builtin.zig_backend == .stage2_x86_64 and
1326 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;1297 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse4_1)) return error.SkipZigTest;
...@@ -1344,7 +1315,6 @@ test "@trunc f16" {...@@ -1344,7 +1315,6 @@ test "@trunc f16" {
1344 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1315 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1345 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1316 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1346 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1317 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1347 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1348 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1318 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
13491319
1350 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) {1320 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) {
...@@ -1361,7 +1331,6 @@ test "@trunc f32/f64" {...@@ -1361,7 +1331,6 @@ test "@trunc f32/f64" {
1361 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1331 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1362 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1332 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1363 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1333 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1364 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1365 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1334 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
13661335
1367 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) {1336 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isMIPS()) {
...@@ -1430,7 +1399,6 @@ fn testTrunc(comptime T: type) !void {...@@ -1430,7 +1399,6 @@ fn testTrunc(comptime T: type) !void {
1430test "@trunc with vectors" {1399test "@trunc with vectors" {
1431 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1400 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1432 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1401 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1433 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1434 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1402 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1435 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1403 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1436 if (builtin.zig_backend == .stage2_x86_64 and1404 if (builtin.zig_backend == .stage2_x86_64 and
...@@ -1454,7 +1422,6 @@ test "neg f16" {...@@ -1454,7 +1422,6 @@ test "neg f16" {
1454 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1422 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1455 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1423 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1456 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1424 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1457 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1458 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;1425 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1459 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1426 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1460 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1427 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
...@@ -1472,7 +1439,6 @@ test "neg f32/f64" {...@@ -1472,7 +1439,6 @@ test "neg f32/f64" {
1472 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1439 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1473 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1440 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1474 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1441 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1475 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1476 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;1442 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1477 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1443 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
14781444
test/behavior/hasdecl.zig+2
...@@ -13,6 +13,7 @@ const Bar = struct {...@@ -13,6 +13,7 @@ const Bar = struct {
1313
14test "@hasDecl" {14test "@hasDecl" {
15 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1617
17 try expect(@hasDecl(Foo, "public_thing"));18 try expect(@hasDecl(Foo, "public_thing"));
18 try expect(!@hasDecl(Foo, "private_thing"));19 try expect(!@hasDecl(Foo, "private_thing"));
...@@ -25,6 +26,7 @@ test "@hasDecl" {...@@ -25,6 +26,7 @@ test "@hasDecl" {
2526
26test "@hasDecl using a sliced string literal" {27test "@hasDecl using a sliced string literal" {
27 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;28 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
29 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2830
29 try expect(@hasDecl(@This(), "std") == true);31 try expect(@hasDecl(@This(), "std") == true);
30 try expect(@hasDecl(@This(), "std"[0..0]) == false);32 try expect(@hasDecl(@This(), "std"[0..0]) == false);
test/behavior/math.zig+54-4
...@@ -440,7 +440,6 @@ test "division" {...@@ -440,7 +440,6 @@ test "division" {
440 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO440 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
441 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO441 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
442 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO442 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
443 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
444 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;443 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
445 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;444 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
446 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;445 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
...@@ -530,7 +529,6 @@ test "division half-precision floats" {...@@ -530,7 +529,6 @@ test "division half-precision floats" {
530 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO529 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
531 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO530 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
532 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO531 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
533 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
534 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;532 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
535 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;533 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
536534
...@@ -1030,6 +1028,60 @@ test "@mulWithOverflow bitsize > 32" {...@@ -1030,6 +1028,60 @@ test "@mulWithOverflow bitsize > 32" {
1030 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO1028 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1031 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1029 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10321030
1031 {
1032 var a: u40 = 3;
1033 var b: u40 = 0x55_5555_5555;
1034 var ov = @mulWithOverflow(a, b);
1035
1036 try expect(ov[0] == 0xff_ffff_ffff);
1037 try expect(ov[1] == 0);
1038
1039 // Check that overflow bits in the low-word of wide-multiplications are checked too.
1040 // Intermediate result is less than 2**64
1041 b = 0x55_5555_5556;
1042 ov = @mulWithOverflow(a, b);
1043 try expect(ov[0] == 2);
1044 try expect(ov[1] == 1);
1045
1046 // Check that overflow bits in the high-word of wide-multiplications are checked too.
1047 // Intermediate result is more than 2**64 and bits 40..64 are not set.
1048 a = 0x10_0000_0000;
1049 b = 0x10_0000_0000;
1050 ov = @mulWithOverflow(a, b);
1051 try expect(ov[0] == 0);
1052 try expect(ov[1] == 1);
1053 }
1054
1055 {
1056 var a: i40 = 3;
1057 var b: i40 = -0x2a_aaaa_aaaa;
1058 var ov = @mulWithOverflow(a, b);
1059
1060 try expect(ov[0] == -0x7f_ffff_fffe);
1061 try expect(ov[1] == 0);
1062
1063 // Check that the sign bit is properly checked
1064 b = -0x2a_aaaa_aaab;
1065 ov = @mulWithOverflow(a, b);
1066 try expect(ov[0] == 0x7f_ffff_ffff);
1067 try expect(ov[1] == 1);
1068
1069 // Check that the low-order bits above the sign are checked.
1070 a = 6;
1071 ov = @mulWithOverflow(a, b);
1072 try expect(ov[0] == -2);
1073 try expect(ov[1] == 1);
1074
1075 // Check that overflow bits in the high-word of wide-multiplications are checked too.
1076 // high parts and sign of low-order bits are all 1.
1077 a = 0x08_0000_0000;
1078 b = -0x08_0000_0001;
1079 ov = @mulWithOverflow(a, b);
1080
1081 try expect(ov[0] == -0x8_0000_0000);
1082 try expect(ov[1] == 1);
1083 }
1084
1033 {1085 {
1034 var a: u62 = 3;1086 var a: u62 = 3;
1035 _ = &a;1087 _ = &a;
...@@ -1579,7 +1631,6 @@ test "@round f16" {...@@ -1579,7 +1631,6 @@ test "@round f16" {
1579 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1631 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1580 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1632 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1581 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1633 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1582 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1583 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1634 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1584 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1635 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
15851636
...@@ -1591,7 +1642,6 @@ test "@round f32/f64" {...@@ -1591,7 +1642,6 @@ test "@round f32/f64" {
1591 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1642 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1592 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1643 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1593 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1644 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1594 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1595 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;1645 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
1596 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1646 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
15971647
test/behavior/optional.zig+2
...@@ -61,6 +61,7 @@ test "optional with zero-bit type" {...@@ -61,6 +61,7 @@ test "optional with zero-bit type" {
61 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;61 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
62 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;62 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
63 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;63 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
64 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
6465
65 const S = struct {66 const S = struct {
66 fn doTheTest(comptime ZeroBit: type, comptime zero_bit: ZeroBit) !void {67 fn doTheTest(comptime ZeroBit: type, comptime zero_bit: ZeroBit) !void {
...@@ -641,6 +642,7 @@ test "result location initialization of optional with OPV payload" {...@@ -641,6 +642,7 @@ test "result location initialization of optional with OPV payload" {
641 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO642 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
642 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO643 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
643 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;644 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
645 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
644646
645 const S = struct {647 const S = struct {
646 x: u0,648 x: u0,
test/behavior/packed-struct.zig+2
...@@ -1306,6 +1306,8 @@ test "2-byte packed struct argument in C calling convention" {...@@ -1306,6 +1306,8 @@ test "2-byte packed struct argument in C calling convention" {
1306}1306}
13071307
1308test "packed struct contains optional pointer" {1308test "packed struct contains optional pointer" {
1309 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1310
1309 const foo: packed struct {1311 const foo: packed struct {
1310 a: ?*@This() = null,1312 a: ?*@This() = null,
1311 } = .{};1313 } = .{};
test/behavior/packed-union.zig+2
...@@ -177,6 +177,8 @@ test "assigning to non-active field at comptime" {...@@ -177,6 +177,8 @@ test "assigning to non-active field at comptime" {
177}177}
178178
179test "comptime packed union of pointers" {179test "comptime packed union of pointers" {
180 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
181
180 const U = packed union {182 const U = packed union {
181 a: *const u32,183 a: *const u32,
182 b: *const [1]u32,184 b: *const [1]u32,
test/behavior/select.zig-2
...@@ -8,7 +8,6 @@ test "@select vectors" {...@@ -8,7 +8,6 @@ test "@select vectors" {
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
12 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;11 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1312
14 try comptime selectVectors();13 try comptime selectVectors();
...@@ -39,7 +38,6 @@ test "@select arrays" {...@@ -39,7 +38,6 @@ test "@select arrays" {
39 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO38 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
40 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO39 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
41 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO40 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
42 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
43 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;41 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
44 if (builtin.zig_backend == .stage2_x86_64 and42 if (builtin.zig_backend == .stage2_x86_64 and
45 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .avx2)) return error.SkipZigTest;43 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .avx2)) return error.SkipZigTest;
test/behavior/shuffle.zig+83
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const mem = std.mem;3const mem = std.mem;
4const expect = std.testing.expect;4const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
56
6test "@shuffle int" {7test "@shuffle int" {
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -49,6 +50,88 @@ test "@shuffle int" {...@@ -49,6 +50,88 @@ test "@shuffle int" {
49 try comptime S.doTheTest();50 try comptime S.doTheTest();
50}51}
5152
53test "@shuffle int strange sizes" {
54 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
55 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
56 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
57 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
58 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
59 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
60
61 try comptime testShuffle(2, 2, 2);
62 try testShuffle(2, 2, 2);
63 try comptime testShuffle(4, 4, 4);
64 try testShuffle(4, 4, 4);
65 try comptime testShuffle(7, 4, 4);
66 try testShuffle(7, 4, 4);
67 try comptime testShuffle(8, 6, 4);
68 try testShuffle(8, 6, 4);
69 try comptime testShuffle(2, 7, 5);
70 try testShuffle(2, 7, 5);
71 try comptime testShuffle(13, 16, 12);
72 try testShuffle(13, 16, 12);
73 try comptime testShuffle(19, 3, 17);
74 try testShuffle(19, 3, 17);
75 try comptime testShuffle(1, 10, 1);
76 try testShuffle(1, 10, 1);
77}
78
79fn testShuffle(
80 comptime x_len: comptime_int,
81 comptime a_len: comptime_int,
82 comptime b_len: comptime_int,
83) !void {
84 const T = i32;
85 const XT = @Vector(x_len, T);
86 const AT = @Vector(a_len, T);
87 const BT = @Vector(b_len, T);
88
89 const a_elems = comptime blk: {
90 var elems: [a_len]T = undefined;
91 for (&elems, 0..) |*elem, i| elem.* = @intCast(100 + i);
92 break :blk elems;
93 };
94 var a: AT = a_elems;
95 _ = &a;
96
97 const b_elems = comptime blk: {
98 var elems: [b_len]T = undefined;
99 for (&elems, 0..) |*elem, i| elem.* = @intCast(1000 + i);
100 break :blk elems;
101 };
102 var b: BT = b_elems;
103 _ = &b;
104
105 const mask_seed: []const i32 = &.{ -14, -31, 23, 1, 21, 13, 17, -21, -10, -27, -16, -5, 15, 14, -2, 26, 2, -31, -24, -16 };
106
107 const mask = comptime blk: {
108 var elems: [x_len]i32 = undefined;
109 for (&elems, 0..) |*elem, i| {
110 const mask_val = mask_seed[i];
111 if (mask_val >= 0) {
112 elem.* = @mod(mask_val, a_len);
113 } else {
114 elem.* = @mod(mask_val, -b_len);
115 }
116 }
117
118 break :blk elems;
119 };
120
121 const x: XT = @shuffle(T, a, b, mask);
122
123 const x_elems: [x_len]T = x;
124 for (mask, x_elems) |m, x_elem| {
125 if (m >= 0) {
126 // Element from A
127 try expectEqual(x_elem, a_elems[@intCast(m)]);
128 } else {
129 // Element from B
130 try expectEqual(x_elem, b_elems[@intCast(~m)]);
131 }
132 }
133}
134
52test "@shuffle bool 1" {135test "@shuffle bool 1" {
53 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO136 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
54 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO137 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
test/behavior/slice.zig+3
...@@ -408,6 +408,7 @@ test "slice syntax resulting in pointer-to-array" {...@@ -408,6 +408,7 @@ test "slice syntax resulting in pointer-to-array" {
408 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO408 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
409 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO409 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
410 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;410 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
411 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
411412
412 const S = struct {413 const S = struct {
413 fn doTheTest() !void {414 fn doTheTest() !void {
...@@ -863,6 +864,7 @@ test "global slice field access" {...@@ -863,6 +864,7 @@ test "global slice field access" {
863864
864test "slice of void" {865test "slice of void" {
865 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;866 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
867 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
866868
867 var n: usize = 10;869 var n: usize = 10;
868 _ = &n;870 _ = &n;
...@@ -988,6 +990,7 @@ test "get address of element of zero-sized slice" {...@@ -988,6 +990,7 @@ test "get address of element of zero-sized slice" {
988 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO990 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
989 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO991 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
990 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;992 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
993 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
991994
992 const S = struct {995 const S = struct {
993 fn destroy(_: *void) void {}996 fn destroy(_: *void) void {}
test/behavior/string_literals.zig+2
...@@ -35,6 +35,7 @@ test "@typeName() returns a string literal" {...@@ -35,6 +35,7 @@ test "@typeName() returns a string literal" {
35 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO35 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
36 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO36 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
37 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;37 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
38 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
3839
39 try std.testing.expect(*const [type_name.len:0]u8 == @TypeOf(type_name));40 try std.testing.expect(*const [type_name.len:0]u8 == @TypeOf(type_name));
40 try std.testing.expect(std.mem.eql(u8, "behavior.string_literals.TestType", type_name));41 try std.testing.expect(std.mem.eql(u8, "behavior.string_literals.TestType", type_name));
...@@ -49,6 +50,7 @@ test "@embedFile() returns a string literal" {...@@ -49,6 +50,7 @@ test "@embedFile() returns a string literal" {
49 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO50 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
50 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO51 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;52 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
53 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
5254
53 try std.testing.expect(*const [expected_contents.len:0]u8 == @TypeOf(actual_contents));55 try std.testing.expect(*const [expected_contents.len:0]u8 == @TypeOf(actual_contents));
54 try std.testing.expect(std.mem.eql(u8, expected_contents, actual_contents));56 try std.testing.expect(std.mem.eql(u8, expected_contents, actual_contents));
test/behavior/typename.zig+5
...@@ -43,6 +43,7 @@ test "anon field init" {...@@ -43,6 +43,7 @@ test "anon field init" {
43 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO43 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
44 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO44 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;45 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
46 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
4647
47 const Foo = .{48 const Foo = .{
48 .T1 = struct {},49 .T1 = struct {},
...@@ -91,6 +92,7 @@ test "top level decl" {...@@ -91,6 +92,7 @@ test "top level decl" {
91 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO92 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
92 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO93 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
93 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;94 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
95 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
9496
95 try expectEqualStrings(97 try expectEqualStrings(
96 "behavior.typename.A_Struct",98 "behavior.typename.A_Struct",
...@@ -141,6 +143,7 @@ test "fn param" {...@@ -141,6 +143,7 @@ test "fn param" {
141 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO143 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
142 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO144 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
143 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;145 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
146 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
144147
145 // https://github.com/ziglang/zig/issues/675148 // https://github.com/ziglang/zig/issues/675
146 try expectEqualStrings(149 try expectEqualStrings(
...@@ -221,6 +224,7 @@ test "local variable" {...@@ -221,6 +224,7 @@ test "local variable" {
221 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO224 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
222 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO225 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
223 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;226 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
227 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
224228
225 const Foo = struct { a: u32 };229 const Foo = struct { a: u32 };
226 const Bar = union { a: u32 };230 const Bar = union { a: u32 };
...@@ -250,6 +254,7 @@ test "anon name strategy used in sub expression" {...@@ -250,6 +254,7 @@ test "anon name strategy used in sub expression" {
250 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO254 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
251 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO255 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
252 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;256 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
257 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
253258
254 const S = struct {259 const S = struct {
255 fn getTheName() []const u8 {260 fn getTheName() []const u8 {
test/behavior/union.zig+1
...@@ -920,6 +920,7 @@ test "union no tag with struct member" {...@@ -920,6 +920,7 @@ test "union no tag with struct member" {
920 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO920 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
921 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO921 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
922 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO922 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
923 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
923924
924 const Struct = struct {};925 const Struct = struct {};
925 const Union = union {926 const Union = union {
test/behavior/vector.zig+1-1
...@@ -268,6 +268,7 @@ test "tuple to vector" {...@@ -268,6 +268,7 @@ test "tuple to vector" {
268 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO268 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
269 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO269 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
270 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;270 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
271 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
271272
272 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {273 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) {
273 // Regressed with LLVM 14:274 // Regressed with LLVM 14:
...@@ -547,7 +548,6 @@ test "vector division operators" {...@@ -547,7 +548,6 @@ test "vector division operators" {
547 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO548 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
548 if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;549 if (builtin.zig_backend == .stage2_llvm and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
549 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO550 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
550 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
551 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;551 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
552552
553 const S = struct {553 const S = struct {