| author | |
| committer | |
| log | 09d93ec845f2f1adaefc512fccaeaa0ea8beed61 |
| tree | 4035e1593a535d6d0a1bea5d61b19cfe43a351fd |
| parent | dee96e2e2f464c3b8edc8ec3a63cd3b1860e3a9d |
| parent | db80dff4e002146063609d20599a7310837074c7 |
| signature |
stage2 llvm: Implement `@bitReverse`, `@byteSwap` built-ins17 files changed, 504 insertions(+), 21 deletions(-)
lib/std/math/big/int.zig+126| ... | @@ -745,6 +745,132 @@ pub const Mutable = struct { | ... | @@ -745,6 +745,132 @@ pub const Mutable = struct { |
| 745 | rma.truncate(rma.toConst(), signedness, bit_count); | 745 | rma.truncate(rma.toConst(), signedness, bit_count); |
| 746 | } | 746 | } |
| 747 | 747 | ||
| 748 | /// r = @bitReverse(a) with 2s-complement semantics. | ||
| 749 | /// r and a may be aliases. | ||
| 750 | /// | ||
| 751 | /// Asserts the result fits in `r`. Upper bound on the number of limbs needed by | ||
| 752 | /// r is `calcTwosCompLimbCount(bit_count)`. | ||
| 753 | pub fn bitReverse(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void { | ||
| 754 | if (bit_count == 0) return; | ||
| 755 | |||
| 756 | r.copy(a); | ||
| 757 | |||
| 758 | const limbs_required = calcTwosCompLimbCount(bit_count); | ||
| 759 | |||
| 760 | if (!a.positive) { | ||
| 761 | r.positive = true; // Negate. | ||
| 762 | r.bitNotWrap(r.toConst(), .unsigned, bit_count); // Bitwise NOT. | ||
| 763 | r.addScalar(r.toConst(), 1); // Add one. | ||
| 764 | } else if (limbs_required > a.limbs.len) { | ||
| 765 | // Zero-extend to our output length | ||
| 766 | for (r.limbs[a.limbs.len..limbs_required]) |*limb| { | ||
| 767 | limb.* = 0; | ||
| 768 | } | ||
| 769 | r.len = limbs_required; | ||
| 770 | } | ||
| 771 | |||
| 772 | // 0b0..01..1000 with @log2(@sizeOf(Limb)) consecutive ones | ||
| 773 | const endian_mask: usize = (@sizeOf(Limb) - 1) << 3; | ||
| 774 | |||
| 775 | var bytes = std.mem.sliceAsBytes(r.limbs); | ||
| 776 | var bits = std.packed_int_array.PackedIntSliceEndian(u1, .Little).init(bytes, limbs_required * @bitSizeOf(Limb)); | ||
| 777 | |||
| 778 | var k: usize = 0; | ||
| 779 | while (k < ((bit_count + 1) / 2)) : (k += 1) { | ||
| 780 | var i = k; | ||
| 781 | var rev_i = bit_count - i - 1; | ||
| 782 | |||
| 783 | // This "endian mask" remaps a low (LE) byte to the corresponding high | ||
| 784 | // (BE) byte in the Limb, without changing which limbs we are indexing | ||
| 785 | if (native_endian == .Big) { | ||
| 786 | i ^= endian_mask; | ||
| 787 | rev_i ^= endian_mask; | ||
| 788 | } | ||
| 789 | |||
| 790 | const bit_i = bits.get(i); | ||
| 791 | const bit_rev_i = bits.get(rev_i); | ||
| 792 | bits.set(i, bit_rev_i); | ||
| 793 | bits.set(rev_i, bit_i); | ||
| 794 | } | ||
| 795 | |||
| 796 | // Calculate signed-magnitude representation for output | ||
| 797 | if (signedness == .signed) { | ||
| 798 | const last_bit = switch (native_endian) { | ||
| 799 | .Little => bits.get(bit_count - 1), | ||
| 800 | .Big => bits.get((bit_count - 1) ^ endian_mask), | ||
| 801 | }; | ||
| 802 | if (last_bit == 1) { | ||
| 803 | r.bitNotWrap(r.toConst(), .unsigned, bit_count); // Bitwise NOT. | ||
| 804 | r.addScalar(r.toConst(), 1); // Add one. | ||
| 805 | r.positive = false; // Negate. | ||
| 806 | } | ||
| 807 | } | ||
| 808 | r.normalize(r.len); | ||
| 809 | } | ||
| 810 | |||
| 811 | /// r = @byteSwap(a) with 2s-complement semantics. | ||
| 812 | /// r and a may be aliases. | ||
| 813 | /// | ||
| 814 | /// Asserts the result fits in `r`. Upper bound on the number of limbs needed by | ||
| 815 | /// r is `calcTwosCompLimbCount(8*byte_count)`. | ||
| 816 | pub fn byteSwap(r: *Mutable, a: Const, signedness: Signedness, byte_count: usize) void { | ||
| 817 | if (byte_count == 0) return; | ||
| 818 | |||
| 819 | r.copy(a); | ||
| 820 | const limbs_required = calcTwosCompLimbCount(8 * byte_count); | ||
| 821 | |||
| 822 | if (!a.positive) { | ||
| 823 | r.positive = true; // Negate. | ||
| 824 | r.bitNotWrap(r.toConst(), .unsigned, 8 * byte_count); // Bitwise NOT. | ||
| 825 | r.addScalar(r.toConst(), 1); // Add one. | ||
| 826 | } else if (limbs_required > a.limbs.len) { | ||
| 827 | // Zero-extend to our output length | ||
| 828 | for (r.limbs[a.limbs.len..limbs_required]) |*limb| { | ||
| 829 | limb.* = 0; | ||
| 830 | } | ||
| 831 | r.len = limbs_required; | ||
| 832 | } | ||
| 833 | |||
| 834 | // 0b0..01..1 with @log2(@sizeOf(Limb)) trailing ones | ||
| 835 | const endian_mask: usize = @sizeOf(Limb) - 1; | ||
| 836 | |||
| 837 | var bytes = std.mem.sliceAsBytes(r.limbs); | ||
| 838 | assert(bytes.len >= byte_count); | ||
| 839 | |||
| 840 | var k: usize = 0; | ||
| 841 | while (k < (byte_count + 1) / 2) : (k += 1) { | ||
| 842 | var i = k; | ||
| 843 | var rev_i = byte_count - k - 1; | ||
| 844 | |||
| 845 | // This "endian mask" remaps a low (LE) byte to the corresponding high | ||
| 846 | // (BE) byte in the Limb, without changing which limbs we are indexing | ||
| 847 | if (native_endian == .Big) { | ||
| 848 | i ^= endian_mask; | ||
| 849 | rev_i ^= endian_mask; | ||
| 850 | } | ||
| 851 | |||
| 852 | const byte_i = bytes[i]; | ||
| 853 | const byte_rev_i = bytes[rev_i]; | ||
| 854 | bytes[rev_i] = byte_i; | ||
| 855 | bytes[i] = byte_rev_i; | ||
| 856 | } | ||
| 857 | |||
| 858 | // Calculate signed-magnitude representation for output | ||
| 859 | if (signedness == .signed) { | ||
| 860 | const last_byte = switch (native_endian) { | ||
| 861 | .Little => bytes[byte_count - 1], | ||
| 862 | .Big => bytes[(byte_count - 1) ^ endian_mask], | ||
| 863 | }; | ||
| 864 | |||
| 865 | if (last_byte & (1 << 7) != 0) { // Check sign bit of last byte | ||
| 866 | r.bitNotWrap(r.toConst(), .unsigned, 8 * byte_count); // Bitwise NOT. | ||
| 867 | r.addScalar(r.toConst(), 1); // Add one. | ||
| 868 | r.positive = false; // Negate. | ||
| 869 | } | ||
| 870 | } | ||
| 871 | r.normalize(r.len); | ||
| 872 | } | ||
| 873 | |||
| 748 | /// r = @popCount(a) with 2s-complement semantics. | 874 | /// r = @popCount(a) with 2s-complement semantics. |
| 749 | /// r and a may be aliases. | 875 | /// r and a may be aliases. |
| 750 | /// | 876 | /// |
lib/std/math/big/int_test.zig+105| ... | @@ -7,6 +7,7 @@ const Limb = std.math.big.Limb; | ... | @@ -7,6 +7,7 @@ const Limb = std.math.big.Limb; |
| 7 | const SignedLimb = std.math.big.SignedLimb; | 7 | const SignedLimb = std.math.big.SignedLimb; |
| 8 | const DoubleLimb = std.math.big.DoubleLimb; | 8 | const DoubleLimb = std.math.big.DoubleLimb; |
| 9 | const SignedDoubleLimb = std.math.big.SignedDoubleLimb; | 9 | const SignedDoubleLimb = std.math.big.SignedDoubleLimb; |
| 10 | const calcTwosCompLimbCount = std.math.big.int.calcTwosCompLimbCount; | ||
| 10 | const maxInt = std.math.maxInt; | 11 | const maxInt = std.math.maxInt; |
| 11 | const minInt = std.math.minInt; | 12 | const minInt = std.math.minInt; |
| 12 | 13 | ||
| ... | @@ -2689,3 +2690,107 @@ test "big int conversion write twos complement zero" { | ... | @@ -2689,3 +2690,107 @@ test "big int conversion write twos complement zero" { |
| 2689 | m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned); | 2690 | m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned); |
| 2690 | try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); | 2691 | try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); |
| 2691 | } | 2692 | } |
| 2693 | |||
| 2694 | fn bitReverseTest(comptime T: type, comptime input: comptime_int, comptime expected_output: comptime_int) !void { | ||
| 2695 | const bit_count = @typeInfo(T).Int.bits; | ||
| 2696 | const signedness = @typeInfo(T).Int.signedness; | ||
| 2697 | |||
| 2698 | var a = try Managed.initSet(testing.allocator, input); | ||
| 2699 | defer a.deinit(); | ||
| 2700 | |||
| 2701 | try a.ensureCapacity(calcTwosCompLimbCount(bit_count)); | ||
| 2702 | var m = a.toMutable(); | ||
| 2703 | m.bitReverse(a.toConst(), signedness, bit_count); | ||
| 2704 | try testing.expect(m.toConst().orderAgainstScalar(expected_output) == .eq); | ||
| 2705 | } | ||
| 2706 | |||
| 2707 | test "big int bit reverse" { | ||
| 2708 | var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff); | ||
| 2709 | defer a.deinit(); | ||
| 2710 | |||
| 2711 | try bitReverseTest(u0, 0, 0); | ||
| 2712 | try bitReverseTest(u5, 0x12, 0x09); | ||
| 2713 | try bitReverseTest(u8, 0x12, 0x48); | ||
| 2714 | try bitReverseTest(u16, 0x1234, 0x2c48); | ||
| 2715 | try bitReverseTest(u24, 0x123456, 0x6a2c48); | ||
| 2716 | try bitReverseTest(u32, 0x12345678, 0x1e6a2c48); | ||
| 2717 | try bitReverseTest(u40, 0x123456789a, 0x591e6a2c48); | ||
| 2718 | try bitReverseTest(u48, 0x123456789abc, 0x3d591e6a2c48); | ||
| 2719 | try bitReverseTest(u56, 0x123456789abcde, 0x7b3d591e6a2c48); | ||
| 2720 | try bitReverseTest(u64, 0x123456789abcdef1, 0x8f7b3d591e6a2c48); | ||
| 2721 | try bitReverseTest(u95, 0x123456789abcdef111213141, 0x4146424447bd9eac8f351624); | ||
| 2722 | try bitReverseTest(u96, 0x123456789abcdef111213141, 0x828c84888f7b3d591e6a2c48); | ||
| 2723 | try bitReverseTest(u128, 0x123456789abcdef11121314151617181, 0x818e868a828c84888f7b3d591e6a2c48); | ||
| 2724 | |||
| 2725 | try bitReverseTest(i8, @bitCast(i8, @as(u8, 0x92)), @bitCast(i8, @as(u8, 0x49))); | ||
| 2726 | try bitReverseTest(i16, @bitCast(i16, @as(u16, 0x1234)), @bitCast(i16, @as(u16, 0x2c48))); | ||
| 2727 | try bitReverseTest(i24, @bitCast(i24, @as(u24, 0x123456)), @bitCast(i24, @as(u24, 0x6a2c48))); | ||
| 2728 | try bitReverseTest(i24, @bitCast(i24, @as(u24, 0x12345f)), @bitCast(i24, @as(u24, 0xfa2c48))); | ||
| 2729 | try bitReverseTest(i24, @bitCast(i24, @as(u24, 0xf23456)), @bitCast(i24, @as(u24, 0x6a2c4f))); | ||
| 2730 | try bitReverseTest(i32, @bitCast(i32, @as(u32, 0x12345678)), @bitCast(i32, @as(u32, 0x1e6a2c48))); | ||
| 2731 | try bitReverseTest(i32, @bitCast(i32, @as(u32, 0xf2345678)), @bitCast(i32, @as(u32, 0x1e6a2c4f))); | ||
| 2732 | try bitReverseTest(i32, @bitCast(i32, @as(u32, 0x1234567f)), @bitCast(i32, @as(u32, 0xfe6a2c48))); | ||
| 2733 | try bitReverseTest(i40, @bitCast(i40, @as(u40, 0x123456789a)), @bitCast(i40, @as(u40, 0x591e6a2c48))); | ||
| 2734 | try bitReverseTest(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0x3d591e6a2c48))); | ||
| 2735 | try bitReverseTest(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0x7b3d591e6a2c48))); | ||
| 2736 | try bitReverseTest(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0x8f7b3d591e6a2c48))); | ||
| 2737 | try bitReverseTest(i96, @bitCast(i96, @as(u96, 0x123456789abcdef111213141)), @bitCast(i96, @as(u96, 0x828c84888f7b3d591e6a2c48))); | ||
| 2738 | try bitReverseTest(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)), @bitCast(i128, @as(u128, 0x818e868a828c84888f7b3d591e6a2c48))); | ||
| 2739 | } | ||
| 2740 | |||
| 2741 | fn byteSwapTest(comptime T: type, comptime input: comptime_int, comptime expected_output: comptime_int) !void { | ||
| 2742 | const byte_count = @typeInfo(T).Int.bits / 8; | ||
| 2743 | const signedness = @typeInfo(T).Int.signedness; | ||
| 2744 | |||
| 2745 | var a = try Managed.initSet(testing.allocator, input); | ||
| 2746 | defer a.deinit(); | ||
| 2747 | |||
| 2748 | try a.ensureCapacity(calcTwosCompLimbCount(8 * byte_count)); | ||
| 2749 | var m = a.toMutable(); | ||
| 2750 | m.byteSwap(a.toConst(), signedness, byte_count); | ||
| 2751 | try testing.expect(m.toConst().orderAgainstScalar(expected_output) == .eq); | ||
| 2752 | } | ||
| 2753 | |||
| 2754 | test "big int byte swap" { | ||
| 2755 | var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff); | ||
| 2756 | defer a.deinit(); | ||
| 2757 | |||
| 2758 | @setEvalBranchQuota(10_000); | ||
| 2759 | |||
| 2760 | try byteSwapTest(u0, 0, 0); | ||
| 2761 | try byteSwapTest(u8, 0x12, 0x12); | ||
| 2762 | try byteSwapTest(u16, 0x1234, 0x3412); | ||
| 2763 | try byteSwapTest(u24, 0x123456, 0x563412); | ||
| 2764 | try byteSwapTest(u32, 0x12345678, 0x78563412); | ||
| 2765 | try byteSwapTest(u40, 0x123456789a, 0x9a78563412); | ||
| 2766 | try byteSwapTest(u48, 0x123456789abc, 0xbc9a78563412); | ||
| 2767 | try byteSwapTest(u56, 0x123456789abcde, 0xdebc9a78563412); | ||
| 2768 | try byteSwapTest(u64, 0x123456789abcdef1, 0xf1debc9a78563412); | ||
| 2769 | try byteSwapTest(u88, 0x123456789abcdef1112131, 0x312111f1debc9a78563412); | ||
| 2770 | try byteSwapTest(u96, 0x123456789abcdef111213141, 0x41312111f1debc9a78563412); | ||
| 2771 | try byteSwapTest(u128, 0x123456789abcdef11121314151617181, 0x8171615141312111f1debc9a78563412); | ||
| 2772 | |||
| 2773 | try byteSwapTest(i8, -50, -50); | ||
| 2774 | try byteSwapTest(i16, @bitCast(i16, @as(u16, 0x1234)), @bitCast(i16, @as(u16, 0x3412))); | ||
| 2775 | try byteSwapTest(i24, @bitCast(i24, @as(u24, 0x123456)), @bitCast(i24, @as(u24, 0x563412))); | ||
| 2776 | try byteSwapTest(i32, @bitCast(i32, @as(u32, 0x12345678)), @bitCast(i32, @as(u32, 0x78563412))); | ||
| 2777 | try byteSwapTest(i40, @bitCast(i40, @as(u40, 0x123456789a)), @bitCast(i40, @as(u40, 0x9a78563412))); | ||
| 2778 | try byteSwapTest(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0xbc9a78563412))); | ||
| 2779 | try byteSwapTest(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0xdebc9a78563412))); | ||
| 2780 | try byteSwapTest(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0xf1debc9a78563412))); | ||
| 2781 | try byteSwapTest(i88, @bitCast(i88, @as(u88, 0x123456789abcdef1112131)), @bitCast(i88, @as(u88, 0x312111f1debc9a78563412))); | ||
| 2782 | try byteSwapTest(i96, @bitCast(i96, @as(u96, 0x123456789abcdef111213141)), @bitCast(i96, @as(u96, 0x41312111f1debc9a78563412))); | ||
| 2783 | try byteSwapTest(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)), @bitCast(i128, @as(u128, 0x8171615141312111f1debc9a78563412))); | ||
| 2784 | |||
| 2785 | try byteSwapTest(u512, 0x80, 1 << 511); | ||
| 2786 | try byteSwapTest(i512, 0x80, minInt(i512)); | ||
| 2787 | try byteSwapTest(i512, 0x40, 1 << 510); | ||
| 2788 | try byteSwapTest(i512, -0x100, (1 << 504) - 1); | ||
| 2789 | try byteSwapTest(i400, -0x100, (1 << 392) - 1); | ||
| 2790 | try byteSwapTest(i400, -0x2, -(1 << 392) - 1); | ||
| 2791 | try byteSwapTest(i24, @bitCast(i24, @as(u24, 0xf23456)), 0x5634f2); | ||
| 2792 | try byteSwapTest(i24, 0x1234f6, @bitCast(i24, @as(u24, 0xf63412))); | ||
| 2793 | try byteSwapTest(i32, @bitCast(i32, @as(u32, 0xf2345678)), 0x785634f2); | ||
| 2794 | try byteSwapTest(i32, 0x123456f8, @bitCast(i32, @as(u32, 0xf8563412))); | ||
| 2795 | try byteSwapTest(i48, 0x123456789abc, @bitCast(i48, @as(u48, 0xbc9a78563412))); | ||
| 2796 | } |
src/Air.zig+8| ... | @@ -236,6 +236,12 @@ pub const Inst = struct { | ... | @@ -236,6 +236,12 @@ pub const Inst = struct { |
| 236 | /// Result type will always be an unsigned integer big enough to fit the answer. | 236 | /// Result type will always be an unsigned integer big enough to fit the answer. |
| 237 | /// Uses the `ty_op` field. | 237 | /// Uses the `ty_op` field. |
| 238 | popcount, | 238 | popcount, |
| 239 | /// Reverse the bytes in an integer according to its representation in twos complement. | ||
| 240 | /// Uses the `ty_op` field. | ||
| 241 | byte_swap, | ||
| 242 | /// Reverse the bits in an integer according to its representation in twos complement. | ||
| 243 | /// Uses the `ty_op` field. | ||
| 244 | bit_reverse, | ||
| 239 | 245 | ||
| 240 | /// Square root of a floating point number. | 246 | /// Square root of a floating point number. |
| 241 | /// Uses the `un_op` field. | 247 | /// Uses the `un_op` field. |
| ... | @@ -874,6 +880,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -874,6 +880,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 874 | .clz, | 880 | .clz, |
| 875 | .ctz, | 881 | .ctz, |
| 876 | .popcount, | 882 | .popcount, |
| 883 | .byte_swap, | ||
| 884 | .bit_reverse, | ||
| 877 | => return air.getRefType(datas[inst].ty_op.ty), | 885 | => return air.getRefType(datas[inst].ty_op.ty), |
| 878 | 886 | ||
| 879 | .loop, | 887 | .loop, |
src/Liveness.zig+2| ... | @@ -318,6 +318,8 @@ fn analyzeInst( | ... | @@ -318,6 +318,8 @@ fn analyzeInst( |
| 318 | .clz, | 318 | .clz, |
| 319 | .ctz, | 319 | .ctz, |
| 320 | .popcount, | 320 | .popcount, |
| 321 | .byte_swap, | ||
| 322 | .bit_reverse, | ||
| 321 | .splat, | 323 | .splat, |
| 322 | => { | 324 | => { |
| 323 | const o = inst_datas[inst].ty_op; | 325 | const o = inst_datas[inst].ty_op; |
src/Sema.zig+50-4| ... | @@ -11701,14 +11701,60 @@ fn zirBitCount( | ... | @@ -11701,14 +11701,60 @@ fn zirBitCount( |
| 11701 | 11701 | ||
| 11702 | fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 11702 | fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 11703 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 11703 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 11704 | const src = inst_data.src(); | 11704 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 11705 | return sema.fail(block, src, "TODO: Sema.zirByteSwap", .{}); | 11705 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 11706 | const operand = sema.resolveInst(inst_data.operand); | ||
| 11707 | const operand_ty = sema.typeOf(operand); | ||
| 11708 | // TODO implement support for vectors | ||
| 11709 | if (operand_ty.zigTypeTag() != .Int) { | ||
| 11710 | return sema.fail(block, ty_src, "expected integer type, found '{}'", .{ | ||
| 11711 | operand_ty, | ||
| 11712 | }); | ||
| 11713 | } | ||
| 11714 | const target = sema.mod.getTarget(); | ||
| 11715 | const bits = operand_ty.intInfo(target).bits; | ||
| 11716 | if (bits == 0) return Air.Inst.Ref.zero; | ||
| 11717 | if (operand_ty.intInfo(target).bits % 8 != 0) { | ||
| 11718 | return sema.fail(block, ty_src, "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", .{ | ||
| 11719 | operand_ty, | ||
| 11720 | operand_ty.intInfo(target).bits, | ||
| 11721 | }); | ||
| 11722 | } | ||
| 11723 | |||
| 11724 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | ||
| 11725 | if (val.isUndef()) return sema.addConstUndef(operand_ty); | ||
| 11726 | const result_val = try val.byteSwap(operand_ty, target, sema.arena); | ||
| 11727 | return sema.addConstant(operand_ty, result_val); | ||
| 11728 | } else operand_src; | ||
| 11729 | |||
| 11730 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 11731 | return block.addTyOp(.byte_swap, operand_ty, operand); | ||
| 11706 | } | 11732 | } |
| 11707 | 11733 | ||
| 11708 | fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 11734 | fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 11709 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 11735 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 11710 | const src = inst_data.src(); | 11736 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 11711 | return sema.fail(block, src, "TODO: Sema.zirBitReverse", .{}); | 11737 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 11738 | const operand = sema.resolveInst(inst_data.operand); | ||
| 11739 | const operand_ty = sema.typeOf(operand); | ||
| 11740 | // TODO implement support for vectors | ||
| 11741 | if (operand_ty.zigTypeTag() != .Int) { | ||
| 11742 | return sema.fail(block, ty_src, "expected integer type, found '{}'", .{ | ||
| 11743 | operand_ty, | ||
| 11744 | }); | ||
| 11745 | } | ||
| 11746 | const target = sema.mod.getTarget(); | ||
| 11747 | const bits = operand_ty.intInfo(target).bits; | ||
| 11748 | if (bits == 0) return Air.Inst.Ref.zero; | ||
| 11749 | |||
| 11750 | const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | ||
| 11751 | if (val.isUndef()) return sema.addConstUndef(operand_ty); | ||
| 11752 | const result_val = try val.bitReverse(operand_ty, target, sema.arena); | ||
| 11753 | return sema.addConstant(operand_ty, result_val); | ||
| 11754 | } else operand_src; | ||
| 11755 | |||
| 11756 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 11757 | return block.addTyOp(.bit_reverse, operand_ty, operand); | ||
| 11712 | } | 11758 | } |
| 11713 | 11759 | ||
| 11714 | fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 11760 | fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
src/arch/aarch64/CodeGen.zig+14| ... | @@ -621,6 +621,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -621,6 +621,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 621 | .clz => try self.airClz(inst), | 621 | .clz => try self.airClz(inst), |
| 622 | .ctz => try self.airCtz(inst), | 622 | .ctz => try self.airCtz(inst), |
| 623 | .popcount => try self.airPopcount(inst), | 623 | .popcount => try self.airPopcount(inst), |
| 624 | .byte_swap => try self.airByteSwap(inst), | ||
| 625 | .bit_reverse => try self.airBitReverse(inst), | ||
| 624 | .tag_name => try self.airTagName(inst), | 626 | .tag_name => try self.airTagName(inst), |
| 625 | .error_name => try self.airErrorName(inst), | 627 | .error_name => try self.airErrorName(inst), |
| 626 | .splat => try self.airSplat(inst), | 628 | .splat => try self.airSplat(inst), |
| ... | @@ -1682,6 +1684,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1682,6 +1684,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { |
| 1682 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1684 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1683 | } | 1685 | } |
| 1684 | 1686 | ||
| 1687 | fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1688 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1689 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); | ||
| 1690 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1694 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1695 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); | ||
| 1696 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1697 | } | ||
| 1698 | |||
| 1685 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { | 1699 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 1686 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1700 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1687 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1701 | const result: MCValue = if (self.liveness.isUnused(inst)) |
src/arch/arm/CodeGen.zig+16| ... | @@ -605,6 +605,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -605,6 +605,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 605 | .clz => try self.airClz(inst), | 605 | .clz => try self.airClz(inst), |
| 606 | .ctz => try self.airCtz(inst), | 606 | .ctz => try self.airCtz(inst), |
| 607 | .popcount => try self.airPopcount(inst), | 607 | .popcount => try self.airPopcount(inst), |
| 608 | .byte_swap => try self.airByteSwap(inst), | ||
| 609 | .bit_reverse => try self.airBitReverse(inst), | ||
| 608 | .tag_name => try self.airTagName(inst), | 610 | .tag_name => try self.airTagName(inst), |
| 609 | .error_name => try self.airErrorName(inst), | 611 | .error_name => try self.airErrorName(inst), |
| 610 | .splat => try self.airSplat(inst), | 612 | .splat => try self.airSplat(inst), |
| ... | @@ -1392,6 +1394,20 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1392,6 +1394,20 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { |
| 1392 | // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1394 | // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1393 | } | 1395 | } |
| 1394 | 1396 | ||
| 1397 | fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1398 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1399 | _ = ty_op; | ||
| 1400 | return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); | ||
| 1401 | // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1402 | } | ||
| 1403 | |||
| 1404 | fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1405 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1406 | _ = ty_op; | ||
| 1407 | return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); | ||
| 1408 | // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1409 | } | ||
| 1410 | |||
| 1395 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { | 1411 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 1396 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1412 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1397 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1413 | const result: MCValue = if (self.liveness.isUnused(inst)) |
src/arch/riscv64/CodeGen.zig+14| ... | @@ -592,6 +592,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -592,6 +592,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 592 | .clz => try self.airClz(inst), | 592 | .clz => try self.airClz(inst), |
| 593 | .ctz => try self.airCtz(inst), | 593 | .ctz => try self.airCtz(inst), |
| 594 | .popcount => try self.airPopcount(inst), | 594 | .popcount => try self.airPopcount(inst), |
| 595 | .byte_swap => try self.airByteSwap(inst), | ||
| 596 | .bit_reverse => try self.airBitReverse(inst), | ||
| 595 | .tag_name => try self.airTagName(inst), | 597 | .tag_name => try self.airTagName(inst), |
| 596 | .error_name => try self.airErrorName(inst), | 598 | .error_name => try self.airErrorName(inst), |
| 597 | .splat => try self.airSplat(inst), | 599 | .splat => try self.airSplat(inst), |
| ... | @@ -1181,6 +1183,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1181,6 +1183,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { |
| 1181 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1183 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1182 | } | 1184 | } |
| 1183 | 1185 | ||
| 1186 | fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1187 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1188 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); | ||
| 1189 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1193 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1194 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); | ||
| 1195 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1196 | } | ||
| 1197 | |||
| 1184 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { | 1198 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 1185 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1199 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1186 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1200 | const result: MCValue = if (self.liveness.isUnused(inst)) |
src/arch/wasm/CodeGen.zig+2| ... | @@ -1687,6 +1687,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1687,6 +1687,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1687 | .clz, | 1687 | .clz, |
| 1688 | .ctz, | 1688 | .ctz, |
| 1689 | .popcount, | 1689 | .popcount, |
| 1690 | .byte_swap, | ||
| 1691 | .bit_reverse, | ||
| 1690 | .is_err_ptr, | 1692 | .is_err_ptr, |
| 1691 | .is_non_err_ptr, | 1693 | .is_non_err_ptr, |
| 1692 | .fptrunc, | 1694 | .fptrunc, |
src/arch/x86_64/CodeGen.zig+20| ... | @@ -686,6 +686,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -686,6 +686,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 686 | .clz => try self.airClz(inst), | 686 | .clz => try self.airClz(inst), |
| 687 | .ctz => try self.airCtz(inst), | 687 | .ctz => try self.airCtz(inst), |
| 688 | .popcount => try self.airPopcount(inst), | 688 | .popcount => try self.airPopcount(inst), |
| 689 | .byte_swap => try self.airByteSwap(inst), | ||
| 690 | .bit_reverse => try self.airBitReverse(inst), | ||
| 689 | .tag_name => try self.airTagName(inst), | 691 | .tag_name => try self.airTagName(inst), |
| 690 | .error_name => try self.airErrorName(inst), | 692 | .error_name => try self.airErrorName(inst), |
| 691 | .splat => try self.airSplat(inst), | 693 | .splat => try self.airSplat(inst), |
| ... | @@ -1716,6 +1718,24 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1716,6 +1718,24 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { |
| 1716 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 1718 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1717 | } | 1719 | } |
| 1718 | 1720 | ||
| 1721 | fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1722 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1723 | const result: MCValue = if (self.liveness.isUnused(inst)) | ||
| 1724 | .dead | ||
| 1725 | else | ||
| 1726 | return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); | ||
| 1727 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1731 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 1732 | const result: MCValue = if (self.liveness.isUnused(inst)) | ||
| 1733 | .dead | ||
| 1734 | else | ||
| 1735 | return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); | ||
| 1736 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | ||
| 1737 | } | ||
| 1738 | |||
| 1719 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { | 1739 | fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { |
| 1720 | const un_op = self.air.instructions.items(.data)[inst].un_op; | 1740 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 1721 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1741 | const result: MCValue = if (self.liveness.isUnused(inst)) |
src/codegen/c.zig+2| ... | @@ -1709,6 +1709,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1709,6 +1709,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1709 | .clz => try airBuiltinCall(f, inst, "clz"), | 1709 | .clz => try airBuiltinCall(f, inst, "clz"), |
| 1710 | .ctz => try airBuiltinCall(f, inst, "ctz"), | 1710 | .ctz => try airBuiltinCall(f, inst, "ctz"), |
| 1711 | .popcount => try airBuiltinCall(f, inst, "popcount"), | 1711 | .popcount => try airBuiltinCall(f, inst, "popcount"), |
| 1712 | .byte_swap => try airBuiltinCall(f, inst, "byte_swap"), | ||
| 1713 | .bit_reverse => try airBuiltinCall(f, inst, "bit_reverse"), | ||
| 1712 | .tag_name => try airTagName(f, inst), | 1714 | .tag_name => try airTagName(f, inst), |
| 1713 | .error_name => try airErrorName(f, inst), | 1715 | .error_name => try airErrorName(f, inst), |
| 1714 | .splat => try airSplat(f, inst), | 1716 | .splat => try airSplat(f, inst), |
src/codegen/llvm.zig+43-3| ... | @@ -2205,7 +2205,9 @@ pub const FuncGen = struct { | ... | @@ -2205,7 +2205,9 @@ pub const FuncGen = struct { |
| 2205 | .get_union_tag => try self.airGetUnionTag(inst), | 2205 | .get_union_tag => try self.airGetUnionTag(inst), |
| 2206 | .clz => try self.airClzCtz(inst, "llvm.ctlz"), | 2206 | .clz => try self.airClzCtz(inst, "llvm.ctlz"), |
| 2207 | .ctz => try self.airClzCtz(inst, "llvm.cttz"), | 2207 | .ctz => try self.airClzCtz(inst, "llvm.cttz"), |
| 2208 | .popcount => try self.airPopCount(inst), | 2208 | .popcount => try self.airBitOp(inst, "llvm.ctpop"), |
| 2209 | .byte_swap => try self.airByteSwap(inst, "llvm.bswap"), | ||
| 2210 | .bit_reverse => try self.airBitOp(inst, "llvm.bitreverse"), | ||
| 2209 | .tag_name => try self.airTagName(inst), | 2211 | .tag_name => try self.airTagName(inst), |
| 2210 | .error_name => try self.airErrorName(inst), | 2212 | .error_name => try self.airErrorName(inst), |
| 2211 | .splat => try self.airSplat(inst), | 2213 | .splat => try self.airSplat(inst), |
| ... | @@ -4348,7 +4350,7 @@ pub const FuncGen = struct { | ... | @@ -4348,7 +4350,7 @@ pub const FuncGen = struct { |
| 4348 | } | 4350 | } |
| 4349 | } | 4351 | } |
| 4350 | 4352 | ||
| 4351 | fn airPopCount(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 4353 | fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { |
| 4352 | if (self.liveness.isUnused(inst)) return null; | 4354 | if (self.liveness.isUnused(inst)) return null; |
| 4353 | 4355 | ||
| 4354 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 4356 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| ... | @@ -4357,7 +4359,7 @@ pub const FuncGen = struct { | ... | @@ -4357,7 +4359,7 @@ pub const FuncGen = struct { |
| 4357 | 4359 | ||
| 4358 | const params = [_]*const llvm.Value{operand}; | 4360 | const params = [_]*const llvm.Value{operand}; |
| 4359 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); | 4361 | const operand_llvm_ty = try self.dg.llvmType(operand_ty); |
| 4360 | const fn_val = self.getIntrinsic("llvm.ctpop", &.{operand_llvm_ty}); | 4362 | const fn_val = self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); |
| 4361 | 4363 | ||
| 4362 | const wrong_size_result = self.builder.buildCall(fn_val, &params, params.len, .C, .Auto, ""); | 4364 | const wrong_size_result = self.builder.buildCall(fn_val, &params, params.len, .C, .Auto, ""); |
| 4363 | const result_ty = self.air.typeOfIndex(inst); | 4365 | const result_ty = self.air.typeOfIndex(inst); |
| ... | @@ -4375,6 +4377,44 @@ pub const FuncGen = struct { | ... | @@ -4375,6 +4377,44 @@ pub const FuncGen = struct { |
| 4375 | } | 4377 | } |
| 4376 | } | 4378 | } |
| 4377 | 4379 | ||
| 4380 | fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { | ||
| 4381 | if (self.liveness.isUnused(inst)) return null; | ||
| 4382 | |||
| 4383 | const target = self.dg.module.getTarget(); | ||
| 4384 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | ||
| 4385 | const operand_ty = self.air.typeOf(ty_op.operand); | ||
| 4386 | var bits = operand_ty.intInfo(target).bits; | ||
| 4387 | assert(bits % 8 == 0); | ||
| 4388 | |||
| 4389 | var operand = try self.resolveInst(ty_op.operand); | ||
| 4390 | var operand_llvm_ty = try self.dg.llvmType(operand_ty); | ||
| 4391 | |||
| 4392 | if (bits % 16 == 8) { | ||
| 4393 | // If not an even byte-multiple, we need zero-extend + shift-left 1 byte | ||
| 4394 | // The truncated result at the end will be the correct bswap | ||
| 4395 | operand_llvm_ty = self.context.intType(bits + 8); | ||
| 4396 | const extended = self.builder.buildZExt(operand, operand_llvm_ty, ""); | ||
| 4397 | operand = self.builder.buildShl(extended, operand_llvm_ty.constInt(8, .False), ""); | ||
| 4398 | bits = bits + 8; | ||
| 4399 | } | ||
| 4400 | |||
| 4401 | const params = [_]*const llvm.Value{operand}; | ||
| 4402 | const fn_val = self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); | ||
| 4403 | |||
| 4404 | const wrong_size_result = self.builder.buildCall(fn_val, &params, params.len, .C, .Auto, ""); | ||
| 4405 | |||
| 4406 | const result_ty = self.air.typeOfIndex(inst); | ||
| 4407 | const result_llvm_ty = try self.dg.llvmType(result_ty); | ||
| 4408 | const result_bits = result_ty.intInfo(target).bits; | ||
| 4409 | if (bits > result_bits) { | ||
| 4410 | return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); | ||
| 4411 | } else if (bits < result_bits) { | ||
| 4412 | return self.builder.buildZExt(wrong_size_result, result_llvm_ty, ""); | ||
| 4413 | } else { | ||
| 4414 | return wrong_size_result; | ||
| 4415 | } | ||
| 4416 | } | ||
| 4417 | |||
| 4378 | fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 4418 | fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 4379 | if (self.liveness.isUnused(inst)) return null; | 4419 | if (self.liveness.isUnused(inst)) return null; |
| 4380 | 4420 |
src/print_air.zig+2| ... | @@ -216,6 +216,8 @@ const Writer = struct { | ... | @@ -216,6 +216,8 @@ const Writer = struct { |
| 216 | .clz, | 216 | .clz, |
| 217 | .ctz, | 217 | .ctz, |
| 218 | .popcount, | 218 | .popcount, |
| 219 | .byte_swap, | ||
| 220 | .bit_reverse, | ||
| 219 | => try w.writeTyOp(s, inst), | 221 | => try w.writeTyOp(s, inst), |
| 220 | 222 | ||
| 221 | .block, | 223 | .block, |
src/value.zig+39| ... | @@ -1334,6 +1334,45 @@ pub const Value = extern union { | ... | @@ -1334,6 +1334,45 @@ pub const Value = extern union { |
| 1334 | } | 1334 | } |
| 1335 | } | 1335 | } |
| 1336 | 1336 | ||
| 1337 | pub fn bitReverse(val: Value, ty: Type, target: Target, arena: Allocator) !Value { | ||
| 1338 | assert(!val.isUndef()); | ||
| 1339 | |||
| 1340 | const info = ty.intInfo(target); | ||
| 1341 | |||
| 1342 | var buffer: Value.BigIntSpace = undefined; | ||
| 1343 | const operand_bigint = val.toBigInt(&buffer); | ||
| 1344 | |||
| 1345 | const limbs = try arena.alloc( | ||
| 1346 | std.math.big.Limb, | ||
| 1347 | std.math.big.int.calcTwosCompLimbCount(info.bits), | ||
| 1348 | ); | ||
| 1349 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | ||
| 1350 | result_bigint.bitReverse(operand_bigint, info.signedness, info.bits); | ||
| 1351 | |||
| 1352 | return fromBigInt(arena, result_bigint.toConst()); | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | pub fn byteSwap(val: Value, ty: Type, target: Target, arena: Allocator) !Value { | ||
| 1356 | assert(!val.isUndef()); | ||
| 1357 | |||
| 1358 | const info = ty.intInfo(target); | ||
| 1359 | |||
| 1360 | // Bit count must be evenly divisible by 8 | ||
| 1361 | assert(info.bits % 8 == 0); | ||
| 1362 | |||
| 1363 | var buffer: Value.BigIntSpace = undefined; | ||
| 1364 | const operand_bigint = val.toBigInt(&buffer); | ||
| 1365 | |||
| 1366 | const limbs = try arena.alloc( | ||
| 1367 | std.math.big.Limb, | ||
| 1368 | std.math.big.int.calcTwosCompLimbCount(info.bits), | ||
| 1369 | ); | ||
| 1370 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | ||
| 1371 | result_bigint.byteSwap(operand_bigint, info.signedness, info.bits / 8); | ||
| 1372 | |||
| 1373 | return fromBigInt(arena, result_bigint.toConst()); | ||
| 1374 | } | ||
| 1375 | |||
| 1337 | /// Asserts the value is an integer and not undefined. | 1376 | /// Asserts the value is an integer and not undefined. |
| 1338 | /// Returns the number of bits the value requires to represent stored in twos complement form. | 1377 | /// Returns the number of bits the value requires to represent stored in twos complement form. |
| 1339 | pub fn intBitCountTwosComp(self: Value, target: Target) usize { | 1378 | pub fn intBitCountTwosComp(self: Value, target: Target) usize { |
test/behavior.zig+2-2| ... | @@ -6,6 +6,8 @@ test { | ... | @@ -6,6 +6,8 @@ test { |
| 6 | _ = @import("behavior/array.zig"); | 6 | _ = @import("behavior/array.zig"); |
| 7 | _ = @import("behavior/basic.zig"); | 7 | _ = @import("behavior/basic.zig"); |
| 8 | _ = @import("behavior/bit_shifting.zig"); | 8 | _ = @import("behavior/bit_shifting.zig"); |
| 9 | _ = @import("behavior/bitreverse.zig"); | ||
| 10 | _ = @import("behavior/byteswap.zig"); | ||
| 9 | _ = @import("behavior/bool.zig"); | 11 | _ = @import("behavior/bool.zig"); |
| 10 | _ = @import("behavior/bugs/394.zig"); | 12 | _ = @import("behavior/bugs/394.zig"); |
| 11 | _ = @import("behavior/bugs/655.zig"); | 13 | _ = @import("behavior/bugs/655.zig"); |
| ... | @@ -124,7 +126,6 @@ test { | ... | @@ -124,7 +126,6 @@ test { |
| 124 | _ = @import("behavior/async_fn.zig"); | 126 | _ = @import("behavior/async_fn.zig"); |
| 125 | } | 127 | } |
| 126 | _ = @import("behavior/await_struct.zig"); | 128 | _ = @import("behavior/await_struct.zig"); |
| 127 | _ = @import("behavior/bitreverse.zig"); | ||
| 128 | _ = @import("behavior/bugs/421.zig"); | 129 | _ = @import("behavior/bugs/421.zig"); |
| 129 | _ = @import("behavior/bugs/529.zig"); | 130 | _ = @import("behavior/bugs/529.zig"); |
| 130 | _ = @import("behavior/bugs/718.zig"); | 131 | _ = @import("behavior/bugs/718.zig"); |
| ... | @@ -151,7 +152,6 @@ test { | ... | @@ -151,7 +152,6 @@ test { |
| 151 | _ = @import("behavior/bugs/7027.zig"); | 152 | _ = @import("behavior/bugs/7027.zig"); |
| 152 | _ = @import("behavior/bugs/7047.zig"); | 153 | _ = @import("behavior/bugs/7047.zig"); |
| 153 | _ = @import("behavior/bugs/10147.zig"); | 154 | _ = @import("behavior/bugs/10147.zig"); |
| 154 | _ = @import("behavior/byteswap.zig"); | ||
| 155 | _ = @import("behavior/const_slice_child.zig"); | 155 | _ = @import("behavior/const_slice_child.zig"); |
| 156 | _ = @import("behavior/export_self_referential_type_info.zig"); | 156 | _ = @import("behavior/export_self_referential_type_info.zig"); |
| 157 | _ = @import("behavior/field_parent_ptr.zig"); | 157 | _ = @import("behavior/field_parent_ptr.zig"); |
test/behavior/bitreverse.zig+36-11| ... | @@ -1,25 +1,45 @@ | ... | @@ -1,25 +1,45 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 3 | const minInt = std.math.minInt; | 4 | const minInt = std.math.minInt; |
| 4 | 5 | ||
| 6 | test "@bitReverse large exotic integer" { | ||
| 7 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 8 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 9 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 10 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 11 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 12 | // Currently failing on stage1 for big-endian targets | ||
| 13 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 14 | |||
| 15 | try expect(@bitReverse(u95, @as(u95, 0x123456789abcdef111213141)) == 0x4146424447bd9eac8f351624); | ||
| 16 | } | ||
| 17 | |||
| 5 | test "@bitReverse" { | 18 | test "@bitReverse" { |
| 19 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 20 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 21 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 22 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 23 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 24 | |||
| 6 | comptime try testBitReverse(); | 25 | comptime try testBitReverse(); |
| 7 | try testBitReverse(); | 26 | try testBitReverse(); |
| 8 | } | 27 | } |
| 9 | 28 | ||
| 10 | fn testBitReverse() !void { | 29 | fn testBitReverse() !void { |
| 11 | // using comptime_ints, unsigned | 30 | // using comptime_ints, unsigned |
| 12 | try expect(@bitReverse(u0, 0) == 0); | 31 | try expect(@bitReverse(u0, @as(u0, 0)) == 0); |
| 13 | try expect(@bitReverse(u5, 0x12) == 0x9); | 32 | try expect(@bitReverse(u5, @as(u5, 0x12)) == 0x9); |
| 14 | try expect(@bitReverse(u8, 0x12) == 0x48); | 33 | try expect(@bitReverse(u8, @as(u8, 0x12)) == 0x48); |
| 15 | try expect(@bitReverse(u16, 0x1234) == 0x2c48); | 34 | try expect(@bitReverse(u16, @as(u16, 0x1234)) == 0x2c48); |
| 16 | try expect(@bitReverse(u24, 0x123456) == 0x6a2c48); | 35 | try expect(@bitReverse(u24, @as(u24, 0x123456)) == 0x6a2c48); |
| 17 | try expect(@bitReverse(u32, 0x12345678) == 0x1e6a2c48); | 36 | try expect(@bitReverse(u32, @as(u32, 0x12345678)) == 0x1e6a2c48); |
| 18 | try expect(@bitReverse(u40, 0x123456789a) == 0x591e6a2c48); | 37 | try expect(@bitReverse(u40, @as(u40, 0x123456789a)) == 0x591e6a2c48); |
| 19 | try expect(@bitReverse(u48, 0x123456789abc) == 0x3d591e6a2c48); | 38 | try expect(@bitReverse(u48, @as(u48, 0x123456789abc)) == 0x3d591e6a2c48); |
| 20 | try expect(@bitReverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); | 39 | try expect(@bitReverse(u56, @as(u56, 0x123456789abcde)) == 0x7b3d591e6a2c48); |
| 21 | try expect(@bitReverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); | 40 | try expect(@bitReverse(u64, @as(u64, 0x123456789abcdef1)) == 0x8f7b3d591e6a2c48); |
| 22 | try expect(@bitReverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); | 41 | try expect(@bitReverse(u96, @as(u96, 0x123456789abcdef111213141)) == 0x828c84888f7b3d591e6a2c48); |
| 42 | try expect(@bitReverse(u128, @as(u128, 0x123456789abcdef11121314151617181)) == 0x818e868a828c84888f7b3d591e6a2c48); | ||
| 23 | 43 | ||
| 24 | // using runtime uints, unsigned | 44 | // using runtime uints, unsigned |
| 25 | var num0: u0 = 0; | 45 | var num0: u0 = 0; |
| ... | @@ -50,11 +70,16 @@ fn testBitReverse() !void { | ... | @@ -50,11 +70,16 @@ fn testBitReverse() !void { |
| 50 | try expect(@bitReverse(i8, @bitCast(i8, @as(u8, 0x92))) == @bitCast(i8, @as(u8, 0x49))); | 70 | try expect(@bitReverse(i8, @bitCast(i8, @as(u8, 0x92))) == @bitCast(i8, @as(u8, 0x49))); |
| 51 | try expect(@bitReverse(i16, @bitCast(i16, @as(u16, 0x1234))) == @bitCast(i16, @as(u16, 0x2c48))); | 71 | try expect(@bitReverse(i16, @bitCast(i16, @as(u16, 0x1234))) == @bitCast(i16, @as(u16, 0x2c48))); |
| 52 | try expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0x123456))) == @bitCast(i24, @as(u24, 0x6a2c48))); | 72 | try expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0x123456))) == @bitCast(i24, @as(u24, 0x6a2c48))); |
| 73 | try expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0x12345f))) == @bitCast(i24, @as(u24, 0xfa2c48))); | ||
| 74 | try expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0xf23456))) == @bitCast(i24, @as(u24, 0x6a2c4f))); | ||
| 53 | try expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0x12345678))) == @bitCast(i32, @as(u32, 0x1e6a2c48))); | 75 | try expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0x12345678))) == @bitCast(i32, @as(u32, 0x1e6a2c48))); |
| 76 | try expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0xf2345678))) == @bitCast(i32, @as(u32, 0x1e6a2c4f))); | ||
| 77 | try expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0x1234567f))) == @bitCast(i32, @as(u32, 0xfe6a2c48))); | ||
| 54 | try expect(@bitReverse(i40, @bitCast(i40, @as(u40, 0x123456789a))) == @bitCast(i40, @as(u40, 0x591e6a2c48))); | 78 | try expect(@bitReverse(i40, @bitCast(i40, @as(u40, 0x123456789a))) == @bitCast(i40, @as(u40, 0x591e6a2c48))); |
| 55 | try expect(@bitReverse(i48, @bitCast(i48, @as(u48, 0x123456789abc))) == @bitCast(i48, @as(u48, 0x3d591e6a2c48))); | 79 | try expect(@bitReverse(i48, @bitCast(i48, @as(u48, 0x123456789abc))) == @bitCast(i48, @as(u48, 0x3d591e6a2c48))); |
| 56 | try expect(@bitReverse(i56, @bitCast(i56, @as(u56, 0x123456789abcde))) == @bitCast(i56, @as(u56, 0x7b3d591e6a2c48))); | 80 | try expect(@bitReverse(i56, @bitCast(i56, @as(u56, 0x123456789abcde))) == @bitCast(i56, @as(u56, 0x7b3d591e6a2c48))); |
| 57 | try expect(@bitReverse(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1))) == @bitCast(i64, @as(u64, 0x8f7b3d591e6a2c48))); | 81 | try expect(@bitReverse(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1))) == @bitCast(i64, @as(u64, 0x8f7b3d591e6a2c48))); |
| 82 | try expect(@bitReverse(i96, @bitCast(i96, @as(u96, 0x123456789abcdef111213141))) == @bitCast(i96, @as(u96, 0x828c84888f7b3d591e6a2c48))); | ||
| 58 | try expect(@bitReverse(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181))) == @bitCast(i128, @as(u128, 0x818e868a828c84888f7b3d591e6a2c48))); | 83 | try expect(@bitReverse(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181))) == @bitCast(i128, @as(u128, 0x818e868a828c84888f7b3d591e6a2c48))); |
| 59 | 84 | ||
| 60 | // using signed, negative. Compare to runtime ints returned from llvm. | 85 | // using signed, negative. Compare to runtime ints returned from llvm. |
test/behavior/byteswap.zig+23-1| ... | @@ -1,18 +1,31 @@ | ... | @@ -1,18 +1,31 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | ||
| 2 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 3 | 4 | ||
| 4 | test "@byteSwap integers" { | 5 | test "@byteSwap integers" { |
| 6 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 7 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 8 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 10 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 11 | |||
| 5 | const ByteSwapIntTest = struct { | 12 | const ByteSwapIntTest = struct { |
| 6 | fn run() !void { | 13 | fn run() !void { |
| 7 | try t(u0, 0, 0); | 14 | try t(u0, 0, 0); |
| 8 | try t(u8, 0x12, 0x12); | 15 | try t(u8, 0x12, 0x12); |
| 9 | try t(u16, 0x1234, 0x3412); | 16 | try t(u16, 0x1234, 0x3412); |
| 10 | try t(u24, 0x123456, 0x563412); | 17 | try t(u24, 0x123456, 0x563412); |
| 18 | try t(i24, @bitCast(i24, @as(u24, 0xf23456)), 0x5634f2); | ||
| 19 | try t(i24, 0x1234f6, @bitCast(i24, @as(u24, 0xf63412))); | ||
| 11 | try t(u32, 0x12345678, 0x78563412); | 20 | try t(u32, 0x12345678, 0x78563412); |
| 21 | try t(i32, @bitCast(i32, @as(u32, 0xf2345678)), 0x785634f2); | ||
| 22 | try t(i32, 0x123456f8, @bitCast(i32, @as(u32, 0xf8563412))); | ||
| 12 | try t(u40, 0x123456789a, 0x9a78563412); | 23 | try t(u40, 0x123456789a, 0x9a78563412); |
| 13 | try t(i48, 0x123456789abc, @bitCast(i48, @as(u48, 0xbc9a78563412))); | 24 | try t(i48, 0x123456789abc, @bitCast(i48, @as(u48, 0xbc9a78563412))); |
| 14 | try t(u56, 0x123456789abcde, 0xdebc9a78563412); | 25 | try t(u56, 0x123456789abcde, 0xdebc9a78563412); |
| 15 | try t(u64, 0x123456789abcdef1, 0xf1debc9a78563412); | 26 | try t(u64, 0x123456789abcdef1, 0xf1debc9a78563412); |
| 27 | try t(u88, 0x123456789abcdef1112131, 0x312111f1debc9a78563412); | ||
| 28 | try t(u96, 0x123456789abcdef111213141, 0x41312111f1debc9a78563412); | ||
| 16 | try t(u128, 0x123456789abcdef11121314151617181, 0x8171615141312111f1debc9a78563412); | 29 | try t(u128, 0x123456789abcdef11121314151617181, 0x8171615141312111f1debc9a78563412); |
| 17 | 30 | ||
| 18 | try t(u0, @as(u0, 0), 0); | 31 | try t(u0, @as(u0, 0), 0); |
| ... | @@ -24,6 +37,8 @@ test "@byteSwap integers" { | ... | @@ -24,6 +37,8 @@ test "@byteSwap integers" { |
| 24 | try t(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0xbc9a78563412))); | 37 | try t(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0xbc9a78563412))); |
| 25 | try t(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0xdebc9a78563412))); | 38 | try t(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0xdebc9a78563412))); |
| 26 | try t(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0xf1debc9a78563412))); | 39 | try t(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0xf1debc9a78563412))); |
| 40 | try t(i88, @bitCast(i88, @as(u88, 0x123456789abcdef1112131)), @bitCast(i88, @as(u88, 0x312111f1debc9a78563412))); | ||
| 41 | try t(i96, @bitCast(i96, @as(u96, 0x123456789abcdef111213141)), @bitCast(i96, @as(u96, 0x41312111f1debc9a78563412))); | ||
| 27 | try t( | 42 | try t( |
| 28 | i128, | 43 | i128, |
| 29 | @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)), | 44 | @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)), |
| ... | @@ -31,7 +46,7 @@ test "@byteSwap integers" { | ... | @@ -31,7 +46,7 @@ test "@byteSwap integers" { |
| 31 | ); | 46 | ); |
| 32 | } | 47 | } |
| 33 | fn t(comptime I: type, input: I, expected_output: I) !void { | 48 | fn t(comptime I: type, input: I, expected_output: I) !void { |
| 34 | try std.testing.expectEqual(expected_output, @byteSwap(I, input)); | 49 | try std.testing.expect(expected_output == @byteSwap(I, input)); |
| 35 | } | 50 | } |
| 36 | }; | 51 | }; |
| 37 | comptime try ByteSwapIntTest.run(); | 52 | comptime try ByteSwapIntTest.run(); |
| ... | @@ -39,6 +54,13 @@ test "@byteSwap integers" { | ... | @@ -39,6 +54,13 @@ test "@byteSwap integers" { |
| 39 | } | 54 | } |
| 40 | 55 | ||
| 41 | test "@byteSwap vectors" { | 56 | test "@byteSwap vectors" { |
| 57 | if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; | ||
| 58 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 59 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 60 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 61 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 62 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 63 | |||
| 42 | const ByteSwapVectorTest = struct { | 64 | const ByteSwapVectorTest = struct { |
| 43 | fn run() !void { | 65 | fn run() !void { |
| 44 | try t(u8, 2, [_]u8{ 0x12, 0x13 }, [_]u8{ 0x12, 0x13 }); | 66 | try t(u8, 2, [_]u8{ 0x12, 0x13 }, [_]u8{ 0x12, 0x13 }); |