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
signature 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 {
745745 rma.truncate(rma.toConst(), signedness, bit_count);
746746 }
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
748874 /// r = @popCount(a) with 2s-complement semantics.
749875 /// r and a may be aliases.
750876 ///
lib/std/math/big/int_test.zig+105
......@@ -7,6 +7,7 @@ const Limb = std.math.big.Limb;
77const SignedLimb = std.math.big.SignedLimb;
88const DoubleLimb = std.math.big.DoubleLimb;
99const SignedDoubleLimb = std.math.big.SignedDoubleLimb;
10const calcTwosCompLimbCount = std.math.big.int.calcTwosCompLimbCount;
1011const maxInt = std.math.maxInt;
1112const minInt = std.math.minInt;
1213
......@@ -2689,3 +2690,107 @@ test "big int conversion write twos complement zero" {
26892690 m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned);
26902691 try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq);
26912692}
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 {
236236 /// Result type will always be an unsigned integer big enough to fit the answer.
237237 /// Uses the `ty_op` field.
238238 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
240246 /// Square root of a floating point number.
241247 /// Uses the `un_op` field.
......@@ -874,6 +880,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
874880 .clz,
875881 .ctz,
876882 .popcount,
883 .byte_swap,
884 .bit_reverse,
877885 => return air.getRefType(datas[inst].ty_op.ty),
878886
879887 .loop,
src/Liveness.zig+2
......@@ -318,6 +318,8 @@ fn analyzeInst(
318318 .clz,
319319 .ctz,
320320 .popcount,
321 .byte_swap,
322 .bit_reverse,
321323 .splat,
322324 => {
323325 const o = inst_datas[inst].ty_op;
src/Sema.zig+50-4
......@@ -11701,14 +11701,60 @@ fn zirBitCount(
1170111701
1170211702fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1170311703 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
11704 const src = inst_data.src();
11705 return sema.fail(block, src, "TODO: Sema.zirByteSwap", .{});
11704 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
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);
1170611732}
1170711733
1170811734fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1170911735 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
11710 const src = inst_data.src();
11711 return sema.fail(block, src, "TODO: Sema.zirBitReverse", .{});
11736 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
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);
1171211758}
1171311759
1171411760fn 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 {
621621 .clz => try self.airClz(inst),
622622 .ctz => try self.airCtz(inst),
623623 .popcount => try self.airPopcount(inst),
624 .byte_swap => try self.airByteSwap(inst),
625 .bit_reverse => try self.airBitReverse(inst),
624626 .tag_name => try self.airTagName(inst),
625627 .error_name => try self.airErrorName(inst),
626628 .splat => try self.airSplat(inst),
......@@ -1682,6 +1684,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
16821684 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
16831685}
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
16851699fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
16861700 const un_op = self.air.instructions.items(.data)[inst].un_op;
16871701 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 {
605605 .clz => try self.airClz(inst),
606606 .ctz => try self.airCtz(inst),
607607 .popcount => try self.airPopcount(inst),
608 .byte_swap => try self.airByteSwap(inst),
609 .bit_reverse => try self.airBitReverse(inst),
608610 .tag_name => try self.airTagName(inst),
609611 .error_name => try self.airErrorName(inst),
610612 .splat => try self.airSplat(inst),
......@@ -1392,6 +1394,20 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
13921394 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
13931395}
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
13951411fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
13961412 const un_op = self.air.instructions.items(.data)[inst].un_op;
13971413 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 {
592592 .clz => try self.airClz(inst),
593593 .ctz => try self.airCtz(inst),
594594 .popcount => try self.airPopcount(inst),
595 .byte_swap => try self.airByteSwap(inst),
596 .bit_reverse => try self.airBitReverse(inst),
595597 .tag_name => try self.airTagName(inst),
596598 .error_name => try self.airErrorName(inst),
597599 .splat => try self.airSplat(inst),
......@@ -1181,6 +1183,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
11811183 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
11821184}
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
11841198fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
11851199 const un_op = self.air.instructions.items(.data)[inst].un_op;
11861200 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 {
16871687 .clz,
16881688 .ctz,
16891689 .popcount,
1690 .byte_swap,
1691 .bit_reverse,
16901692 .is_err_ptr,
16911693 .is_non_err_ptr,
16921694 .fptrunc,
src/arch/x86_64/CodeGen.zig+20
......@@ -686,6 +686,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
686686 .clz => try self.airClz(inst),
687687 .ctz => try self.airCtz(inst),
688688 .popcount => try self.airPopcount(inst),
689 .byte_swap => try self.airByteSwap(inst),
690 .bit_reverse => try self.airBitReverse(inst),
689691 .tag_name => try self.airTagName(inst),
690692 .error_name => try self.airErrorName(inst),
691693 .splat => try self.airSplat(inst),
......@@ -1716,6 +1718,24 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
17161718 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
17171719}
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
17191739fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
17201740 const un_op = self.air.instructions.items(.data)[inst].un_op;
17211741 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
17091709 .clz => try airBuiltinCall(f, inst, "clz"),
17101710 .ctz => try airBuiltinCall(f, inst, "ctz"),
17111711 .popcount => try airBuiltinCall(f, inst, "popcount"),
1712 .byte_swap => try airBuiltinCall(f, inst, "byte_swap"),
1713 .bit_reverse => try airBuiltinCall(f, inst, "bit_reverse"),
17121714 .tag_name => try airTagName(f, inst),
17131715 .error_name => try airErrorName(f, inst),
17141716 .splat => try airSplat(f, inst),
src/codegen/llvm.zig+43-3
......@@ -2205,7 +2205,9 @@ pub const FuncGen = struct {
22052205 .get_union_tag => try self.airGetUnionTag(inst),
22062206 .clz => try self.airClzCtz(inst, "llvm.ctlz"),
22072207 .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"),
22092211 .tag_name => try self.airTagName(inst),
22102212 .error_name => try self.airErrorName(inst),
22112213 .splat => try self.airSplat(inst),
......@@ -4348,7 +4350,7 @@ pub const FuncGen = struct {
43484350 }
43494351 }
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 {
43524354 if (self.liveness.isUnused(inst)) return null;
43534355
43544356 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
......@@ -4357,7 +4359,7 @@ pub const FuncGen = struct {
43574359
43584360 const params = [_]*const llvm.Value{operand};
43594361 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
43624364 const wrong_size_result = self.builder.buildCall(fn_val, &params, params.len, .C, .Auto, "");
43634365 const result_ty = self.air.typeOfIndex(inst);
......@@ -4375,6 +4377,44 @@ pub const FuncGen = struct {
43754377 }
43764378 }
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
43784418 fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
43794419 if (self.liveness.isUnused(inst)) return null;
43804420
src/print_air.zig+2
......@@ -216,6 +216,8 @@ const Writer = struct {
216216 .clz,
217217 .ctz,
218218 .popcount,
219 .byte_swap,
220 .bit_reverse,
219221 => try w.writeTyOp(s, inst),
220222
221223 .block,
src/value.zig+39
......@@ -1334,6 +1334,45 @@ pub const Value = extern union {
13341334 }
13351335 }
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
13371376 /// Asserts the value is an integer and not undefined.
13381377 /// Returns the number of bits the value requires to represent stored in twos complement form.
13391378 pub fn intBitCountTwosComp(self: Value, target: Target) usize {
test/behavior.zig+2-2
......@@ -6,6 +6,8 @@ test {
66 _ = @import("behavior/array.zig");
77 _ = @import("behavior/basic.zig");
88 _ = @import("behavior/bit_shifting.zig");
9 _ = @import("behavior/bitreverse.zig");
10 _ = @import("behavior/byteswap.zig");
911 _ = @import("behavior/bool.zig");
1012 _ = @import("behavior/bugs/394.zig");
1113 _ = @import("behavior/bugs/655.zig");
......@@ -124,7 +126,6 @@ test {
124126 _ = @import("behavior/async_fn.zig");
125127 }
126128 _ = @import("behavior/await_struct.zig");
127 _ = @import("behavior/bitreverse.zig");
128129 _ = @import("behavior/bugs/421.zig");
129130 _ = @import("behavior/bugs/529.zig");
130131 _ = @import("behavior/bugs/718.zig");
......@@ -151,7 +152,6 @@ test {
151152 _ = @import("behavior/bugs/7027.zig");
152153 _ = @import("behavior/bugs/7047.zig");
153154 _ = @import("behavior/bugs/10147.zig");
154 _ = @import("behavior/byteswap.zig");
155155 _ = @import("behavior/const_slice_child.zig");
156156 _ = @import("behavior/export_self_referential_type_info.zig");
157157 _ = @import("behavior/field_parent_ptr.zig");
test/behavior/bitreverse.zig+36-11
......@@ -1,25 +1,45 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const expect = std.testing.expect;
34const 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
518test "@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
625 comptime try testBitReverse();
726 try testBitReverse();
827}
928
1029fn testBitReverse() !void {
1130 // using comptime_ints, unsigned
12 try expect(@bitReverse(u0, 0) == 0);
13 try expect(@bitReverse(u5, 0x12) == 0x9);
14 try expect(@bitReverse(u8, 0x12) == 0x48);
15 try expect(@bitReverse(u16, 0x1234) == 0x2c48);
16 try expect(@bitReverse(u24, 0x123456) == 0x6a2c48);
17 try expect(@bitReverse(u32, 0x12345678) == 0x1e6a2c48);
18 try expect(@bitReverse(u40, 0x123456789a) == 0x591e6a2c48);
19 try expect(@bitReverse(u48, 0x123456789abc) == 0x3d591e6a2c48);
20 try expect(@bitReverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48);
21 try expect(@bitReverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48);
22 try expect(@bitReverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48);
31 try expect(@bitReverse(u0, @as(u0, 0)) == 0);
32 try expect(@bitReverse(u5, @as(u5, 0x12)) == 0x9);
33 try expect(@bitReverse(u8, @as(u8, 0x12)) == 0x48);
34 try expect(@bitReverse(u16, @as(u16, 0x1234)) == 0x2c48);
35 try expect(@bitReverse(u24, @as(u24, 0x123456)) == 0x6a2c48);
36 try expect(@bitReverse(u32, @as(u32, 0x12345678)) == 0x1e6a2c48);
37 try expect(@bitReverse(u40, @as(u40, 0x123456789a)) == 0x591e6a2c48);
38 try expect(@bitReverse(u48, @as(u48, 0x123456789abc)) == 0x3d591e6a2c48);
39 try expect(@bitReverse(u56, @as(u56, 0x123456789abcde)) == 0x7b3d591e6a2c48);
40 try expect(@bitReverse(u64, @as(u64, 0x123456789abcdef1)) == 0x8f7b3d591e6a2c48);
41 try expect(@bitReverse(u96, @as(u96, 0x123456789abcdef111213141)) == 0x828c84888f7b3d591e6a2c48);
42 try expect(@bitReverse(u128, @as(u128, 0x123456789abcdef11121314151617181)) == 0x818e868a828c84888f7b3d591e6a2c48);
2343
2444 // using runtime uints, unsigned
2545 var num0: u0 = 0;
......@@ -50,11 +70,16 @@ fn testBitReverse() !void {
5070 try expect(@bitReverse(i8, @bitCast(i8, @as(u8, 0x92))) == @bitCast(i8, @as(u8, 0x49)));
5171 try expect(@bitReverse(i16, @bitCast(i16, @as(u16, 0x1234))) == @bitCast(i16, @as(u16, 0x2c48)));
5272 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)));
5375 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)));
5478 try expect(@bitReverse(i40, @bitCast(i40, @as(u40, 0x123456789a))) == @bitCast(i40, @as(u40, 0x591e6a2c48)));
5579 try expect(@bitReverse(i48, @bitCast(i48, @as(u48, 0x123456789abc))) == @bitCast(i48, @as(u48, 0x3d591e6a2c48)));
5680 try expect(@bitReverse(i56, @bitCast(i56, @as(u56, 0x123456789abcde))) == @bitCast(i56, @as(u56, 0x7b3d591e6a2c48)));
5781 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)));
5883 try expect(@bitReverse(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181))) == @bitCast(i128, @as(u128, 0x818e868a828c84888f7b3d591e6a2c48)));
5984
6085 // using signed, negative. Compare to runtime ints returned from llvm.
test/behavior/byteswap.zig+23-1
......@@ -1,18 +1,31 @@
11const std = @import("std");
2const builtin = @import("builtin");
23const expect = std.testing.expect;
34
45test "@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
512 const ByteSwapIntTest = struct {
613 fn run() !void {
714 try t(u0, 0, 0);
815 try t(u8, 0x12, 0x12);
916 try t(u16, 0x1234, 0x3412);
1017 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)));
1120 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)));
1223 try t(u40, 0x123456789a, 0x9a78563412);
1324 try t(i48, 0x123456789abc, @bitCast(i48, @as(u48, 0xbc9a78563412)));
1425 try t(u56, 0x123456789abcde, 0xdebc9a78563412);
1526 try t(u64, 0x123456789abcdef1, 0xf1debc9a78563412);
27 try t(u88, 0x123456789abcdef1112131, 0x312111f1debc9a78563412);
28 try t(u96, 0x123456789abcdef111213141, 0x41312111f1debc9a78563412);
1629 try t(u128, 0x123456789abcdef11121314151617181, 0x8171615141312111f1debc9a78563412);
1730
1831 try t(u0, @as(u0, 0), 0);
......@@ -24,6 +37,8 @@ test "@byteSwap integers" {
2437 try t(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0xbc9a78563412)));
2538 try t(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0xdebc9a78563412)));
2639 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)));
2742 try t(
2843 i128,
2944 @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)),
......@@ -31,7 +46,7 @@ test "@byteSwap integers" {
3146 );
3247 }
3348 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));
3550 }
3651 };
3752 comptime try ByteSwapIntTest.run();
......@@ -39,6 +54,13 @@ test "@byteSwap integers" {
3954}
4055
4156test "@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
4264 const ByteSwapVectorTest = struct {
4365 fn run() !void {
4466 try t(u8, 2, [_]u8{ 0x12, 0x13 }, [_]u8{ 0x12, 0x13 });