authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-18 20:29:31-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-18 20:29:31-05:00
log09d93ec845f2f1adaefc512fccaeaa0ea8beed61
tree4035e1593a535d6d0a1bea5d61b19cfe43a351fd
parentdee96e2e2f464c3b8edc8ec3a63cd3b1860e3a9d
parentdb80dff4e002146063609d20599a7310837074c7
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10887 from topolarity/stage2-bitreverse-byteswap

stage2 llvm: Implement `@bitReverse`, `@byteSwap` built-ins

17 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 }
747747
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;
7const SignedLimb = std.math.big.SignedLimb;7const SignedLimb = std.math.big.SignedLimb;
8const DoubleLimb = std.math.big.DoubleLimb;8const DoubleLimb = std.math.big.DoubleLimb;
9const SignedDoubleLimb = std.math.big.SignedDoubleLimb;9const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
10const calcTwosCompLimbCount = std.math.big.int.calcTwosCompLimbCount;
10const maxInt = std.math.maxInt;11const maxInt = std.math.maxInt;
11const minInt = std.math.minInt;12const minInt = std.math.minInt;
1213
...@@ -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
2694fn 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
2707test "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
2741fn 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
2754test "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,
239245
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),
878886
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(
1170111701
11702fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11702fn 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}
1170711733
11708fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11734fn 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}
1171311759
11714fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {11760fn 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}
16841686
1687fn 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
1693fn 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
1685fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {1699fn 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}
13941396
1397fn 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
1404fn 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
1395fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {1411fn 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}
11831185
1186fn 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
1192fn 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
1184fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {1198fn 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}
17181720
1721fn 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
1730fn 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
1719fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {1739fn 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 }
43504352
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;
43534355
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 {
43574359
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});
43614363
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 }
43774379
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;
43804420
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),
220222
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 }
13361336
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 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const expect = std.testing.expect;3const expect = std.testing.expect;
3const minInt = std.math.minInt;4const minInt = std.math.minInt;
45
6test "@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
5test "@bitReverse" {18test "@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}
928
10fn testBitReverse() !void {29fn testBitReverse() !void {
11 // using comptime_ints, unsigned30 // 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);
2343
24 // using runtime uints, unsigned44 // 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)));
5984
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 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const expect = std.testing.expect;3const expect = std.testing.expect;
34
4test "@byteSwap integers" {5test "@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);
1730
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}
4055
41test "@byteSwap vectors" {56test "@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 });