authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-29 17:48:34-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-29 17:49:02-07:00
logd6067db06267e37dec65202667741bc1b63fe980
treea693698a60b30a7d0ca2764056858c8b74066cd7
parent5ff01bd820ea08005a422f046ad5bbad663b0dab

stage2: implement `@popCount` for non-vectors


14 files changed, 209 insertions(+), 176 deletions(-)

lib/std/math/big/int.zig+30
...@@ -733,6 +733,27 @@ pub const Mutable = struct {...@@ -733,6 +733,27 @@ pub const Mutable = struct {
733 rma.truncate(rma.toConst(), signedness, bit_count);733 rma.truncate(rma.toConst(), signedness, bit_count);
734 }734 }
735735
736 /// r = @popCount(a) with 2s-complement semantics.
737 /// r and a may be aliases.
738 ///
739 /// Assets the result fits in `r`. Upper bound on the number of limbs needed by
740 /// r is `calcTwosCompLimbCount(bit_count)`.
741 pub fn popCount(r: *Mutable, a: Const, bit_count: usize) void {
742 r.copy(a);
743
744 if (!a.positive) {
745 r.positive = true; // Negate.
746 r.bitNotWrap(r.toConst(), .unsigned, bit_count); // Bitwise NOT.
747 r.addScalar(r.toConst(), 1); // Add one.
748 }
749
750 var sum: Limb = 0;
751 for (r.limbs[0..r.len]) |limb| {
752 sum += @popCount(Limb, limb);
753 }
754 r.set(sum);
755 }
756
736 /// rma = a * a757 /// rma = a * a
737 ///758 ///
738 /// `rma` may not alias with `a`.759 /// `rma` may not alias with `a`.
...@@ -2735,6 +2756,15 @@ pub const Managed = struct {...@@ -2735,6 +2756,15 @@ pub const Managed = struct {
2735 m.saturate(a, signedness, bit_count);2756 m.saturate(a, signedness, bit_count);
2736 r.setMetadata(m.positive, m.len);2757 r.setMetadata(m.positive, m.len);
2737 }2758 }
2759
2760 /// r = @popCount(a) with 2s-complement semantics.
2761 /// r and a may be aliases.
2762 pub fn popCount(r: *Managed, a: Const, bit_count: usize) !void {
2763 try r.ensureCapacity(calcTwosCompLimbCount(bit_count));
2764 var m = r.toMutable();
2765 m.popCount(a, bit_count);
2766 r.setMetadata(m.positive, m.len);
2767 }
2738};2768};
27392769
2740/// Different operators which can be used in accumulation style functions2770/// Different operators which can be used in accumulation style functions
lib/std/math/big/int_test.zig+11
...@@ -2434,3 +2434,14 @@ test "big.int regression test for realloc with alias" {...@@ -2434,3 +2434,14 @@ test "big.int regression test for realloc with alias" {
24342434
2435 try testing.expect(a.toConst().orderAgainstScalar(14691098406862188148944207245954912110548093601382197697835) == .eq);2435 try testing.expect(a.toConst().orderAgainstScalar(14691098406862188148944207245954912110548093601382197697835) == .eq);
2436}2436}
2437
2438test "big int popcount" {
2439 var a = try Managed.initSet(testing.allocator, -1);
2440 defer a.deinit();
2441 var b = try Managed.initSet(testing.allocator, -1);
2442 defer b.deinit();
2443
2444 try a.popCount(b.toConst(), 16);
2445
2446 try testing.expect(a.toConst().orderAgainstScalar(16) == .eq);
2447}
src/Air.zig+5
...@@ -202,6 +202,10 @@ pub const Inst = struct {...@@ -202,6 +202,10 @@ pub const Inst = struct {
202 /// Result type will always be an unsigned integer big enough to fit the answer.202 /// Result type will always be an unsigned integer big enough to fit the answer.
203 /// Uses the `ty_op` field.203 /// Uses the `ty_op` field.
204 ctz,204 ctz,
205 /// Count number of 1 bits in an integer according to its representation in twos complement.
206 /// Result type will always be an unsigned integer big enough to fit the answer.
207 /// Uses the `ty_op` field.
208 popcount,
205209
206 /// `<`. Result type is always bool.210 /// `<`. Result type is always bool.
207 /// Uses the `bin_op` field.211 /// Uses the `bin_op` field.
...@@ -744,6 +748,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -744,6 +748,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
744 .get_union_tag,748 .get_union_tag,
745 .clz,749 .clz,
746 .ctz,750 .ctz,
751 .popcount,
747 => return air.getRefType(datas[inst].ty_op.ty),752 => return air.getRefType(datas[inst].ty_op.ty),
748753
749 .loop,754 .loop,
src/Liveness.zig+1
...@@ -313,6 +313,7 @@ fn analyzeInst(...@@ -313,6 +313,7 @@ fn analyzeInst(
313 .get_union_tag,313 .get_union_tag,
314 .clz,314 .clz,
315 .ctz,315 .ctz,
316 .popcount,
316 => {317 => {
317 const o = inst_datas[inst].ty_op;318 const o = inst_datas[inst].ty_op;
318 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });319 return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
src/Sema.zig+24-2
...@@ -9904,8 +9904,30 @@ fn zirCtz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins...@@ -9904,8 +9904,30 @@ fn zirCtz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
99049904
9905fn zirPopCount(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9905fn zirPopCount(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9906 const inst_data = sema.code.instructions.items(.data)[inst].un_node;9906 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
9907 const src = inst_data.src();9907 const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
9908 return sema.fail(block, src, "TODO: Sema.zirPopCount", .{});9908 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
9909 const operand = sema.resolveInst(inst_data.operand);
9910 const operand_ty = sema.typeOf(operand);
9911 // TODO implement support for vectors
9912 if (operand_ty.zigTypeTag() != .Int) {
9913 return sema.fail(block, ty_src, "expected integer type, found '{}'", .{
9914 operand_ty,
9915 });
9916 }
9917 const target = sema.mod.getTarget();
9918 const bits = operand_ty.intInfo(target).bits;
9919 if (bits == 0) return Air.Inst.Ref.zero;
9920
9921 const result_ty = try Type.smallestUnsignedInt(sema.arena, bits);
9922
9923 const runtime_src = if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
9924 if (val.isUndef()) return sema.addConstUndef(result_ty);
9925 const result_val = try val.popCount(operand_ty, target, sema.arena);
9926 return sema.addConstant(result_ty, result_val);
9927 } else operand_src;
9928
9929 try sema.requireRuntimeBlock(block, runtime_src);
9930 return block.addTyOp(.popcount, result_ty, operand);
9909}9931}
99109932
9911fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9933fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/arch/aarch64/CodeGen.zig+7
...@@ -481,6 +481,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -481,6 +481,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
481 .get_union_tag => try self.airGetUnionTag(inst),481 .get_union_tag => try self.airGetUnionTag(inst),
482 .clz => try self.airClz(inst),482 .clz => try self.airClz(inst),
483 .ctz => try self.airCtz(inst),483 .ctz => try self.airCtz(inst),
484 .popcount => try self.airPopcount(inst),
484485
485 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),486 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
486 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),487 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -1138,6 +1139,12 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {...@@ -1138,6 +1139,12 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
1138 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1139 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1139}1140}
11401141
1142fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
1143 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1144 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
1145 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1146}
1147
1141fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {1148fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1142 if (!self.liveness.operandDies(inst, op_index))1149 if (!self.liveness.operandDies(inst, op_index))
1143 return false;1150 return false;
src/codegen.zig+9
...@@ -836,6 +836,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -836,6 +836,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
836 .get_union_tag => try self.airGetUnionTag(inst),836 .get_union_tag => try self.airGetUnionTag(inst),
837 .clz => try self.airClz(inst),837 .clz => try self.airClz(inst),
838 .ctz => try self.airCtz(inst),838 .ctz => try self.airCtz(inst),
839 .popcount => try self.airPopcount(inst),
839840
840 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),841 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
841 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),842 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -1610,6 +1611,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1610,6 +1611,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1610 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1611 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1611 }1612 }
16121613
1614 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
1615 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1616 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1617 else => return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}),
1618 };
1619 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1620 }
1621
1613 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {1622 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1614 if (!self.liveness.operandDies(inst, op_index))1623 if (!self.liveness.operandDies(inst, op_index))
1615 return false;1624 return false;
src/codegen/c.zig+1
...@@ -1107,6 +1107,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1107,6 +1107,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1107 .get_union_tag => try airGetUnionTag(f, inst),1107 .get_union_tag => try airGetUnionTag(f, inst),
1108 .clz => try airBuiltinCall(f, inst, "clz"),1108 .clz => try airBuiltinCall(f, inst, "clz"),
1109 .ctz => try airBuiltinCall(f, inst, "ctz"),1109 .ctz => try airBuiltinCall(f, inst, "ctz"),
1110 .popcount => try airBuiltinCall(f, inst, "popcount"),
11101111
1111 .int_to_float,1112 .int_to_float,
1112 .float_to_int,1113 .float_to_int,
src/codegen/llvm.zig+35
...@@ -1777,6 +1777,7 @@ pub const FuncGen = struct {...@@ -1777,6 +1777,7 @@ pub const FuncGen = struct {
1777 .get_union_tag => try self.airGetUnionTag(inst),1777 .get_union_tag => try self.airGetUnionTag(inst),
1778 .clz => try self.airClzCtz(inst, "ctlz"),1778 .clz => try self.airClzCtz(inst, "ctlz"),
1779 .ctz => try self.airClzCtz(inst, "cttz"),1779 .ctz => try self.airClzCtz(inst, "cttz"),
1780 .popcount => try self.airPopCount(inst, "ctpop"),
17801781
1781 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),1782 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
1782 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),1783 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
...@@ -3679,6 +3680,40 @@ pub const FuncGen = struct {...@@ -3679,6 +3680,40 @@ pub const FuncGen = struct {
3679 }3680 }
3680 }3681 }
36813682
3683 fn airPopCount(self: *FuncGen, inst: Air.Inst.Index, prefix: [*:0]const u8) !?*const llvm.Value {
3684 if (self.liveness.isUnused(inst)) return null;
3685
3686 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
3687 const operand_ty = self.air.typeOf(ty_op.operand);
3688 const operand = try self.resolveInst(ty_op.operand);
3689 const target = self.dg.module.getTarget();
3690 const bits = operand_ty.intInfo(target).bits;
3691
3692 var fn_name_buf: [100]u8 = undefined;
3693 const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.i{d}", .{
3694 prefix, bits,
3695 }) catch unreachable;
3696 const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
3697 const operand_llvm_ty = try self.dg.llvmType(operand_ty);
3698 const param_types = [_]*const llvm.Type{operand_llvm_ty};
3699 const fn_type = llvm.functionType(operand_llvm_ty, &param_types, param_types.len, .False);
3700 break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
3701 };
3702
3703 const params = [_]*const llvm.Value{operand};
3704 const wrong_size_result = self.builder.buildCall(fn_val, &params, params.len, .C, .Auto, "");
3705 const result_ty = self.air.typeOfIndex(inst);
3706 const result_llvm_ty = try self.dg.llvmType(result_ty);
3707 const result_bits = result_ty.intInfo(target).bits;
3708 if (bits > result_bits) {
3709 return self.builder.buildTrunc(wrong_size_result, result_llvm_ty, "");
3710 } else if (bits < result_bits) {
3711 return self.builder.buildZExt(wrong_size_result, result_llvm_ty, "");
3712 } else {
3713 return wrong_size_result;
3714 }
3715 }
3716
3682 fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {3717 fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
3683 return self.callFloatUnary(arg, ty, "floor");3718 return self.callFloatUnary(arg, ty, "floor");
3684 }3719 }
src/print_air.zig+1
...@@ -196,6 +196,7 @@ const Writer = struct {...@@ -196,6 +196,7 @@ const Writer = struct {
196 .get_union_tag,196 .get_union_tag,
197 .clz,197 .clz,
198 .ctz,198 .ctz,
199 .popcount,
199 => try w.writeTyOp(s, inst),200 => try w.writeTyOp(s, inst),
200201
201 .block,202 .block,
src/value.zig+59-157
...@@ -1062,14 +1062,7 @@ pub const Value = extern union {...@@ -1062,14 +1062,7 @@ pub const Value = extern union {
1062 const limbs_buffer = try arena.alloc(std.math.big.Limb, 2);1062 const limbs_buffer = try arena.alloc(std.math.big.Limb, 2);
1063 var bigint = BigIntMutable.init(limbs_buffer, 0);1063 var bigint = BigIntMutable.init(limbs_buffer, 0);
1064 bigint.readTwosComplement(buffer, int_info.bits, endian, int_info.signedness);1064 bigint.readTwosComplement(buffer, int_info.bits, endian, int_info.signedness);
1065 // TODO if it fits in 64 bits then use one of those tags1065 return fromBigInt(arena, bigint.toConst());
1066
1067 const result_limbs = bigint.limbs[0..bigint.len];
1068 if (bigint.positive) {
1069 return Value.Tag.int_big_positive.create(arena, result_limbs);
1070 } else {
1071 return Value.Tag.int_big_negative.create(arena, result_limbs);
1072 }
1073 },1066 },
1074 .Float => switch (ty.floatBits(target)) {1067 .Float => switch (ty.floatBits(target)) {
1075 16 => return Value.Tag.float_16.create(arena, floatReadFromMemory(f16, target, buffer)),1068 16 => return Value.Tag.float_16.create(arena, floatReadFromMemory(f16, target, buffer)),
...@@ -1200,16 +1193,34 @@ pub const Value = extern union {...@@ -1200,16 +1193,34 @@ pub const Value = extern union {
1200 if (x == 0) return 0;1193 if (x == 0) return 0;
1201 return @intCast(usize, std.math.log2(x) + 1);1194 return @intCast(usize, std.math.log2(x) + 1);
1202 },1195 },
1203 .int_i64 => {
1204 @panic("TODO implement i64 intBitCountTwosComp");
1205 },
1206 .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().bitCountTwosComp(),1196 .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().bitCountTwosComp(),
1207 .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(),1197 .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(),
12081198
1209 else => unreachable,1199 else => {
1200 var buffer: BigIntSpace = undefined;
1201 return self.toBigInt(&buffer).bitCountTwosComp();
1202 },
1210 }1203 }
1211 }1204 }
12121205
1206 pub fn popCount(val: Value, ty: Type, target: Target, arena: *Allocator) !Value {
1207 assert(!val.isUndef());
1208
1209 const info = ty.intInfo(target);
1210
1211 var buffer: Value.BigIntSpace = undefined;
1212 const operand_bigint = val.toBigInt(&buffer);
1213
1214 const limbs = try arena.alloc(
1215 std.math.big.Limb,
1216 std.math.big.int.calcTwosCompLimbCount(info.bits),
1217 );
1218 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1219 result_bigint.popCount(operand_bigint, info.bits);
1220
1221 return fromBigInt(arena, result_bigint.toConst());
1222 }
1223
1213 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.1224 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
1214 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {1225 pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
1215 switch (self.tag()) {1226 switch (self.tag()) {
...@@ -1246,7 +1257,8 @@ pub const Value = extern union {...@@ -1246,7 +1257,8 @@ pub const Value = extern union {
1246 const info = ty.intInfo(target);1257 const info = ty.intInfo(target);
1247 if (info.signedness == .unsigned and x < 0)1258 if (info.signedness == .unsigned and x < 0)
1248 return false;1259 return false;
1249 @panic("TODO implement i64 intFitsInType");1260 var buffer: BigIntSpace = undefined;
1261 return self.toBigInt(&buffer).fitsInTwosComp(info.signedness, info.bits);
1250 },1262 },
1251 .ComptimeInt => return true,1263 .ComptimeInt => return true,
1252 else => unreachable,1264 else => unreachable,
...@@ -1943,12 +1955,22 @@ pub const Value = extern union {...@@ -1943,12 +1955,22 @@ pub const Value = extern union {
1943 );1955 );
1944 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };1956 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1945 result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);1957 result_bigint.addWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1946 const result_limbs = result_bigint.limbs[0..result_bigint.len];1958 return fromBigInt(arena, result_bigint.toConst());
1959 }
19471960
1948 if (result_bigint.positive) {1961 fn fromBigInt(arena: *Allocator, big_int: BigIntConst) !Value {
1949 return Value.Tag.int_big_positive.create(arena, result_limbs);1962 if (big_int.positive) {
1963 if (big_int.to(u64)) |x| {
1964 return Value.Tag.int_u64.create(arena, x);
1965 } else |_| {
1966 return Value.Tag.int_big_positive.create(arena, big_int.limbs);
1967 }
1950 } else {1968 } else {
1951 return Value.Tag.int_big_negative.create(arena, result_limbs);1969 if (big_int.to(i64)) |x| {
1970 return Value.Tag.int_i64.create(arena, x);
1971 } else |_| {
1972 return Value.Tag.int_big_negative.create(arena, big_int.limbs);
1973 }
1952 }1974 }
1953 }1975 }
19541976
...@@ -1975,13 +1997,7 @@ pub const Value = extern union {...@@ -1975,13 +1997,7 @@ pub const Value = extern union {
1975 );1997 );
1976 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };1998 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
1977 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);1999 result_bigint.addSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
1978 const result_limbs = result_bigint.limbs[0..result_bigint.len];2000 return fromBigInt(arena, result_bigint.toConst());
1979
1980 if (result_bigint.positive) {
1981 return Value.Tag.int_big_positive.create(arena, result_limbs);
1982 } else {
1983 return Value.Tag.int_big_negative.create(arena, result_limbs);
1984 }
1985 }2001 }
19862002
1987 /// Supports both floats and ints; handles undefined.2003 /// Supports both floats and ints; handles undefined.
...@@ -2010,13 +2026,7 @@ pub const Value = extern union {...@@ -2010,13 +2026,7 @@ pub const Value = extern union {
2010 );2026 );
2011 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2027 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2012 result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);2028 result_bigint.subWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2013 const result_limbs = result_bigint.limbs[0..result_bigint.len];2029 return fromBigInt(arena, result_bigint.toConst());
2014
2015 if (result_bigint.positive) {
2016 return Value.Tag.int_big_positive.create(arena, result_limbs);
2017 } else {
2018 return Value.Tag.int_big_negative.create(arena, result_limbs);
2019 }
2020 }2030 }
20212031
2022 /// Supports integers only; asserts neither operand is undefined.2032 /// Supports integers only; asserts neither operand is undefined.
...@@ -2042,13 +2052,7 @@ pub const Value = extern union {...@@ -2042,13 +2052,7 @@ pub const Value = extern union {
2042 );2052 );
2043 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2053 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2044 result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);2054 result_bigint.subSat(lhs_bigint, rhs_bigint, info.signedness, info.bits);
2045 const result_limbs = result_bigint.limbs[0..result_bigint.len];2055 return fromBigInt(arena, result_bigint.toConst());
2046
2047 if (result_bigint.positive) {
2048 return Value.Tag.int_big_positive.create(arena, result_limbs);
2049 } else {
2050 return Value.Tag.int_big_negative.create(arena, result_limbs);
2051 }
2052 }2056 }
20532057
2054 /// Supports both floats and ints; handles undefined.2058 /// Supports both floats and ints; handles undefined.
...@@ -2082,13 +2086,7 @@ pub const Value = extern union {...@@ -2082,13 +2086,7 @@ pub const Value = extern union {
2082 );2086 );
2083 defer arena.free(limbs_buffer);2087 defer arena.free(limbs_buffer);
2084 result_bigint.mulWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits, limbs_buffer, arena);2088 result_bigint.mulWrap(lhs_bigint, rhs_bigint, info.signedness, info.bits, limbs_buffer, arena);
2085 const result_limbs = result_bigint.limbs[0..result_bigint.len];2089 return fromBigInt(arena, result_bigint.toConst());
2086
2087 if (result_bigint.positive) {
2088 return Value.Tag.int_big_positive.create(arena, result_limbs);
2089 } else {
2090 return Value.Tag.int_big_negative.create(arena, result_limbs);
2091 }
2092 }2090 }
20932091
2094 /// Supports integers only; asserts neither operand is undefined.2092 /// Supports integers only; asserts neither operand is undefined.
...@@ -2124,13 +2122,7 @@ pub const Value = extern union {...@@ -2124,13 +2122,7 @@ pub const Value = extern union {
2124 defer arena.free(limbs_buffer);2122 defer arena.free(limbs_buffer);
2125 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);2123 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, arena);
2126 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);2124 result_bigint.saturate(result_bigint.toConst(), info.signedness, info.bits);
2127 const result_limbs = result_bigint.limbs[0..result_bigint.len];2125 return fromBigInt(arena, result_bigint.toConst());
2128
2129 if (result_bigint.positive) {
2130 return Value.Tag.int_big_positive.create(arena, result_limbs);
2131 } else {
2132 return Value.Tag.int_big_negative.create(arena, result_limbs);
2133 }
2134 }2126 }
21352127
2136 /// Supports both floats and ints; handles undefined.2128 /// Supports both floats and ints; handles undefined.
...@@ -2174,13 +2166,7 @@ pub const Value = extern union {...@@ -2174,13 +2166,7 @@ pub const Value = extern union {
21742166
2175 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2167 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2176 result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);2168 result_bigint.bitNotWrap(val_bigint, info.signedness, info.bits);
2177 const result_limbs = result_bigint.limbs[0..result_bigint.len];2169 return fromBigInt(arena, result_bigint.toConst());
2178
2179 if (result_bigint.positive) {
2180 return Value.Tag.int_big_positive.create(arena, result_limbs);
2181 } else {
2182 return Value.Tag.int_big_negative.create(arena, result_limbs);
2183 }
2184 }2170 }
21852171
2186 /// operands must be integers; handles undefined. 2172 /// operands must be integers; handles undefined.
...@@ -2200,13 +2186,7 @@ pub const Value = extern union {...@@ -2200,13 +2186,7 @@ pub const Value = extern union {
2200 );2186 );
2201 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2187 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2202 result_bigint.bitAnd(lhs_bigint, rhs_bigint);2188 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
2203 const result_limbs = result_bigint.limbs[0..result_bigint.len];2189 return fromBigInt(arena, result_bigint.toConst());
2204
2205 if (result_bigint.positive) {
2206 return Value.Tag.int_big_positive.create(arena, result_limbs);
2207 } else {
2208 return Value.Tag.int_big_negative.create(arena, result_limbs);
2209 }
2210 }2190 }
22112191
2212 /// operands must be integers; handles undefined. 2192 /// operands must be integers; handles undefined.
...@@ -2239,13 +2219,7 @@ pub const Value = extern union {...@@ -2239,13 +2219,7 @@ pub const Value = extern union {
2239 );2219 );
2240 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2220 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2241 result_bigint.bitOr(lhs_bigint, rhs_bigint);2221 result_bigint.bitOr(lhs_bigint, rhs_bigint);
2242 const result_limbs = result_bigint.limbs[0..result_bigint.len];2222 return fromBigInt(arena, result_bigint.toConst());
2243
2244 if (result_bigint.positive) {
2245 return Value.Tag.int_big_positive.create(arena, result_limbs);
2246 } else {
2247 return Value.Tag.int_big_negative.create(arena, result_limbs);
2248 }
2249 }2223 }
22502224
2251 /// operands must be integers; handles undefined. 2225 /// operands must be integers; handles undefined.
...@@ -2265,13 +2239,7 @@ pub const Value = extern union {...@@ -2265,13 +2239,7 @@ pub const Value = extern union {
2265 );2239 );
2266 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2240 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2267 result_bigint.bitXor(lhs_bigint, rhs_bigint);2241 result_bigint.bitXor(lhs_bigint, rhs_bigint);
2268 const result_limbs = result_bigint.limbs[0..result_bigint.len];2242 return fromBigInt(arena, result_bigint.toConst());
2269
2270 if (result_bigint.positive) {
2271 return Value.Tag.int_big_positive.create(arena, result_limbs);
2272 } else {
2273 return Value.Tag.int_big_negative.create(arena, result_limbs);
2274 }
2275 }2243 }
22762244
2277 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2245 pub fn intAdd(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
...@@ -2287,13 +2255,7 @@ pub const Value = extern union {...@@ -2287,13 +2255,7 @@ pub const Value = extern union {
2287 );2255 );
2288 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2256 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2289 result_bigint.add(lhs_bigint, rhs_bigint);2257 result_bigint.add(lhs_bigint, rhs_bigint);
2290 const result_limbs = result_bigint.limbs[0..result_bigint.len];2258 return fromBigInt(allocator, result_bigint.toConst());
2291
2292 if (result_bigint.positive) {
2293 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2294 } else {
2295 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2296 }
2297 }2259 }
22982260
2299 pub fn intSub(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2261 pub fn intSub(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
...@@ -2309,13 +2271,7 @@ pub const Value = extern union {...@@ -2309,13 +2271,7 @@ pub const Value = extern union {
2309 );2271 );
2310 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2272 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
2311 result_bigint.sub(lhs_bigint, rhs_bigint);2273 result_bigint.sub(lhs_bigint, rhs_bigint);
2312 const result_limbs = result_bigint.limbs[0..result_bigint.len];2274 return fromBigInt(allocator, result_bigint.toConst());
2313
2314 if (result_bigint.positive) {
2315 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2316 } else {
2317 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2318 }
2319 }2275 }
23202276
2321 pub fn intDiv(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2277 pub fn intDiv(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
...@@ -2340,13 +2296,7 @@ pub const Value = extern union {...@@ -2340,13 +2296,7 @@ pub const Value = extern union {
2340 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2296 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2341 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2297 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2342 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);2298 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2343 const result_limbs = result_q.limbs[0..result_q.len];2299 return fromBigInt(allocator, result_q.toConst());
2344
2345 if (result_q.positive) {
2346 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2347 } else {
2348 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2349 }
2350 }2300 }
23512301
2352 pub fn intDivFloor(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2302 pub fn intDivFloor(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
...@@ -2371,13 +2321,7 @@ pub const Value = extern union {...@@ -2371,13 +2321,7 @@ pub const Value = extern union {
2371 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2321 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2372 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2322 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2373 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);2323 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2374 const result_limbs = result_q.limbs[0..result_q.len];2324 return fromBigInt(allocator, result_q.toConst());
2375
2376 if (result_q.positive) {
2377 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2378 } else {
2379 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2380 }
2381 }2325 }
23822326
2383 pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2327 pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
...@@ -2404,13 +2348,7 @@ pub const Value = extern union {...@@ -2404,13 +2348,7 @@ pub const Value = extern union {
2404 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2348 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2405 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2349 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2406 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);2350 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2407 const result_limbs = result_r.limbs[0..result_r.len];2351 return fromBigInt(allocator, result_r.toConst());
2408
2409 if (result_r.positive) {
2410 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2411 } else {
2412 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2413 }
2414 }2352 }
24152353
2416 pub fn intMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2354 pub fn intMod(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
...@@ -2435,13 +2373,7 @@ pub const Value = extern union {...@@ -2435,13 +2373,7 @@ pub const Value = extern union {
2435 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2373 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2436 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2374 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2437 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);2375 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2438 const result_limbs = result_r.limbs[0..result_r.len];2376 return fromBigInt(allocator, result_r.toConst());
2439
2440 if (result_r.positive) {
2441 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2442 } else {
2443 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2444 }
2445 }2377 }
24462378
2447 /// Returns true if the value is a floating point type and is NaN. Returns false otherwise.2379 /// 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 {...@@ -2487,13 +2419,7 @@ pub const Value = extern union {
2487 );2419 );
2488 defer allocator.free(limbs_buffer);2420 defer allocator.free(limbs_buffer);
2489 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);2421 result_bigint.mul(lhs_bigint, rhs_bigint, limbs_buffer, allocator);
2490 const result_limbs = result_bigint.limbs[0..result_bigint.len];2422 return fromBigInt(allocator, result_bigint.toConst());
2491
2492 if (result_bigint.positive) {
2493 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2494 } else {
2495 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2496 }
2497 }2423 }
24982424
2499 pub fn intTrunc(val: Value, allocator: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Value {2425 pub fn intTrunc(val: Value, allocator: *Allocator, signedness: std.builtin.Signedness, bits: u16) !Value {
...@@ -2507,13 +2433,7 @@ pub const Value = extern union {...@@ -2507,13 +2433,7 @@ pub const Value = extern union {
2507 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };2433 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
25082434
2509 result_bigint.truncate(val_bigint, signedness, bits);2435 result_bigint.truncate(val_bigint, signedness, bits);
2510 const result_limbs = result_bigint.limbs[0..result_bigint.len];2436 return fromBigInt(allocator, result_bigint.toConst());
2511
2512 if (result_bigint.positive) {
2513 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2514 } else {
2515 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2516 }
2517 }2437 }
25182438
2519 pub fn shl(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2439 pub fn shl(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
...@@ -2532,13 +2452,7 @@ pub const Value = extern union {...@@ -2532,13 +2452,7 @@ pub const Value = extern union {
2532 .len = undefined,2452 .len = undefined,
2533 };2453 };
2534 result_bigint.shiftLeft(lhs_bigint, shift);2454 result_bigint.shiftLeft(lhs_bigint, shift);
2535 const result_limbs = result_bigint.limbs[0..result_bigint.len];2455 return fromBigInt(allocator, result_bigint.toConst());
2536
2537 if (result_bigint.positive) {
2538 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2539 } else {
2540 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2541 }
2542 }2456 }
25432457
2544 pub fn shlSat(2458 pub fn shlSat(
...@@ -2565,13 +2479,7 @@ pub const Value = extern union {...@@ -2565,13 +2479,7 @@ pub const Value = extern union {
2565 .len = undefined,2479 .len = undefined,
2566 };2480 };
2567 result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits);2481 result_bigint.shiftLeftSat(lhs_bigint, shift, info.signedness, info.bits);
2568 const result_limbs = result_bigint.limbs[0..result_bigint.len];2482 return fromBigInt(arena, result_bigint.toConst());
2569
2570 if (result_bigint.positive) {
2571 return Value.Tag.int_big_positive.create(arena, result_limbs);
2572 } else {
2573 return Value.Tag.int_big_negative.create(arena, result_limbs);
2574 }
2575 }2483 }
25762484
2577 pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value {2485 pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
...@@ -2590,13 +2498,7 @@ pub const Value = extern union {...@@ -2590,13 +2498,7 @@ pub const Value = extern union {
2590 .len = undefined,2498 .len = undefined,
2591 };2499 };
2592 result_bigint.shiftRight(lhs_bigint, shift);2500 result_bigint.shiftRight(lhs_bigint, shift);
2593 const result_limbs = result_bigint.limbs[0..result_bigint.len];2501 return fromBigInt(allocator, result_bigint.toConst());
2594
2595 if (result_bigint.positive) {
2596 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2597 } else {
2598 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2599 }
2600 }2502 }
26012503
2602 pub fn floatAdd(2504 pub fn floatAdd(
test/behavior.zig+2-1
...@@ -50,6 +50,7 @@ test {...@@ -50,6 +50,7 @@ test {
50 _ = @import("behavior/null.zig");50 _ = @import("behavior/null.zig");
51 _ = @import("behavior/optional.zig");51 _ = @import("behavior/optional.zig");
52 _ = @import("behavior/pointers.zig");52 _ = @import("behavior/pointers.zig");
53 _ = @import("behavior/popcount.zig");
53 _ = @import("behavior/ptrcast.zig");54 _ = @import("behavior/ptrcast.zig");
54 _ = @import("behavior/pub_enum.zig");55 _ = @import("behavior/pub_enum.zig");
55 _ = @import("behavior/saturating_arithmetic.zig");56 _ = @import("behavior/saturating_arithmetic.zig");
...@@ -153,7 +154,7 @@ test {...@@ -153,7 +154,7 @@ test {
153 _ = @import("behavior/null_stage1.zig");154 _ = @import("behavior/null_stage1.zig");
154 _ = @import("behavior/optional_stage1.zig");155 _ = @import("behavior/optional_stage1.zig");
155 _ = @import("behavior/pointers_stage1.zig");156 _ = @import("behavior/pointers_stage1.zig");
156 _ = @import("behavior/popcount.zig");157 _ = @import("behavior/popcount_stage1.zig");
157 _ = @import("behavior/ptrcast_stage1.zig");158 _ = @import("behavior/ptrcast_stage1.zig");
158 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");159 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
159 _ = @import("behavior/reflection.zig");160 _ = @import("behavior/reflection.zig");
test/behavior/popcount.zig-16
...@@ -44,19 +44,3 @@ fn testPopCountIntegers() !void {...@@ -44,19 +44,3 @@ fn testPopCountIntegers() !void {
44 try expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24);44 try expect(@popCount(i128, 0b11111111000110001100010000100001000011000011100101010001) == 24);
45 }45 }
46}46}
47
48test "@popCount vectors" {
49 comptime try testPopCountVectors();
50 try testPopCountVectors();
51}
52
53fn testPopCountVectors() !void {
54 {
55 var x: Vector(8, u32) = [1]u32{0xffffffff} ** 8;
56 try expectEqual([1]u6{32} ** 8, @as([8]u6, @popCount(u32, x)));
57 }
58 {
59 var x: Vector(8, i16) = [1]i16{-1} ** 8;
60 try expectEqual([1]u5{16} ** 8, @as([8]u5, @popCount(i16, x)));
61 }
62}
test/behavior/popcount_stage1.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const Vector = std.meta.Vector;
5
6test "@popCount vectors" {
7 comptime try testPopCountVectors();
8 try testPopCountVectors();
9}
10
11fn testPopCountVectors() !void {
12 {
13 var x: Vector(8, u32) = [1]u32{0xffffffff} ** 8;
14 const expected = [1]u6{32} ** 8;
15 const result: [8]u6 = @popCount(u32, x);
16 try expect(std.mem.eql(u6, &expected, &result));
17 }
18 {
19 var x: Vector(8, i16) = [1]i16{-1} ** 8;
20 const expected = [1]u5{16} ** 8;
21 const result: [8]u5 = @popCount(i16, x);
22 try expect(std.mem.eql(u5, &expected, &result));
23 }
24}