From d6067db06267e37dec65202667741bc1b63fe980 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 29 Oct 2021 17:48:34 -0700 Subject: [PATCH] stage2: implement `@popCount` for non-vectors --- lib/std/math/big/int.zig | 30 +++++ lib/std/math/big/int_test.zig | 11 ++ src/Air.zig | 5 + src/Liveness.zig | 1 + src/Sema.zig | 26 +++- src/arch/aarch64/CodeGen.zig | 7 + src/codegen.zig | 9 ++ src/codegen/c.zig | 1 + src/codegen/llvm.zig | 35 +++++ src/print_air.zig | 1 + src/value.zig | 216 ++++++++---------------------- test/behavior.zig | 3 +- test/behavior/popcount.zig | 16 --- test/behavior/popcount_stage1.zig | 24 ++++ 14 files changed, 209 insertions(+), 176 deletions(-) create mode 100644 test/behavior/popcount_stage1.zig diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 5edf5b3ce5..6e4571648d 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -733,6 +733,27 @@ pub const Mutable = struct { rma.truncate(rma.toConst(), signedness, bit_count); } + /// r = @popCount(a) with 2s-complement semantics. + /// r and a may be aliases. + /// + /// Assets the result fits in `r`. Upper bound on the number of limbs needed by + /// r is `calcTwosCompLimbCount(bit_count)`. + pub fn popCount(r: *Mutable, a: Const, bit_count: usize) void { + r.copy(a); + + if (!a.positive) { + r.positive = true; // Negate. + r.bitNotWrap(r.toConst(), .unsigned, bit_count); // Bitwise NOT. + r.addScalar(r.toConst(), 1); // Add one. + } + + var sum: Limb = 0; + for (r.limbs[0..r.len]) |limb| { + sum += @popCount(Limb, limb); + } + r.set(sum); + } + /// rma = a * a /// /// `rma` may not alias with `a`. @@ -2735,6 +2756,15 @@ pub const Managed = struct { m.saturate(a, signedness, bit_count); r.setMetadata(m.positive, m.len); } + + /// r = @popCount(a) with 2s-complement semantics. + /// r and a may be aliases. + pub fn popCount(r: *Managed, a: Const, bit_count: usize) !void { + try r.ensureCapacity(calcTwosCompLimbCount(bit_count)); + var m = r.toMutable(); + m.popCount(a, bit_count); + r.setMetadata(m.positive, m.len); + } }; /// Different operators which can be used in accumulation style functions diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 5975cf4896..d226f20083 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -2434,3 +2434,14 @@ test "big.int regression test for realloc with alias" { try testing.expect(a.toConst().orderAgainstScalar(14691098406862188148944207245954912110548093601382197697835) == .eq); } + +test "big int popcount" { + var a = try Managed.initSet(testing.allocator, -1); + defer a.deinit(); + var b = try Managed.initSet(testing.allocator, -1); + defer b.deinit(); + + try a.popCount(b.toConst(), 16); + + try testing.expect(a.toConst().orderAgainstScalar(16) == .eq); +} diff --git a/src/Air.zig b/src/Air.zig index d39a78f1ad..9902ccbf56 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -202,6 +202,10 @@ pub const Inst = struct { /// Result type will always be an unsigned integer big enough to fit the answer. /// Uses the `ty_op` field. ctz, + /// Count number of 1 bits in an integer according to its representation in twos complement. + /// Result type will always be an unsigned integer big enough to fit the answer. + /// Uses the `ty_op` field. + popcount, /// `<`. Result type is always bool. /// Uses the `bin_op` field. @@ -744,6 +748,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .get_union_tag, .clz, .ctz, + .popcount, => return air.getRefType(datas[inst].ty_op.ty), .loop, diff --git a/src/Liveness.zig b/src/Liveness.zig index 499500fddb..1210390971 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -313,6 +313,7 @@ fn analyzeInst( .get_union_tag, .clz, .ctz, + .popcount, => { const o = inst_datas[inst].ty_op; return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none }); diff --git a/src/Sema.zig b/src/Sema.zig index 83b81ef174..0788997576 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9904,8 +9904,30 @@ fn zirCtz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins fn zirPopCount(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.zirPopCount", .{}); + 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 result_ty = try Type.smallestUnsignedInt(sema.arena, bits); + + const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { + if (val.isUndef()) return sema.addConstUndef(result_ty); + const result_val = try val.popCount(operand_ty, target, sema.arena); + return sema.addConstant(result_ty, result_val); + } else operand_src; + + try sema.requireRuntimeBlock(block, runtime_src); + return block.addTyOp(.popcount, result_ty, operand); } fn zirByteSwap(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 2c6feec70c..9331fb249e 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -481,6 +481,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .get_union_tag => try self.airGetUnionTag(inst), .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), + .popcount => try self.airPopcount(inst), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), @@ -1138,6 +1139,12 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airPopcount(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 airPopcount for {}", .{self.target.cpu.arch}); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/codegen.zig b/src/codegen.zig index 374a9353e1..82aa9430e8 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -836,6 +836,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .get_union_tag => try self.airGetUnionTag(inst), .clz => try self.airClz(inst), .ctz => try self.airCtz(inst), + .popcount => try self.airPopcount(inst), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), @@ -1610,6 +1611,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } + fn airPopcount(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 switch (arch) { + else => return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}), + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + } + fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { if (!self.liveness.operandDies(inst, op_index)) return false; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 3ab74a85be..8e046264aa 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1107,6 +1107,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .get_union_tag => try airGetUnionTag(f, inst), .clz => try airBuiltinCall(f, inst, "clz"), .ctz => try airBuiltinCall(f, inst, "ctz"), + .popcount => try airBuiltinCall(f, inst, "popcount"), .int_to_float, .float_to_int, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 572e23ff28..683ee9b95b 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1777,6 +1777,7 @@ pub const FuncGen = struct { .get_union_tag => try self.airGetUnionTag(inst), .clz => try self.airClzCtz(inst, "ctlz"), .ctz => try self.airClzCtz(inst, "cttz"), + .popcount => try self.airPopCount(inst, "ctpop"), .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), @@ -3679,6 +3680,40 @@ pub const FuncGen = struct { } } + fn airPopCount(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const ty_op = self.air.instructions.items(.data)[inst].ty_op; + const operand_ty = self.air.typeOf(ty_op.operand); + const operand = try self.resolveInst(ty_op.operand); + const target = self.dg.module.getTarget(); + const bits = operand_ty.intInfo(target).bits; + + var fn_name_buf: [100]u8 = undefined; + const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{ + prefix, bits, + }) catch unreachable; + const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { + const operand_llvm_ty = try self.dg.llvmType(operand_ty); + const param_types = [_]*const llvm.Type{operand_llvm_ty}; + const fn_type = llvm.functionType(operand_llvm_ty, ¶m_types, param_types.len, .False); + break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); + }; + + const params = [_]*const llvm.Value{operand}; + 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 callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { return self.callFloatUnary(arg, ty, "floor"); } diff --git a/src/print_air.zig b/src/print_air.zig index 17efa8297d..56a73b3fcf 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -196,6 +196,7 @@ const Writer = struct { .get_union_tag, .clz, .ctz, + .popcount, => try w.writeTyOp(s, inst), .block, diff --git a/src/value.zig b/src/value.zig index 382aeeedf7..2e3f1bc4b8 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1062,14 +1062,7 @@ pub const Value = extern union { const limbs_buffer = try arena.alloc(std.math.big.Limb, 2); var bigint = BigIntMutable.init(limbs_buffer, 0); bigint.readTwosComplement(buffer, int_info.bits, endian, int_info.signedness); - // TODO if it fits in 64 bits then use one of those tags - - const result_limbs = bigint.limbs[0..bigint.len]; - if (bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, bigint.toConst()); }, .Float => switch (ty.floatBits(target)) { 16 => return Value.Tag.float_16.create(arena, floatReadFromMemory(f16, target, buffer)), @@ -1200,16 +1193,34 @@ pub const Value = extern union { if (x == 0) return 0; return @intCast(usize, std.math.log2(x) + 1); }, - .int_i64 => { - @panic("TODO implement i64 intBitCountTwosComp"); - }, .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().bitCountTwosComp(), .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(), - else => unreachable, + else => { + var buffer: BigIntSpace = undefined; + return self.toBigInt(&buffer).bitCountTwosComp(); + }, } } + pub fn popCount(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.popCount(operand_bigint, info.bits); + + return fromBigInt(arena, result_bigint.toConst()); + } + /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { switch (self.tag()) { @@ -1246,7 +1257,8 @@ pub const Value = extern union { const info = ty.intInfo(target); if (info.signedness == .unsigned and x < 0) return false; - @panic("TODO implement i64 intFitsInType"); + var buffer: BigIntSpace = undefined; + return self.toBigInt(&buffer).fitsInTwosComp(info.signedness, info.bits); }, .ComptimeInt => return true, else => unreachable, @@ -1943,12 +1955,22 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; + return fromBigInt(arena, result_bigint.toConst()); + } - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); + fn fromBigInt(arena: *Allocator, big_int: BigIntConst) !Value { + if (big_int.positive) { + if (big_int.to(u64)) |x| { + return Value.Tag.int_u64.create(arena, x); + } else |_| { + return Value.Tag.int_big_positive.create(arena, big_int.limbs); + } } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); + if (big_int.to(i64)) |x| { + return Value.Tag.int_i64.create(arena, x); + } else |_| { + return Value.Tag.int_big_negative.create(arena, big_int.limbs); + } } } @@ -1975,13 +1997,7 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } /// Supports both floats and ints; handles undefined. @@ -2010,13 +2026,7 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } /// Supports integers only; asserts neither operand is undefined. @@ -2042,13 +2052,7 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } /// Supports both floats and ints; handles undefined. @@ -2082,13 +2086,7 @@ pub const Value = extern union { ); defer arena.free(limbs_buffer); result_bigint.mulWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits, limbs_buffer, arena); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } /// Supports integers only; asserts neither operand is undefined. @@ -2124,13 +2122,7 @@ pub const Value = extern union { defer arena.free(limbs_buffer); result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena); result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } /// Supports both floats and ints; handles undefined. @@ -2174,13 +2166,7 @@ pub const Value = extern union { var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } /// operands must be integers; handles undefined. @@ -2200,13 +2186,7 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.bitAnd(lhs_bigint, rhs_bigint); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } /// operands must be integers; handles undefined. @@ -2239,13 +2219,7 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.bitOr(lhs_bigint, rhs_bigint); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } /// operands must be integers; handles undefined. @@ -2265,13 +2239,7 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.bitXor(lhs_bigint, rhs_bigint); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value { @@ -2287,13 +2255,7 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.add(lhs_bigint, rhs_bigint); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_bigint.toConst()); } pub fn intSub(lhs: Value, rhs: Value, allocator: *Allocator) !Value { @@ -2309,13 +2271,7 @@ pub const Value = extern union { ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.sub(lhs_bigint, rhs_bigint); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_bigint.toConst()); } pub fn intDiv(lhs: Value, rhs: Value, allocator: *Allocator) !Value { @@ -2340,13 +2296,7 @@ pub const Value = extern union { var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); - const result_limbs = result_q.limbs[0..result_q.len]; - - if (result_q.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_q.toConst()); } pub fn intDivFloor(lhs: Value, rhs: Value, allocator: *Allocator) !Value { @@ -2371,13 +2321,7 @@ pub const Value = extern union { var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); - const result_limbs = result_q.limbs[0..result_q.len]; - - if (result_q.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_q.toConst()); } pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { @@ -2404,13 +2348,7 @@ pub const Value = extern union { var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); - const result_limbs = result_r.limbs[0..result_r.len]; - - if (result_r.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_r.toConst()); } pub fn intMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value { @@ -2435,13 +2373,7 @@ pub const Value = extern union { var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer); - const result_limbs = result_r.limbs[0..result_r.len]; - - if (result_r.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_r.toConst()); } /// Returns true if the value is a floating point type and is NaN. Returns false otherwise. @@ -2487,13 +2419,7 @@ pub const Value = extern union { ); defer allocator.free(limbs_buffer); result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_bigint.toConst()); } pub fn intTrunc(val: Value, allocator: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Value { @@ -2507,13 +2433,7 @@ pub const Value = extern union { var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.truncate(val_bigint, signedness, bits); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_bigint.toConst()); } pub fn shl(lhs: Value, rhs: Value, allocator: *Allocator) !Value { @@ -2532,13 +2452,7 @@ pub const Value = extern union { .len = undefined, }; result_bigint.shiftLeft(lhs_bigint, shift); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_bigint.toConst()); } pub fn shlSat( @@ -2565,13 +2479,7 @@ pub const Value = extern union { .len = undefined, }; result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(arena, result_limbs); - } else { - return Value.Tag.int_big_negative.create(arena, result_limbs); - } + return fromBigInt(arena, result_bigint.toConst()); } pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value { @@ -2590,13 +2498,7 @@ pub const Value = extern union { .len = undefined, }; result_bigint.shiftRight(lhs_bigint, shift); - const result_limbs = result_bigint.limbs[0..result_bigint.len]; - - if (result_bigint.positive) { - return Value.Tag.int_big_positive.create(allocator, result_limbs); - } else { - return Value.Tag.int_big_negative.create(allocator, result_limbs); - } + return fromBigInt(allocator, result_bigint.toConst()); } pub fn floatAdd( diff --git a/test/behavior.zig b/test/behavior.zig index c320a3930d..3b84e6f5e6 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -50,6 +50,7 @@ test { _ = @import("behavior/null.zig"); _ = @import("behavior/optional.zig"); _ = @import("behavior/pointers.zig"); + _ = @import("behavior/popcount.zig"); _ = @import("behavior/ptrcast.zig"); _ = @import("behavior/pub_enum.zig"); _ = @import("behavior/saturating_arithmetic.zig"); @@ -153,7 +154,7 @@ test { _ = @import("behavior/null_stage1.zig"); _ = @import("behavior/optional_stage1.zig"); _ = @import("behavior/pointers_stage1.zig"); - _ = @import("behavior/popcount.zig"); + _ = @import("behavior/popcount_stage1.zig"); _ = @import("behavior/ptrcast_stage1.zig"); _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("behavior/reflection.zig"); diff --git a/test/behavior/popcount.zig b/test/behavior/popcount.zig index 1ee4b33190..98493c7579 100644 --- a/test/behavior/popcount.zig +++ b/test/behavior/popcount.zig @@ -44,19 +44,3 @@ fn testPopCountIntegers() !void { try expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24); } } - -test "@popCount vectors" { - comptime try testPopCountVectors(); - try testPopCountVectors(); -} - -fn testPopCountVectors() !void { - { - var x: Vector(8, u32) = [1]u32{0xffffffff} ** 8; - try expectEqual([1]u6{32} ** 8, @as([8]u6, @popCount(u32, x))); - } - { - var x: Vector(8, i16) = [1]i16{-1} ** 8; - try expectEqual([1]u5{16} ** 8, @as([8]u5, @popCount(i16, x))); - } -} diff --git a/test/behavior/popcount_stage1.zig b/test/behavior/popcount_stage1.zig new file mode 100644 index 0000000000..3783fdfe2f --- /dev/null +++ b/test/behavior/popcount_stage1.zig @@ -0,0 +1,24 @@ +const std = @import("std"); +const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; +const Vector = std.meta.Vector; + +test "@popCount vectors" { + comptime try testPopCountVectors(); + try testPopCountVectors(); +} + +fn testPopCountVectors() !void { + { + var x: Vector(8, u32) = [1]u32{0xffffffff} ** 8; + const expected = [1]u6{32} ** 8; + const result: [8]u6 = @popCount(u32, x); + try expect(std.mem.eql(u6, &expected, &result)); + } + { + var x: Vector(8, i16) = [1]i16{-1} ** 8; + const expected = [1]u5{16} ** 8; + const result: [8]u5 = @popCount(i16, x); + try expect(std.mem.eql(u5, &expected, &result)); + } +}