| author | |
| committer | |
| log | 7140d08334de7d3c0c1342a0c2eb8a8db638b5df |
| tree | c031195d03ff3ce11e65a7342eab968a64767d3f |
| parent | f4c4daec863cf7394e141aefc5ad52e9c34f5979 |
- use logical ops for boolean bitwise instructions
- normalize strange-int results in airArithOp, airReduce, and airIntCast
Co-authored-by: Quint Daenen <quint@daenen.email>10 files changed, 62 insertions(+), 45 deletions(-)
src/codegen/spirv/CodeGen.zig+51-20| ... | ... | @@ -3016,9 +3016,9 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void { |
| 3016 | 3016 | .ptr_add => try cg.airPtrAdd(inst), |
| 3017 | 3017 | .ptr_sub => try cg.airPtrSub(inst), |
| 3018 | 3018 | |
| 3019 | .bit_and => try cg.airBinOpSimple(inst, .OpBitwiseAnd), | |
| 3020 | .bit_or => try cg.airBinOpSimple(inst, .OpBitwiseOr), | |
| 3021 | .xor => try cg.airBinOpSimple(inst, .OpBitwiseXor), | |
| 3019 | .bit_and => try cg.airBitwiseOp(inst, .bit_and), | |
| 3020 | .bit_or => try cg.airBitwiseOp(inst, .bit_or), | |
| 3021 | .xor => try cg.airBitwiseOp(inst, .xor), | |
| 3022 | 3022 | |
| 3023 | 3023 | .shl, .shl_exact => try cg.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical), |
| 3024 | 3024 | .shr, .shr_exact => try cg.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic), |
| ... | ... | @@ -3145,6 +3145,34 @@ fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: Opcode) !?Id { |
| 3145 | 3145 | return try result.materialize(cg); |
| 3146 | 3146 | } |
| 3147 | 3147 | |
| 3148 | const BitwiseOp = enum { bit_and, bit_or, xor }; | |
| 3149 | ||
| 3150 | fn airBitwiseOp(cg: *CodeGen, inst: Air.Inst.Index, op: BitwiseOp) !?Id { | |
| 3151 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | |
| 3152 | const lhs = try cg.temporary(bin_op.lhs); | |
| 3153 | const rhs = try cg.temporary(bin_op.rhs); | |
| 3154 | const info = cg.arithmeticTypeInfo(lhs.ty); | |
| 3155 | ||
| 3156 | // SPIR-V requires logical opcodes for booleans, bitwise opcodes for integers. | |
| 3157 | const opcode: Opcode = switch (info.class) { | |
| 3158 | .bool => switch (op) { | |
| 3159 | .bit_and => .OpLogicalAnd, | |
| 3160 | .bit_or => .OpLogicalOr, | |
| 3161 | .xor => .OpLogicalNotEqual, | |
| 3162 | }, | |
| 3163 | .integer, .strange_integer => switch (op) { | |
| 3164 | .bit_and => .OpBitwiseAnd, | |
| 3165 | .bit_or => .OpBitwiseOr, | |
| 3166 | .xor => .OpBitwiseXor, | |
| 3167 | }, | |
| 3168 | .float => unreachable, | |
| 3169 | .composite_integer => unreachable, // TODO | |
| 3170 | }; | |
| 3171 | ||
| 3172 | const result = try cg.buildBinary(opcode, lhs, rhs); | |
| 3173 | return try result.materialize(cg); | |
| 3174 | } | |
| 3175 | ||
| 3148 | 3176 | fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode) !?Id { |
| 3149 | 3177 | const zcu = cg.module.zcu; |
| 3150 | 3178 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| ... | ... | @@ -3389,9 +3417,12 @@ fn airArithOp( |
| 3389 | 3417 | const info = cg.arithmeticTypeInfo(lhs.ty); |
| 3390 | 3418 | const result = switch (info.class) { |
| 3391 | 3419 | .composite_integer => unreachable, // TODO |
| 3392 | .integer, .strange_integer => switch (info.signedness) { | |
| 3393 | .signed => try cg.buildBinary(sop, lhs, rhs), | |
| 3394 | .unsigned => try cg.buildBinary(uop, lhs, rhs), | |
| 3420 | .integer, .strange_integer => res: { | |
| 3421 | const raw = switch (info.signedness) { | |
| 3422 | .signed => try cg.buildBinary(sop, lhs, rhs), | |
| 3423 | .unsigned => try cg.buildBinary(uop, lhs, rhs), | |
| 3424 | }; | |
| 3425 | break :res try cg.normalize(raw, info); | |
| 3395 | 3426 | }, |
| 3396 | 3427 | .float => try cg.buildBinary(fop, lhs, rhs), |
| 3397 | 3428 | .bool => unreachable, |
| ... | ... | @@ -3766,7 +3797,6 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3766 | 3797 | const operand = try cg.resolve(reduce.operand); |
| 3767 | 3798 | const operand_ty = cg.typeOf(reduce.operand); |
| 3768 | 3799 | const scalar_ty = operand_ty.scalarType(zcu); |
| 3769 | const scalar_ty_id = try cg.resolveType(scalar_ty, .direct); | |
| 3770 | 3800 | const info = cg.arithmeticTypeInfo(operand_ty); |
| 3771 | 3801 | const len = operand_ty.vectorLen(zcu); |
| 3772 | 3802 | const first = try cg.extractVectorComponent(scalar_ty, operand, 0); |
| ... | ... | @@ -3792,8 +3822,6 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3792 | 3822 | else => {}, |
| 3793 | 3823 | } |
| 3794 | 3824 | |
| 3795 | var result_id = first; | |
| 3796 | ||
| 3797 | 3825 | const opcode: Opcode = switch (info.class) { |
| 3798 | 3826 | .bool => switch (reduce.operation) { |
| 3799 | 3827 | .And => .OpLogicalAnd, |
| ... | ... | @@ -3817,19 +3845,18 @@ fn airReduce(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 3817 | 3845 | .composite_integer => unreachable, // TODO |
| 3818 | 3846 | }; |
| 3819 | 3847 | |
| 3820 | for (1..len) |i| { | |
| 3821 | const lhs = result_id; | |
| 3822 | const rhs = try cg.extractVectorComponent(scalar_ty, operand, @intCast(i)); | |
| 3823 | result_id = cg.module.allocId(); | |
| 3848 | const needs_normalize = info.class == .strange_integer and | |
| 3849 | (reduce.operation == .Add or reduce.operation == .Mul); | |
| 3824 | 3850 | |
| 3825 | try cg.body.emitRaw(cg.module.gpa, opcode, 4); | |
| 3826 | cg.body.writeOperand(Id, scalar_ty_id); | |
| 3827 | cg.body.writeOperand(Id, result_id); | |
| 3828 | cg.body.writeOperand(Id, lhs); | |
| 3829 | cg.body.writeOperand(Id, rhs); | |
| 3851 | var result: Temporary = .init(scalar_ty, first); | |
| 3852 | for (1..len) |i| { | |
| 3853 | const rhs_id = try cg.extractVectorComponent(scalar_ty, operand, @intCast(i)); | |
| 3854 | const rhs: Temporary = .init(scalar_ty, rhs_id); | |
| 3855 | const stepped = try cg.buildBinary(opcode, result, rhs); | |
| 3856 | result = if (needs_normalize) try cg.normalize(stepped, info) else stepped; | |
| 3830 | 3857 | } |
| 3831 | 3858 | |
| 3832 | return result_id; | |
| 3859 | return try result.materialize(cg); | |
| 3833 | 3860 | } |
| 3834 | 3861 | |
| 3835 | 3862 | fn airShuffleOne(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| ... | ... | @@ -4314,7 +4341,11 @@ fn airIntCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 4314 | 4341 | const dst_info = cg.arithmeticTypeInfo(dst_ty); |
| 4315 | 4342 | |
| 4316 | 4343 | if (src_info.backing_bits == dst_info.backing_bits) { |
| 4317 | return try src.materialize(cg); | |
| 4344 | const result = if (dst_info.bits < src_info.bits) | |
| 4345 | try cg.normalize(src.pun(dst_ty), dst_info) | |
| 4346 | else | |
| 4347 | src.pun(dst_ty); | |
| 4348 | return try result.materialize(cg); | |
| 4318 | 4349 | } |
| 4319 | 4350 | |
| 4320 | 4351 | const converted = try cg.buildConvert(dst_ty, src); |
test/behavior/bool.zig-1| ... | ... | @@ -10,7 +10,6 @@ test "bool literals" { |
| 10 | 10 | |
| 11 | 11 | test "cast bool to int" { |
| 12 | 12 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 13 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 14 | 13 | |
| 15 | 14 | const t = true; |
| 16 | 15 | const f = false; |
test/behavior/cast_int.zig-2| ... | ... | @@ -19,7 +19,6 @@ test "@intCast i32 to u7" { |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | test "coerce i8 to i32 and @intCast back" { |
| 22 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 23 | 22 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 24 | 23 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 25 | 24 | |
| ... | ... | @@ -35,7 +34,6 @@ test "coerce i8 to i32 and @intCast back" { |
| 35 | 34 | } |
| 36 | 35 | |
| 37 | 36 | test "coerce non byte-sized integers accross 32bits boundary" { |
| 38 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 39 | 37 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 40 | 38 | |
| 41 | 39 | { |
test/behavior/duplicated_test_names.zig-1| ... | ... | @@ -15,7 +15,6 @@ comptime { |
| 15 | 15 | test "thingy" {} |
| 16 | 16 | |
| 17 | 17 | test thingy { |
| 18 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 19 | 18 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 20 | 19 | |
| 21 | 20 | if (thingy(1, 2) != 3) unreachable; |
test/behavior/hasdecl.zig-2| ... | ... | @@ -13,7 +13,6 @@ const Bar = struct { |
| 13 | 13 | |
| 14 | 14 | test "@hasDecl" { |
| 15 | 15 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 16 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 17 | 16 | |
| 18 | 17 | try expect(@hasDecl(Foo, "public_thing")); |
| 19 | 18 | try expect(!@hasDecl(Foo, "private_thing")); |
| ... | ... | @@ -26,7 +25,6 @@ test "@hasDecl" { |
| 26 | 25 | |
| 27 | 26 | test "@hasDecl using a sliced string literal" { |
| 28 | 27 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 29 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 30 | 28 | |
| 31 | 29 | try expect(@hasDecl(@This(), "std") == true); |
| 32 | 30 | try expect(@hasDecl(@This(), "std"[0..0]) == false); |
test/behavior/import.zig-3| ... | ... | @@ -5,21 +5,18 @@ const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const a_namespace = @import("import/a_namespace.zig"); |
| 6 | 6 | |
| 7 | 7 | test "call fn via namespace lookup" { |
| 8 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 9 | 8 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 10 | 9 | |
| 11 | 10 | try expect(@as(i32, 1234) == a_namespace.foo()); |
| 12 | 11 | } |
| 13 | 12 | |
| 14 | 13 | test "importing the same thing gives the same import" { |
| 15 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 16 | 14 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 17 | 15 | |
| 18 | 16 | try expect(@import("std") == @import("std")); |
| 19 | 17 | } |
| 20 | 18 | |
| 21 | 19 | test "import empty file" { |
| 22 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 23 | 20 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 24 | 21 | |
| 25 | 22 | _ = @import("import/empty.zig"); |
test/behavior/ir_block_deps.zig+1-1| ... | ... | @@ -19,8 +19,8 @@ fn getErrInt() anyerror!i32 { |
| 19 | 19 | |
| 20 | 20 | test "ir block deps" { |
| 21 | 21 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 22 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | |
| 23 | 22 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 23 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | |
| 24 | 24 | |
| 25 | 25 | try expect((foo(1) catch unreachable) == 0); |
| 26 | 26 | try expect((foo(2) catch unreachable) == 0); |
test/behavior/namespace_depends_on_compile_var.zig-1| ... | ... | @@ -3,7 +3,6 @@ const builtin = @import("builtin"); |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | |
| 5 | 5 | test "namespace depends on compile var" { |
| 6 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 7 | 6 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 8 | 7 | |
| 9 | 8 | if (some_namespace.a_bool) { |
test/behavior/pub_enum.zig-2| ... | ... | @@ -3,7 +3,6 @@ const other = @import("pub_enum/other.zig"); |
| 3 | 3 | const expect = @import("std").testing.expect; |
| 4 | 4 | |
| 5 | 5 | test "pub enum" { |
| 6 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 7 | 6 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 8 | 7 | |
| 9 | 8 | try pubEnumTest(other.APubEnum.Two); |
| ... | ... | @@ -13,7 +12,6 @@ fn pubEnumTest(foo: other.APubEnum) !void { |
| 13 | 12 | } |
| 14 | 13 | |
| 15 | 14 | test "cast with imported symbol" { |
| 16 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 17 | 15 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 18 | 16 | |
| 19 | 17 | try expect(@as(other.size_t, 42) == 42); |
test/behavior/wrapping_arithmetic.zig+10-12| ... | ... | @@ -3,9 +3,9 @@ const builtin = @import("builtin"); |
| 3 | 3 | const minInt = std.math.minInt; |
| 4 | 4 | const maxInt = std.math.maxInt; |
| 5 | 5 | const expect = std.testing.expect; |
| 6 | const skip128 = builtin.zig_backend == .stage2_spirv; | |
| 6 | 7 | |
| 7 | 8 | test "wrapping add" { |
| 8 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 9 | 9 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 10 | 10 | |
| 11 | 11 | const S = struct { |
| ... | ... | @@ -14,14 +14,14 @@ test "wrapping add" { |
| 14 | 14 | try testWrapAdd(i8, -128, -128, 0); |
| 15 | 15 | try testWrapAdd(i2, 1, 1, -2); |
| 16 | 16 | try testWrapAdd(i64, maxInt(i64), 1, minInt(i64)); |
| 17 | try testWrapAdd(i128, maxInt(i128), -maxInt(i128), 0); | |
| 18 | try testWrapAdd(i128, minInt(i128), maxInt(i128), -1); | |
| 17 | if (!skip128) try testWrapAdd(i128, maxInt(i128), -maxInt(i128), 0); | |
| 18 | if (!skip128) try testWrapAdd(i128, minInt(i128), maxInt(i128), -1); | |
| 19 | 19 | try testWrapAdd(i8, 127, 127, -2); |
| 20 | 20 | try testWrapAdd(u8, 3, 10, 13); |
| 21 | 21 | try testWrapAdd(u8, 255, 255, 254); |
| 22 | 22 | try testWrapAdd(u2, 3, 2, 1); |
| 23 | 23 | try testWrapAdd(u3, 7, 1, 0); |
| 24 | try testWrapAdd(u128, maxInt(u128), 1, minInt(u128)); | |
| 24 | if (!skip128) try testWrapAdd(u128, maxInt(u128), 1, minInt(u128)); | |
| 25 | 25 | } |
| 26 | 26 | |
| 27 | 27 | fn testWrapAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void { |
| ... | ... | @@ -43,7 +43,6 @@ test "wrapping add" { |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | 45 | test "wrapping subtraction" { |
| 46 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 47 | 46 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 48 | 47 | |
| 49 | 48 | const S = struct { |
| ... | ... | @@ -52,12 +51,12 @@ test "wrapping subtraction" { |
| 52 | 51 | try testWrapSub(i8, -128, -128, 0); |
| 53 | 52 | try testWrapSub(i8, -1, 127, -128); |
| 54 | 53 | try testWrapSub(i64, minInt(i64), 1, maxInt(i64)); |
| 55 | try testWrapSub(i128, maxInt(i128), -1, minInt(i128)); | |
| 56 | try testWrapSub(i128, minInt(i128), -maxInt(i128), -1); | |
| 54 | if (!skip128) try testWrapSub(i128, maxInt(i128), -1, minInt(i128)); | |
| 55 | if (!skip128) try testWrapSub(i128, minInt(i128), -maxInt(i128), -1); | |
| 57 | 56 | try testWrapSub(u8, 10, 3, 7); |
| 58 | 57 | try testWrapSub(u8, 0, 255, 1); |
| 59 | 58 | try testWrapSub(u5, 0, 31, 1); |
| 60 | try testWrapSub(u128, 0, maxInt(u128), 1); | |
| 59 | if (!skip128) try testWrapSub(u128, 0, maxInt(u128), 1); | |
| 61 | 60 | } |
| 62 | 61 | |
| 63 | 62 | fn testWrapSub(comptime T: type, lhs: T, rhs: T, expected: T) !void { |
| ... | ... | @@ -79,7 +78,6 @@ test "wrapping subtraction" { |
| 79 | 78 | } |
| 80 | 79 | |
| 81 | 80 | test "wrapping multiplication" { |
| 82 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | |
| 83 | 81 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 84 | 82 | |
| 85 | 83 | const S = struct { |
| ... | ... | @@ -90,11 +88,11 @@ test "wrapping multiplication" { |
| 90 | 88 | try testWrapMul(i8, -128, -128, 0); |
| 91 | 89 | try testWrapMul(i8, maxInt(i8), maxInt(i8), 1); |
| 92 | 90 | try testWrapMul(i16, maxInt(i16), -1, minInt(i16) + 1); |
| 93 | try testWrapMul(i128, maxInt(i128), -1, minInt(i128) + 1); | |
| 94 | try testWrapMul(i128, minInt(i128), -1, minInt(i128)); | |
| 91 | if (!skip128) try testWrapMul(i128, maxInt(i128), -1, minInt(i128) + 1); | |
| 92 | if (!skip128) try testWrapMul(i128, minInt(i128), -1, minInt(i128)); | |
| 95 | 93 | try testWrapMul(u8, 10, 3, 30); |
| 96 | 94 | try testWrapMul(u8, 2, 255, 254); |
| 97 | try testWrapMul(u128, maxInt(u128), maxInt(u128), 1); | |
| 95 | if (!skip128) try testWrapMul(u128, maxInt(u128), maxInt(u128), 1); | |
| 98 | 96 | } |
| 99 | 97 | |
| 100 | 98 | fn testWrapMul(comptime T: type, lhs: T, rhs: T, expected: T) !void { |