diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index b572272613db9a7fba95c67d900873a6e3b125f2..dd3b35488da9ad4580c48465ed25734280304a38 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -3016,9 +3016,9 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { .ptr_add => try cg.airPtrAdd(inst), .ptr_sub => try cg.airPtrSub(inst), - .bit_and => try cg.airBinOpSimple(inst, .OpBitwiseAnd), - .bit_or => try cg.airBinOpSimple(inst, .OpBitwiseOr), - .xor => try cg.airBinOpSimple(inst, .OpBitwiseXor), + .bit_and => try cg.airBitwiseOp(inst, .bit_and), + .bit_or => try cg.airBitwiseOp(inst, .bit_or), + .xor => try cg.airBitwiseOp(inst, .xor), .shl, .shl_exact => try cg.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical), .shr, .shr_exact => try cg.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic), @@ -3145,6 +3145,34 @@ fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: Opcode) !?Id { return try result.materialize(cg); } +const BitwiseOp = enum { bit_and, bit_or, xor }; + +fn airBitwiseOp(cg: *CodeGen, inst: Air.Inst.Index, op: BitwiseOp) !?Id { + const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; + const lhs = try cg.temporary(bin_op.lhs); + const rhs = try cg.temporary(bin_op.rhs); + const info = cg.arithmeticTypeInfo(lhs.ty); + + // SPIR-V requires logical opcodes for booleans, bitwise opcodes for integers. + const opcode: Opcode = switch (info.class) { + .bool => switch (op) { + .bit_and => .OpLogicalAnd, + .bit_or => .OpLogicalOr, + .xor => .OpLogicalNotEqual, + }, + .integer, .strange_integer => switch (op) { + .bit_and => .OpBitwiseAnd, + .bit_or => .OpBitwiseOr, + .xor => .OpBitwiseXor, + }, + .float => unreachable, + .composite_integer => unreachable, // TODO + }; + + const result = try cg.buildBinary(opcode, lhs, rhs); + return try result.materialize(cg); +} + fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode) !?Id { const zcu = cg.module.zcu; const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; @@ -3389,9 +3417,12 @@ fn airArithOp( const info = cg.arithmeticTypeInfo(lhs.ty); const result = switch (info.class) { .composite_integer => unreachable, // TODO - .integer, .strange_integer => switch (info.signedness) { - .signed => try cg.buildBinary(sop, lhs, rhs), - .unsigned => try cg.buildBinary(uop, lhs, rhs), + .integer, .strange_integer => res: { + const raw = switch (info.signedness) { + .signed => try cg.buildBinary(sop, lhs, rhs), + .unsigned => try cg.buildBinary(uop, lhs, rhs), + }; + break :res try cg.normalize(raw, info); }, .float => try cg.buildBinary(fop, lhs, rhs), .bool => unreachable, @@ -3766,7 +3797,6 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const operand = try cg.resolve(reduce.operand); const operand_ty = cg.typeOf(reduce.operand); const scalar_ty = operand_ty.scalarType(zcu); - const scalar_ty_id = try cg.resolveType(scalar_ty, .direct); const info = cg.arithmeticTypeInfo(operand_ty); const len = operand_ty.vectorLen(zcu); const first = try cg.extractVectorComponent(scalar_ty, operand, 0); @@ -3792,8 +3822,6 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id { else => {}, } - var result_id = first; - const opcode: Opcode = switch (info.class) { .bool => switch (reduce.operation) { .And => .OpLogicalAnd, @@ -3817,19 +3845,18 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id { .composite_integer => unreachable, // TODO }; + const needs_normalize = info.class == .strange_integer and + (reduce.operation == .Add or reduce.operation == .Mul); + + var result: Temporary = .init(scalar_ty, first); for (1..len) |i| { - const lhs = result_id; - const rhs = try cg.extractVectorComponent(scalar_ty, operand, @intCast(i)); - result_id = cg.module.allocId(); - - try cg.body.emitRaw(cg.module.gpa, opcode, 4); - cg.body.writeOperand(Id, scalar_ty_id); - cg.body.writeOperand(Id, result_id); - cg.body.writeOperand(Id, lhs); - cg.body.writeOperand(Id, rhs); + const rhs_id = try cg.extractVectorComponent(scalar_ty, operand, @intCast(i)); + const rhs: Temporary = .init(scalar_ty, rhs_id); + const stepped = try cg.buildBinary(opcode, result, rhs); + result = if (needs_normalize) try cg.normalize(stepped, info) else stepped; } - return result_id; + return try result.materialize(cg); } fn airShuffleOne(cg: *CodeGen, inst: Air.Inst.Index) !?Id { @@ -4314,7 +4341,11 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const dst_info = cg.arithmeticTypeInfo(dst_ty); if (src_info.backing_bits == dst_info.backing_bits) { - return try src.materialize(cg); + const result = if (dst_info.bits < src_info.bits) + try cg.normalize(src.pun(dst_ty), dst_info) + else + src.pun(dst_ty); + return try result.materialize(cg); } const converted = try cg.buildConvert(dst_ty, src); diff --git a/test/behavior/bool.zig b/test/behavior/bool.zig index 7944c0903eba58efb24377b22d773f347a3674c6..72c1dff3362afc5960160776841f04a8b22ef2f9 100644 --- a/test/behavior/bool.zig +++ b/test/behavior/bool.zig @@ -10,7 +10,6 @@ test "bool literals" { test "cast bool to int" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const t = true; const f = false; diff --git a/test/behavior/cast_int.zig b/test/behavior/cast_int.zig index cd0f36c68a6fe6526b68624766c4178f7b37dee3..9e2754bb50e222b907285dcfb0a0c55240d66e72 100644 --- a/test/behavior/cast_int.zig +++ b/test/behavior/cast_int.zig @@ -19,7 +19,6 @@ test "@intCast i32 to u7" { } test "coerce i8 to i32 and @intCast back" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -35,7 +34,6 @@ test "coerce i8 to i32 and @intCast back" { } test "coerce non byte-sized integers accross 32bits boundary" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO { diff --git a/test/behavior/duplicated_test_names.zig b/test/behavior/duplicated_test_names.zig index e7ee93776cb23e8731255818a1a38f485c243860..b16a7787d84b7f22c57376fee8cae225ced9b87f 100644 --- a/test/behavior/duplicated_test_names.zig +++ b/test/behavior/duplicated_test_names.zig @@ -15,7 +15,6 @@ comptime { test "thingy" {} test thingy { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (thingy(1, 2) != 3) unreachable; diff --git a/test/behavior/hasdecl.zig b/test/behavior/hasdecl.zig index 220a4afd491947c9a3906f80402ec197a8aff01a..71f9200b276e8bb02fdf5f53a7eb33c5c436e7d1 100644 --- a/test/behavior/hasdecl.zig +++ b/test/behavior/hasdecl.zig @@ -13,7 +13,6 @@ const Bar = struct { test "@hasDecl" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try expect(@hasDecl(Foo, "public_thing")); try expect(!@hasDecl(Foo, "private_thing")); @@ -26,7 +25,6 @@ test "@hasDecl" { test "@hasDecl using a sliced string literal" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try expect(@hasDecl(@This(), "std") == true); try expect(@hasDecl(@This(), "std"[0..0]) == false); diff --git a/test/behavior/import.zig b/test/behavior/import.zig index faa3a08141558864248a10bff2e5a60f7993fe42..7f1025a9156ef723d2733432f2e6511f3732c071 100644 --- a/test/behavior/import.zig +++ b/test/behavior/import.zig @@ -5,21 +5,18 @@ const expectEqual = std.testing.expectEqual; const a_namespace = @import("import/a_namespace.zig"); test "call fn via namespace lookup" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(@as(i32, 1234) == a_namespace.foo()); } test "importing the same thing gives the same import" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(@import("std") == @import("std")); } test "import empty file" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; _ = @import("import/empty.zig"); diff --git a/test/behavior/ir_block_deps.zig b/test/behavior/ir_block_deps.zig index f6ed6bb53f0d5e961602d787db1e7895f4de1a18..d4218387ecd6987feaaa1c751947b2b27e8fb01f 100644 --- a/test/behavior/ir_block_deps.zig +++ b/test/behavior/ir_block_deps.zig @@ -19,8 +19,8 @@ fn getErrInt() anyerror!i32 { test "ir block deps" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try expect((foo(1) catch unreachable) == 0); try expect((foo(2) catch unreachable) == 0); diff --git a/test/behavior/namespace_depends_on_compile_var.zig b/test/behavior/namespace_depends_on_compile_var.zig index 8cc7a9b85e01a37be83a8f82645a1c8114ea95b2..36c2b5ae666043d26317b63afcf71afd04e67275 100644 --- a/test/behavior/namespace_depends_on_compile_var.zig +++ b/test/behavior/namespace_depends_on_compile_var.zig @@ -3,7 +3,6 @@ const builtin = @import("builtin"); const expect = std.testing.expect; test "namespace depends on compile var" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (some_namespace.a_bool) { diff --git a/test/behavior/pub_enum.zig b/test/behavior/pub_enum.zig index 39ee14b9dc52151f0adecec02a4add23adfc3d72..fe397fdc640d2aa280050cfcd8ce95a40bd45328 100644 --- a/test/behavior/pub_enum.zig +++ b/test/behavior/pub_enum.zig @@ -3,7 +3,6 @@ const other = @import("pub_enum/other.zig"); const expect = @import("std").testing.expect; test "pub enum" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try pubEnumTest(other.APubEnum.Two); @@ -13,7 +12,6 @@ fn pubEnumTest(foo: other.APubEnum) !void { } test "cast with imported symbol" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; try expect(@as(other.size_t, 42) == 42); diff --git a/test/behavior/wrapping_arithmetic.zig b/test/behavior/wrapping_arithmetic.zig index 247023c17a4d3531f5c45b66b9cc8742fe681b4a..e80f7407c1a37ef1586afbcf536848ed7b55f5fc 100644 --- a/test/behavior/wrapping_arithmetic.zig +++ b/test/behavior/wrapping_arithmetic.zig @@ -3,9 +3,9 @@ const builtin = @import("builtin"); const minInt = std.math.minInt; const maxInt = std.math.maxInt; const expect = std.testing.expect; +const skip128 = builtin.zig_backend == .stage2_spirv; test "wrapping add" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { @@ -14,14 +14,14 @@ test "wrapping add" { try testWrapAdd(i8, -128, -128, 0); try testWrapAdd(i2, 1, 1, -2); try testWrapAdd(i64, maxInt(i64), 1, minInt(i64)); - try testWrapAdd(i128, maxInt(i128), -maxInt(i128), 0); - try testWrapAdd(i128, minInt(i128), maxInt(i128), -1); + if (!skip128) try testWrapAdd(i128, maxInt(i128), -maxInt(i128), 0); + if (!skip128) try testWrapAdd(i128, minInt(i128), maxInt(i128), -1); try testWrapAdd(i8, 127, 127, -2); try testWrapAdd(u8, 3, 10, 13); try testWrapAdd(u8, 255, 255, 254); try testWrapAdd(u2, 3, 2, 1); try testWrapAdd(u3, 7, 1, 0); - try testWrapAdd(u128, maxInt(u128), 1, minInt(u128)); + if (!skip128) try testWrapAdd(u128, maxInt(u128), 1, minInt(u128)); } fn testWrapAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { @@ -43,7 +43,6 @@ test "wrapping add" { } test "wrapping subtraction" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { @@ -52,12 +51,12 @@ test "wrapping subtraction" { try testWrapSub(i8, -128, -128, 0); try testWrapSub(i8, -1, 127, -128); try testWrapSub(i64, minInt(i64), 1, maxInt(i64)); - try testWrapSub(i128, maxInt(i128), -1, minInt(i128)); - try testWrapSub(i128, minInt(i128), -maxInt(i128), -1); + if (!skip128) try testWrapSub(i128, maxInt(i128), -1, minInt(i128)); + if (!skip128) try testWrapSub(i128, minInt(i128), -maxInt(i128), -1); try testWrapSub(u8, 10, 3, 7); try testWrapSub(u8, 0, 255, 1); try testWrapSub(u5, 0, 31, 1); - try testWrapSub(u128, 0, maxInt(u128), 1); + if (!skip128) try testWrapSub(u128, 0, maxInt(u128), 1); } fn testWrapSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { @@ -79,7 +78,6 @@ test "wrapping subtraction" { } test "wrapping multiplication" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { @@ -90,11 +88,11 @@ test "wrapping multiplication" { try testWrapMul(i8, -128, -128, 0); try testWrapMul(i8, maxInt(i8), maxInt(i8), 1); try testWrapMul(i16, maxInt(i16), -1, minInt(i16) + 1); - try testWrapMul(i128, maxInt(i128), -1, minInt(i128) + 1); - try testWrapMul(i128, minInt(i128), -1, minInt(i128)); + if (!skip128) try testWrapMul(i128, maxInt(i128), -1, minInt(i128) + 1); + if (!skip128) try testWrapMul(i128, minInt(i128), -1, minInt(i128)); try testWrapMul(u8, 10, 3, 30); try testWrapMul(u8, 2, 255, 254); - try testWrapMul(u128, maxInt(u128), maxInt(u128), 1); + if (!skip128) try testWrapMul(u128, maxInt(u128), maxInt(u128), 1); } fn testWrapMul(comptime T: type, lhs: T, rhs: T, expected: T) !void {