From ef417f19e147ebfa46035dac5ad800723c8fed94 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 13 Feb 2022 15:04:46 -0700 Subject: [PATCH 1/3] stage2: Implement `@bitReverse` and `@byteSwap` This change implements the above built-ins for Sema and the LLVM backend. Other backends have had placeholders added for lowering. --- lib/std/math/big/int.zig | 126 ++++++++++++++++++++++++++++++++++ lib/std/math/big/int_test.zig | 105 ++++++++++++++++++++++++++++ src/Air.zig | 8 +++ src/Liveness.zig | 2 + src/Sema.zig | 54 +++++++++++++-- src/arch/aarch64/CodeGen.zig | 14 ++++ src/arch/arm/CodeGen.zig | 16 +++++ src/arch/riscv64/CodeGen.zig | 14 ++++ src/arch/wasm/CodeGen.zig | 2 + src/arch/x86_64/CodeGen.zig | 20 ++++++ src/codegen/c.zig | 2 + src/codegen/llvm.zig | 46 ++++++++++++- src/print_air.zig | 2 + src/value.zig | 39 +++++++++++ test/behavior.zig | 4 +- test/behavior/bitreverse.zig | 34 ++++++--- test/behavior/byteswap.zig | 20 +++++- 17 files changed, 487 insertions(+), 21 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 731f4b5456..30b1530932 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -745,6 +745,132 @@ pub const Mutable = struct { rma.truncate(rma.toConst(), signedness, bit_count); } + /// r = @bitReverse(a) with 2s-complement semantics. + /// r and a may be aliases. + /// + /// Asserts the result fits in `r`. Upper bound on the number of limbs needed by + /// r is `calcTwosCompLimbCount(bit_count)`. + pub fn bitReverse(r: *Mutable, a: Const, signedness: Signedness, bit_count: usize) void { + if (bit_count == 0) return; + + r.copy(a); + + const limbs_required = calcTwosCompLimbCount(bit_count); + + if (!a.positive) { + r.positive = true; // Negate. + r.bitNotWrap(r.toConst(), .unsigned, bit_count); // Bitwise NOT. + r.addScalar(r.toConst(), 1); // Add one. + } else if (limbs_required > a.limbs.len) { + // Zero-extend to our output length + for (r.limbs[a.limbs.len..limbs_required]) |*limb| { + limb.* = 0; + } + r.len = limbs_required; + } + + // 0b0..01..1000 with @log2(@sizeOf(Limb)) consecutive ones + const endian_mask: usize = (@sizeOf(Limb) - 1) << 3; + + var bytes = std.mem.sliceAsBytes(r.limbs); + var bits = std.packed_int_array.PackedIntSliceEndian(u1, .Little).init(bytes, limbs_required * @bitSizeOf(Limb)); + + var k: usize = 0; + while (k < ((bit_count + 1) / 2)) : (k += 1) { + var i = k; + var rev_i = bit_count - i - 1; + + // This "endian mask" remaps a low (LE) byte to the corresponding high + // (BE) byte in the Limb, without changing which limbs we are indexing + if (native_endian == .Big) { + i ^= endian_mask; + rev_i ^= endian_mask; + } + + const bit_i = bits.get(i); + const bit_rev_i = bits.get(rev_i); + bits.set(i, bit_rev_i); + bits.set(rev_i, bit_i); + } + + // Calculate signed-magnitude representation for output + if (signedness == .signed) { + const last_bit = switch (native_endian) { + .Little => bits.get(bit_count - 1), + .Big => bits.get((bit_count - 1) ^ endian_mask), + }; + if (last_bit == 1) { + r.bitNotWrap(r.toConst(), .unsigned, bit_count); // Bitwise NOT. + r.addScalar(r.toConst(), 1); // Add one. + r.positive = false; // Negate. + } + } + r.normalize(r.len); + } + + /// r = @byteSwap(a) with 2s-complement semantics. + /// r and a may be aliases. + /// + /// Asserts the result fits in `r`. Upper bound on the number of limbs needed by + /// r is `calcTwosCompLimbCount(8*byte_count)`. + pub fn byteSwap(r: *Mutable, a: Const, signedness: Signedness, byte_count: usize) void { + if (byte_count == 0) return; + + r.copy(a); + const limbs_required = calcTwosCompLimbCount(8 * byte_count); + + if (!a.positive) { + r.positive = true; // Negate. + r.bitNotWrap(r.toConst(), .unsigned, 8 * byte_count); // Bitwise NOT. + r.addScalar(r.toConst(), 1); // Add one. + } else if (limbs_required > a.limbs.len) { + // Zero-extend to our output length + for (r.limbs[a.limbs.len..limbs_required]) |*limb| { + limb.* = 0; + } + r.len = limbs_required; + } + + // 0b0..01..1 with @log2(@sizeOf(Limb)) trailing ones + const endian_mask: usize = @sizeOf(Limb) - 1; + + var bytes = std.mem.sliceAsBytes(r.limbs); + assert(bytes.len >= byte_count); + + var k: usize = 0; + while (k < (byte_count + 1) / 2) : (k += 1) { + var i = k; + var rev_i = byte_count - k - 1; + + // This "endian mask" remaps a low (LE) byte to the corresponding high + // (BE) byte in the Limb, without changing which limbs we are indexing + if (native_endian == .Big) { + i ^= endian_mask; + rev_i ^= endian_mask; + } + + const byte_i = bytes[i]; + const byte_rev_i = bytes[rev_i]; + bytes[rev_i] = byte_i; + bytes[i] = byte_rev_i; + } + + // Calculate signed-magnitude representation for output + if (signedness == .signed) { + const last_byte = switch (native_endian) { + .Little => bytes[byte_count - 1], + .Big => bytes[(byte_count - 1) ^ endian_mask], + }; + + if (last_byte & (1 << 7) != 0) { // Check sign bit of last byte + r.bitNotWrap(r.toConst(), .unsigned, 8 * byte_count); // Bitwise NOT. + r.addScalar(r.toConst(), 1); // Add one. + r.positive = false; // Negate. + } + } + r.normalize(r.len); + } + /// r = @popCount(a) with 2s-complement semantics. /// r and a may be aliases. /// diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index e7469326e4..71c80d5a83 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -7,6 +7,7 @@ const Limb = std.math.big.Limb; const SignedLimb = std.math.big.SignedLimb; const DoubleLimb = std.math.big.DoubleLimb; const SignedDoubleLimb = std.math.big.SignedDoubleLimb; +const calcTwosCompLimbCount = std.math.big.int.calcTwosCompLimbCount; const maxInt = std.math.maxInt; const minInt = std.math.minInt; @@ -2689,3 +2690,107 @@ test "big int conversion write twos complement zero" { m.readTwosComplement(buffer, bit_count, 16, .Big, .unsigned); try testing.expect(m.toConst().orderAgainstScalar(0x0) == .eq); } + +fn bitReverseTest(comptime T: type, comptime input: comptime_int, comptime expected_output: comptime_int) !void { + const bit_count = @typeInfo(T).Int.bits; + const signedness = @typeInfo(T).Int.signedness; + + var a = try Managed.initSet(testing.allocator, input); + defer a.deinit(); + + try a.ensureCapacity(calcTwosCompLimbCount(bit_count)); + var m = a.toMutable(); + m.bitReverse(a.toConst(), signedness, bit_count); + try testing.expect(m.toConst().orderAgainstScalar(expected_output) == .eq); +} + +test "big int bit reverse" { + var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff); + defer a.deinit(); + + try bitReverseTest(u0, 0, 0); + try bitReverseTest(u5, 0x12, 0x09); + try bitReverseTest(u8, 0x12, 0x48); + try bitReverseTest(u16, 0x1234, 0x2c48); + try bitReverseTest(u24, 0x123456, 0x6a2c48); + try bitReverseTest(u32, 0x12345678, 0x1e6a2c48); + try bitReverseTest(u40, 0x123456789a, 0x591e6a2c48); + try bitReverseTest(u48, 0x123456789abc, 0x3d591e6a2c48); + try bitReverseTest(u56, 0x123456789abcde, 0x7b3d591e6a2c48); + try bitReverseTest(u64, 0x123456789abcdef1, 0x8f7b3d591e6a2c48); + try bitReverseTest(u95, 0x123456789abcdef111213141, 0x4146424447bd9eac8f351624); + try bitReverseTest(u96, 0x123456789abcdef111213141, 0x828c84888f7b3d591e6a2c48); + try bitReverseTest(u128, 0x123456789abcdef11121314151617181, 0x818e868a828c84888f7b3d591e6a2c48); + + try bitReverseTest(i8, @bitCast(i8, @as(u8, 0x92)), @bitCast(i8, @as(u8, 0x49))); + try bitReverseTest(i16, @bitCast(i16, @as(u16, 0x1234)), @bitCast(i16, @as(u16, 0x2c48))); + try bitReverseTest(i24, @bitCast(i24, @as(u24, 0x123456)), @bitCast(i24, @as(u24, 0x6a2c48))); + try bitReverseTest(i24, @bitCast(i24, @as(u24, 0x12345f)), @bitCast(i24, @as(u24, 0xfa2c48))); + try bitReverseTest(i24, @bitCast(i24, @as(u24, 0xf23456)), @bitCast(i24, @as(u24, 0x6a2c4f))); + try bitReverseTest(i32, @bitCast(i32, @as(u32, 0x12345678)), @bitCast(i32, @as(u32, 0x1e6a2c48))); + try bitReverseTest(i32, @bitCast(i32, @as(u32, 0xf2345678)), @bitCast(i32, @as(u32, 0x1e6a2c4f))); + try bitReverseTest(i32, @bitCast(i32, @as(u32, 0x1234567f)), @bitCast(i32, @as(u32, 0xfe6a2c48))); + try bitReverseTest(i40, @bitCast(i40, @as(u40, 0x123456789a)), @bitCast(i40, @as(u40, 0x591e6a2c48))); + try bitReverseTest(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0x3d591e6a2c48))); + try bitReverseTest(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0x7b3d591e6a2c48))); + try bitReverseTest(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0x8f7b3d591e6a2c48))); + try bitReverseTest(i96, @bitCast(i96, @as(u96, 0x123456789abcdef111213141)), @bitCast(i96, @as(u96, 0x828c84888f7b3d591e6a2c48))); + try bitReverseTest(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)), @bitCast(i128, @as(u128, 0x818e868a828c84888f7b3d591e6a2c48))); +} + +fn byteSwapTest(comptime T: type, comptime input: comptime_int, comptime expected_output: comptime_int) !void { + const byte_count = @typeInfo(T).Int.bits / 8; + const signedness = @typeInfo(T).Int.signedness; + + var a = try Managed.initSet(testing.allocator, input); + defer a.deinit(); + + try a.ensureCapacity(calcTwosCompLimbCount(8 * byte_count)); + var m = a.toMutable(); + m.byteSwap(a.toConst(), signedness, byte_count); + try testing.expect(m.toConst().orderAgainstScalar(expected_output) == .eq); +} + +test "big int byte swap" { + var a = try Managed.initSet(testing.allocator, 0x01_ffffffff_ffffffff_ffffffff); + defer a.deinit(); + + @setEvalBranchQuota(10_000); + + try byteSwapTest(u0, 0, 0); + try byteSwapTest(u8, 0x12, 0x12); + try byteSwapTest(u16, 0x1234, 0x3412); + try byteSwapTest(u24, 0x123456, 0x563412); + try byteSwapTest(u32, 0x12345678, 0x78563412); + try byteSwapTest(u40, 0x123456789a, 0x9a78563412); + try byteSwapTest(u48, 0x123456789abc, 0xbc9a78563412); + try byteSwapTest(u56, 0x123456789abcde, 0xdebc9a78563412); + try byteSwapTest(u64, 0x123456789abcdef1, 0xf1debc9a78563412); + try byteSwapTest(u88, 0x123456789abcdef1112131, 0x312111f1debc9a78563412); + try byteSwapTest(u96, 0x123456789abcdef111213141, 0x41312111f1debc9a78563412); + try byteSwapTest(u128, 0x123456789abcdef11121314151617181, 0x8171615141312111f1debc9a78563412); + + try byteSwapTest(i8, -50, -50); + try byteSwapTest(i16, @bitCast(i16, @as(u16, 0x1234)), @bitCast(i16, @as(u16, 0x3412))); + try byteSwapTest(i24, @bitCast(i24, @as(u24, 0x123456)), @bitCast(i24, @as(u24, 0x563412))); + try byteSwapTest(i32, @bitCast(i32, @as(u32, 0x12345678)), @bitCast(i32, @as(u32, 0x78563412))); + try byteSwapTest(i40, @bitCast(i40, @as(u40, 0x123456789a)), @bitCast(i40, @as(u40, 0x9a78563412))); + try byteSwapTest(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0xbc9a78563412))); + try byteSwapTest(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0xdebc9a78563412))); + try byteSwapTest(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0xf1debc9a78563412))); + try byteSwapTest(i88, @bitCast(i88, @as(u88, 0x123456789abcdef1112131)), @bitCast(i88, @as(u88, 0x312111f1debc9a78563412))); + try byteSwapTest(i96, @bitCast(i96, @as(u96, 0x123456789abcdef111213141)), @bitCast(i96, @as(u96, 0x41312111f1debc9a78563412))); + try byteSwapTest(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)), @bitCast(i128, @as(u128, 0x8171615141312111f1debc9a78563412))); + + try byteSwapTest(u512, 0x80, 1 << 511); + try byteSwapTest(i512, 0x80, minInt(i512)); + try byteSwapTest(i512, 0x40, 1 << 510); + try byteSwapTest(i512, -0x100, (1 << 504) - 1); + try byteSwapTest(i400, -0x100, (1 << 392) - 1); + try byteSwapTest(i400, -0x2, -(1 << 392) - 1); + try byteSwapTest(i24, @bitCast(i24, @as(u24, 0xf23456)), 0x5634f2); + try byteSwapTest(i24, 0x1234f6, @bitCast(i24, @as(u24, 0xf63412))); + try byteSwapTest(i32, @bitCast(i32, @as(u32, 0xf2345678)), 0x785634f2); + try byteSwapTest(i32, 0x123456f8, @bitCast(i32, @as(u32, 0xf8563412))); + try byteSwapTest(i48, 0x123456789abc, @bitCast(i48, @as(u48, 0xbc9a78563412))); +} diff --git a/src/Air.zig b/src/Air.zig index 623da26255..7ce3032f8d 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -236,6 +236,12 @@ pub const Inst = struct { /// Result type will always be an unsigned integer big enough to fit the answer. /// Uses the `ty_op` field. popcount, + /// Reverse the bytes in an integer according to its representation in twos complement. + /// Uses the `ty_op` field. + byte_swap, + /// Reverse the bits in an integer according to its representation in twos complement. + /// Uses the `ty_op` field. + bit_reverse, /// Square root of a floating point number. /// Uses the `un_op` field. @@ -874,6 +880,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .clz, .ctz, .popcount, + .byte_swap, + .bit_reverse, => return air.getRefType(datas[inst].ty_op.ty), .loop, diff --git a/src/Liveness.zig b/src/Liveness.zig index 12ba63fc00..d268101e52 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -318,6 +318,8 @@ fn analyzeInst( .clz, .ctz, .popcount, + .byte_swap, + .bit_reverse, .splat, => { const o = inst_datas[inst].ty_op; diff --git a/src/Sema.zig b/src/Sema.zig index a153be5119..b0cdca3ed9 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -11701,14 +11701,60 @@ fn zirBitCount( fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirByteSwap", .{}); + const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const operand = sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + // TODO implement support for vectors + if (operand_ty.zigTypeTag() != .Int) { + return sema.fail(block, ty_src, "expected integer type, found '{}'", .{ + operand_ty, + }); + } + const target = sema.mod.getTarget(); + const bits = operand_ty.intInfo(target).bits; + if (bits == 0) return Air.Inst.Ref.zero; + if (operand_ty.intInfo(target).bits % 8 != 0) { + return sema.fail(block, ty_src, "@byteSwap requires the number of bits to be evenly divisible by 8, but {} has {} bits", .{ + operand_ty, + operand_ty.intInfo(target).bits, + }); + } + + const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { + if (val.isUndef()) return sema.addConstUndef(operand_ty); + const result_val = try val.byteSwap(operand_ty, target, sema.arena); + return sema.addConstant(operand_ty, result_val); + } else operand_src; + + try sema.requireRuntimeBlock(block, runtime_src); + return block.addTyOp(.byte_swap, operand_ty, operand); } fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirBitReverse", .{}); + const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const operand = sema.resolveInst(inst_data.operand); + const operand_ty = sema.typeOf(operand); + // TODO implement support for vectors + if (operand_ty.zigTypeTag() != .Int) { + return sema.fail(block, ty_src, "expected integer type, found '{}'", .{ + operand_ty, + }); + } + const target = sema.mod.getTarget(); + const bits = operand_ty.intInfo(target).bits; + if (bits == 0) return Air.Inst.Ref.zero; + + const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { + if (val.isUndef()) return sema.addConstUndef(operand_ty); + const result_val = try val.bitReverse(operand_ty, target, sema.arena); + return sema.addConstant(operand_ty, result_val); + } else operand_src; + + try sema.requireRuntimeBlock(block, runtime_src); + return block.addTyOp(.bit_reverse, operand_ty, operand); } fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 6e83ad72d1..cef59afd09 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -621,6 +621,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), .popcount => try self.airPopcount(inst), + .byte_swap => try self.airByteSwap(inst), + .bit_reverse => try self.airBitReverse(inst), .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), @@ -1682,6 +1684,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index e211f9d7bc..87ca182f01 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -605,6 +605,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), .popcount => try self.airPopcount(inst), + .byte_swap => try self.airByteSwap(inst), + .bit_reverse => try self.airBitReverse(inst), .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), @@ -1392,6 +1394,20 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + _ = ty_op; + return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); + // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + _ = ty_op; + return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); + // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 09ca92f229..8aa6956662 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -592,6 +592,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), .popcount => try self.airPopcount(inst), + .byte_swap => try self.airByteSwap(inst), + .bit_reverse => try self.airBitReverse(inst), .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), @@ -1181,6 +1183,18 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index bcad7cac19..2bd6b92963 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -1687,6 +1687,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { .clz, .ctz, .popcount, + .byte_swap, + .bit_reverse, .is_err_ptr, .is_non_err_ptr, .fptrunc, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index bf49aedd2d..4e5a3f1e82 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -686,6 +686,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), .popcount => try self.airPopcount(inst), + .byte_swap => try self.airByteSwap(inst), + .bit_reverse => try self.airBitReverse(inst), .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), @@ -1716,6 +1718,24 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const result: MCValue = if (self.liveness.isUnused(inst)) + .dead + else + return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 0391fa4c84..5fd3bd92ca 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1709,6 +1709,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .clz => try airBuiltinCall(f, inst, "clz"), .ctz => try airBuiltinCall(f, inst, "ctz"), .popcount => try airBuiltinCall(f, inst, "popcount"), + .byte_swap => try airBuiltinCall(f, inst, "byte_swap"), + .bit_reverse => try airBuiltinCall(f, inst, "bit_reverse"), .tag_name => try airTagName(f, inst), .error_name => try airErrorName(f, inst), .splat => try airSplat(f, inst), diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 3d65829c1e..05e377864b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2205,7 +2205,9 @@ pub const FuncGen = struct { .get_union_tag => try self.airGetUnionTag(inst), .clz => try self.airClzCtz(inst, "llvm.ctlz"), .ctz => try self.airClzCtz(inst, "llvm.cttz"), - .popcount => try self.airPopCount(inst), + .popcount => try self.airBitOp(inst, "llvm.ctpop"), + .byte_swap => try self.airByteSwap(inst, "llvm.bswap"), + .bit_reverse => try self.airBitOp(inst, "llvm.bitreverse"), .tag_name => try self.airTagName(inst), .error_name => try self.airErrorName(inst), .splat => try self.airSplat(inst), @@ -4348,7 +4350,7 @@ pub const FuncGen = struct { } } - fn airPopCount(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + fn airBitOp(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[inst].ty_op; @@ -4357,7 +4359,7 @@ pub const FuncGen = struct { const params = [_]*const llvm.Value{operand}; const operand_llvm_ty = try self.dg.llvmType(operand_ty); - const fn_val = self.getIntrinsic("llvm.ctpop", &.{operand_llvm_ty}); + const fn_val = self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); const result_ty = self.air.typeOfIndex(inst); @@ -4375,6 +4377,44 @@ pub const FuncGen = struct { } } + fn airByteSwap(self: *FuncGen, inst: Air.Inst.Index, llvm_fn_name: []const u8) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const target = self.dg.module.getTarget(); + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand_ty = self.air.typeOf(ty_op.operand); + var bits = operand_ty.intInfo(target).bits; + assert(bits % 8 == 0); + + var operand = try self.resolveInst(ty_op.operand); + var operand_llvm_ty = try self.dg.llvmType(operand_ty); + + if (bits % 16 == 8) { + // If not an even byte-multiple, we need zero-extend + shift-left 1 byte + // The truncated result at the end will be the correct bswap + operand_llvm_ty = self.context.intType(bits + 8); + const extended = self.builder.buildZExt(operand, operand_llvm_ty, ""); + operand = self.builder.buildShl(extended, operand_llvm_ty.constInt(8, .False), ""); + bits = bits + 8; + } + + const params = [_]*const llvm.Value{operand}; + const fn_val = self.getIntrinsic(llvm_fn_name, &.{operand_llvm_ty}); + + const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, params.len, .C, .Auto, ""); + + const result_ty = self.air.typeOfIndex(inst); + const result_llvm_ty = try self.dg.llvmType(result_ty); + const result_bits = result_ty.intInfo(target).bits; + if (bits > result_bits) { + return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, ""); + } else if (bits < result_bits) { + return self.builder.buildZExt(wrong_size_result, result_llvm_ty, ""); + } else { + return wrong_size_result; + } + } + fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; diff --git a/src/print_air.zig b/src/print_air.zig index bcadf31e74..a8158fb7b0 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -216,6 +216,8 @@ const Writer = struct { .clz, .ctz, .popcount, + .byte_swap, + .bit_reverse, => try w.writeTyOp(s, inst), .block, diff --git a/src/value.zig b/src/value.zig index 25073d3194..84167e394f 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1334,6 +1334,45 @@ pub const Value = extern union { } } + pub fn bitReverse(val: Value, ty: Type, target: Target, arena: Allocator) !Value { + assert(!val.isUndef()); + + const info = ty.intInfo(target); + + var buffer: Value.BigIntSpace = undefined; + const operand_bigint = val.toBigInt(&buffer); + + const limbs = try arena.alloc( + std.math.big.Limb, + std.math.big.int.calcTwosCompLimbCount(info.bits), + ); + var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; + result_bigint.bitReverse(operand_bigint, info.signedness, info.bits); + + return fromBigInt(arena, result_bigint.toConst()); + } + + pub fn byteSwap(val: Value, ty: Type, target: Target, arena: Allocator) !Value { + assert(!val.isUndef()); + + const info = ty.intInfo(target); + + // Bit count must be evenly divisible by 8 + assert(info.bits % 8 == 0); + + var buffer: Value.BigIntSpace = undefined; + const operand_bigint = val.toBigInt(&buffer); + + const limbs = try arena.alloc( + std.math.big.Limb, + std.math.big.int.calcTwosCompLimbCount(info.bits), + ); + var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; + result_bigint.byteSwap(operand_bigint, info.signedness, info.bits / 8); + + return fromBigInt(arena, result_bigint.toConst()); + } + /// Asserts the value is an integer and not undefined. /// Returns the number of bits the value requires to represent stored in twos complement form. pub fn intBitCountTwosComp(self: Value, target: Target) usize { diff --git a/test/behavior.zig b/test/behavior.zig index abfd8fb0bf..713ea33d8d 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -105,6 +105,8 @@ test { if (builtin.zig_backend != .stage2_c) { // Tests that pass for stage1 and the llvm backend. _ = @import("behavior/atomics.zig"); + _ = @import("behavior/bitreverse.zig"); + _ = @import("behavior/byteswap.zig"); _ = @import("behavior/bugs/9584.zig"); _ = @import("behavior/eval.zig"); _ = @import("behavior/floatop.zig"); @@ -124,7 +126,6 @@ test { _ = @import("behavior/async_fn.zig"); } _ = @import("behavior/await_struct.zig"); - _ = @import("behavior/bitreverse.zig"); _ = @import("behavior/bugs/421.zig"); _ = @import("behavior/bugs/529.zig"); _ = @import("behavior/bugs/718.zig"); @@ -151,7 +152,6 @@ test { _ = @import("behavior/bugs/7027.zig"); _ = @import("behavior/bugs/7047.zig"); _ = @import("behavior/bugs/10147.zig"); - _ = @import("behavior/byteswap.zig"); _ = @import("behavior/const_slice_child.zig"); _ = @import("behavior/export_self_referential_type_info.zig"); _ = @import("behavior/field_parent_ptr.zig"); diff --git a/test/behavior/bitreverse.zig b/test/behavior/bitreverse.zig index 14a7d1cdbe..61de04b530 100644 --- a/test/behavior/bitreverse.zig +++ b/test/behavior/bitreverse.zig @@ -1,25 +1,32 @@ const std = @import("std"); +const builtin = @import("builtin"); const expect = std.testing.expect; const minInt = std.math.minInt; test "@bitReverse" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + comptime try testBitReverse(); try testBitReverse(); } fn testBitReverse() !void { // using comptime_ints, unsigned - try expect(@bitReverse(u0, 0) == 0); - try expect(@bitReverse(u5, 0x12) == 0x9); - try expect(@bitReverse(u8, 0x12) == 0x48); - try expect(@bitReverse(u16, 0x1234) == 0x2c48); - try expect(@bitReverse(u24, 0x123456) == 0x6a2c48); - try expect(@bitReverse(u32, 0x12345678) == 0x1e6a2c48); - try expect(@bitReverse(u40, 0x123456789a) == 0x591e6a2c48); - try expect(@bitReverse(u48, 0x123456789abc) == 0x3d591e6a2c48); - try expect(@bitReverse(u56, 0x123456789abcde) == 0x7b3d591e6a2c48); - try expect(@bitReverse(u64, 0x123456789abcdef1) == 0x8f7b3d591e6a2c48); - try expect(@bitReverse(u128, 0x123456789abcdef11121314151617181) == 0x818e868a828c84888f7b3d591e6a2c48); + try expect(@bitReverse(u0, @as(u0, 0)) == 0); + try expect(@bitReverse(u5, @as(u5, 0x12)) == 0x9); + try expect(@bitReverse(u8, @as(u8, 0x12)) == 0x48); + try expect(@bitReverse(u16, @as(u16, 0x1234)) == 0x2c48); + try expect(@bitReverse(u24, @as(u24, 0x123456)) == 0x6a2c48); + try expect(@bitReverse(u32, @as(u32, 0x12345678)) == 0x1e6a2c48); + try expect(@bitReverse(u40, @as(u40, 0x123456789a)) == 0x591e6a2c48); + try expect(@bitReverse(u48, @as(u48, 0x123456789abc)) == 0x3d591e6a2c48); + try expect(@bitReverse(u56, @as(u56, 0x123456789abcde)) == 0x7b3d591e6a2c48); + try expect(@bitReverse(u64, @as(u64, 0x123456789abcdef1)) == 0x8f7b3d591e6a2c48); + try expect(@bitReverse(u95, @as(u95, 0x123456789abcdef111213141)) == 0x4146424447bd9eac8f351624); + try expect(@bitReverse(u96, @as(u96, 0x123456789abcdef111213141)) == 0x828c84888f7b3d591e6a2c48); + try expect(@bitReverse(u128, @as(u128, 0x123456789abcdef11121314151617181)) == 0x818e868a828c84888f7b3d591e6a2c48); // using runtime uints, unsigned var num0: u0 = 0; @@ -50,11 +57,16 @@ fn testBitReverse() !void { try expect(@bitReverse(i8, @bitCast(i8, @as(u8, 0x92))) == @bitCast(i8, @as(u8, 0x49))); try expect(@bitReverse(i16, @bitCast(i16, @as(u16, 0x1234))) == @bitCast(i16, @as(u16, 0x2c48))); try expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0x123456))) == @bitCast(i24, @as(u24, 0x6a2c48))); + try expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0x12345f))) == @bitCast(i24, @as(u24, 0xfa2c48))); + try expect(@bitReverse(i24, @bitCast(i24, @as(u24, 0xf23456))) == @bitCast(i24, @as(u24, 0x6a2c4f))); try expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0x12345678))) == @bitCast(i32, @as(u32, 0x1e6a2c48))); + try expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0xf2345678))) == @bitCast(i32, @as(u32, 0x1e6a2c4f))); + try expect(@bitReverse(i32, @bitCast(i32, @as(u32, 0x1234567f))) == @bitCast(i32, @as(u32, 0xfe6a2c48))); try expect(@bitReverse(i40, @bitCast(i40, @as(u40, 0x123456789a))) == @bitCast(i40, @as(u40, 0x591e6a2c48))); try expect(@bitReverse(i48, @bitCast(i48, @as(u48, 0x123456789abc))) == @bitCast(i48, @as(u48, 0x3d591e6a2c48))); try expect(@bitReverse(i56, @bitCast(i56, @as(u56, 0x123456789abcde))) == @bitCast(i56, @as(u56, 0x7b3d591e6a2c48))); try expect(@bitReverse(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1))) == @bitCast(i64, @as(u64, 0x8f7b3d591e6a2c48))); + try expect(@bitReverse(i96, @bitCast(i96, @as(u96, 0x123456789abcdef111213141))) == @bitCast(i96, @as(u96, 0x828c84888f7b3d591e6a2c48))); try expect(@bitReverse(i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181))) == @bitCast(i128, @as(u128, 0x818e868a828c84888f7b3d591e6a2c48))); // using signed, negative. Compare to runtime ints returned from llvm. diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index 4affc99b59..ee482b916f 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -1,18 +1,29 @@ const std = @import("std"); +const builtin = @import("builtin"); const expect = std.testing.expect; test "@byteSwap integers" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const ByteSwapIntTest = struct { fn run() !void { try t(u0, 0, 0); try t(u8, 0x12, 0x12); try t(u16, 0x1234, 0x3412); try t(u24, 0x123456, 0x563412); + try t(i24, @bitCast(i24, @as(u24, 0xf23456)), 0x5634f2); + try t(i24, 0x1234f6, @bitCast(i24, @as(u24, 0xf63412))); try t(u32, 0x12345678, 0x78563412); + try t(i32, @bitCast(i32, @as(u32, 0xf2345678)), 0x785634f2); + try t(i32, 0x123456f8, @bitCast(i32, @as(u32, 0xf8563412))); try t(u40, 0x123456789a, 0x9a78563412); try t(i48, 0x123456789abc, @bitCast(i48, @as(u48, 0xbc9a78563412))); try t(u56, 0x123456789abcde, 0xdebc9a78563412); try t(u64, 0x123456789abcdef1, 0xf1debc9a78563412); + try t(u88, 0x123456789abcdef1112131, 0x312111f1debc9a78563412); + try t(u96, 0x123456789abcdef111213141, 0x41312111f1debc9a78563412); try t(u128, 0x123456789abcdef11121314151617181, 0x8171615141312111f1debc9a78563412); try t(u0, @as(u0, 0), 0); @@ -24,6 +35,8 @@ test "@byteSwap integers" { try t(i48, @bitCast(i48, @as(u48, 0x123456789abc)), @bitCast(i48, @as(u48, 0xbc9a78563412))); try t(i56, @bitCast(i56, @as(u56, 0x123456789abcde)), @bitCast(i56, @as(u56, 0xdebc9a78563412))); try t(i64, @bitCast(i64, @as(u64, 0x123456789abcdef1)), @bitCast(i64, @as(u64, 0xf1debc9a78563412))); + try t(i88, @bitCast(i88, @as(u88, 0x123456789abcdef1112131)), @bitCast(i88, @as(u88, 0x312111f1debc9a78563412))); + try t(i96, @bitCast(i96, @as(u96, 0x123456789abcdef111213141)), @bitCast(i96, @as(u96, 0x41312111f1debc9a78563412))); try t( i128, @bitCast(i128, @as(u128, 0x123456789abcdef11121314151617181)), @@ -31,7 +44,7 @@ test "@byteSwap integers" { ); } fn t(comptime I: type, input: I, expected_output: I) !void { - try std.testing.expectEqual(expected_output, @byteSwap(I, input)); + try std.testing.expect(expected_output == @byteSwap(I, input)); } }; comptime try ByteSwapIntTest.run(); @@ -39,6 +52,11 @@ test "@byteSwap integers" { } test "@byteSwap vectors" { + if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + const ByteSwapVectorTest = struct { fn run() !void { try t(u8, 2, [_]u8{ 0x12, 0x13 }, [_]u8{ 0x12, 0x13 }); From fe1d6c2f56caaf4151d3880f483fe88af9f34b6b Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Sun, 13 Feb 2022 21:38:31 -0700 Subject: [PATCH 2/3] Skip failing stage1 `@bitReverse` test --- test/behavior/bitreverse.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/behavior/bitreverse.zig b/test/behavior/bitreverse.zig index 61de04b530..9e74aa0510 100644 --- a/test/behavior/bitreverse.zig +++ b/test/behavior/bitreverse.zig @@ -24,10 +24,14 @@ fn testBitReverse() !void { try expect(@bitReverse(u48, @as(u48, 0x123456789abc)) == 0x3d591e6a2c48); try expect(@bitReverse(u56, @as(u56, 0x123456789abcde)) == 0x7b3d591e6a2c48); try expect(@bitReverse(u64, @as(u64, 0x123456789abcdef1)) == 0x8f7b3d591e6a2c48); - try expect(@bitReverse(u95, @as(u95, 0x123456789abcdef111213141)) == 0x4146424447bd9eac8f351624); try expect(@bitReverse(u96, @as(u96, 0x123456789abcdef111213141)) == 0x828c84888f7b3d591e6a2c48); try expect(@bitReverse(u128, @as(u128, 0x123456789abcdef11121314151617181)) == 0x818e868a828c84888f7b3d591e6a2c48); + if (builtin.zig_backend != .stage1) { + // Currently failing on stage1 for big-endian targets + try expect(@bitReverse(u95, @as(u95, 0x123456789abcdef111213141)) == 0x4146424447bd9eac8f351624); + } + // using runtime uints, unsigned var num0: u0 = 0; try expect(@bitReverse(u0, num0) == 0); From db80dff4e002146063609d20599a7310837074c7 Mon Sep 17 00:00:00 2001 From: Cody Tapscott Date: Tue, 15 Feb 2022 10:45:25 -0700 Subject: [PATCH 3/3] Add backend-specific skips for bitreverse, byteswap tests --- src/codegen/llvm.zig | 2 +- test/behavior.zig | 4 ++-- test/behavior/bitreverse.zig | 19 ++++++++++++++----- test/behavior/byteswap.zig | 4 ++++ 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 05e377864b..4fdd462dba 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4390,7 +4390,7 @@ pub const FuncGen = struct { var operand_llvm_ty = try self.dg.llvmType(operand_ty); if (bits % 16 == 8) { - // If not an even byte-multiple, we need zero-extend + shift-left 1 byte + // If not an even byte-multiple, we need zero-extend + shift-left 1 byte // The truncated result at the end will be the correct bswap operand_llvm_ty = self.context.intType(bits + 8); const extended = self.builder.buildZExt(operand, operand_llvm_ty, ""); diff --git a/test/behavior.zig b/test/behavior.zig index 713ea33d8d..5b575ba190 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -6,6 +6,8 @@ test { _ = @import("behavior/array.zig"); _ = @import("behavior/basic.zig"); _ = @import("behavior/bit_shifting.zig"); + _ = @import("behavior/bitreverse.zig"); + _ = @import("behavior/byteswap.zig"); _ = @import("behavior/bool.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/655.zig"); @@ -105,8 +107,6 @@ test { if (builtin.zig_backend != .stage2_c) { // Tests that pass for stage1 and the llvm backend. _ = @import("behavior/atomics.zig"); - _ = @import("behavior/bitreverse.zig"); - _ = @import("behavior/byteswap.zig"); _ = @import("behavior/bugs/9584.zig"); _ = @import("behavior/eval.zig"); _ = @import("behavior/floatop.zig"); diff --git a/test/behavior/bitreverse.zig b/test/behavior/bitreverse.zig index 9e74aa0510..ff0775eb07 100644 --- a/test/behavior/bitreverse.zig +++ b/test/behavior/bitreverse.zig @@ -3,10 +3,24 @@ const builtin = @import("builtin"); const expect = std.testing.expect; const minInt = std.math.minInt; +test "@bitReverse large exotic integer" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + // Currently failing on stage1 for big-endian targets + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + try expect(@bitReverse(u95, @as(u95, 0x123456789abcdef111213141)) == 0x4146424447bd9eac8f351624); +} + test "@bitReverse" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; comptime try testBitReverse(); try testBitReverse(); @@ -27,11 +41,6 @@ fn testBitReverse() !void { try expect(@bitReverse(u96, @as(u96, 0x123456789abcdef111213141)) == 0x828c84888f7b3d591e6a2c48); try expect(@bitReverse(u128, @as(u128, 0x123456789abcdef11121314151617181)) == 0x818e868a828c84888f7b3d591e6a2c48); - if (builtin.zig_backend != .stage1) { - // Currently failing on stage1 for big-endian targets - try expect(@bitReverse(u95, @as(u95, 0x123456789abcdef111213141)) == 0x4146424447bd9eac8f351624); - } - // using runtime uints, unsigned var num0: u0 = 0; try expect(@bitReverse(u0, num0) == 0); diff --git a/test/behavior/byteswap.zig b/test/behavior/byteswap.zig index ee482b916f..265bc40f99 100644 --- a/test/behavior/byteswap.zig +++ b/test/behavior/byteswap.zig @@ -6,6 +6,8 @@ test "@byteSwap integers" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const ByteSwapIntTest = struct { fn run() !void { @@ -56,6 +58,8 @@ test "@byteSwap vectors" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; const ByteSwapVectorTest = struct { fn run() !void {