stage2: implement @clz and @ctz

Also improve the LLVM backend to support lowering bigints to LLVM
values.

Moves over a bunch of math.zig test cases to the "passing for stage2"
section.
This commit is contained in:
Andrew Kelley
2021-09-28 22:38:51 -07:00
parent 7efc2a0626
commit 33e77f127d
12 changed files with 379 additions and 216 deletions

View File

@@ -160,6 +160,14 @@ pub const Inst = struct {
/// Result type is the return type of the function being called.
/// Uses the `pl_op` field with the `Call` payload. operand is the callee.
call,
/// Count leading zeroes of 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.
clz,
/// Count trailing zeroes of 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.
ctz,
/// `<`. Result type is always bool.
/// Uses the `bin_op` field.
@@ -669,6 +677,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.float_to_int,
.int_to_float,
.get_union_tag,
.clz,
.ctz,
=> return air.getRefType(datas[inst].ty_op.ty),
.loop,

View File

@@ -304,6 +304,8 @@ fn analyzeInst(
.float_to_int,
.int_to_float,
.get_union_tag,
.clz,
.ctz,
=> {
const o = inst_datas[inst].ty_op;
return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });

View File

@@ -4611,8 +4611,8 @@ fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
const dest_type = try sema.resolveType(block, dest_ty_src, extra.lhs);
const operand = sema.resolveInst(extra.rhs);
const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_type);
_ = try sema.requireIntegerType(block, operand_src, sema.typeOf(operand));
const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_type);
_ = try sema.checkIntType(block, operand_src, sema.typeOf(operand));
if (try sema.isComptimeKnown(block, operand_src, operand)) {
return sema.coerce(block, dest_type, operand, operand_src);
@@ -8384,7 +8384,7 @@ fn zirIntToFloat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile
const operand = sema.resolveInst(extra.rhs);
const operand_ty = sema.typeOf(operand);
try sema.checkIntType(block, ty_src, dest_ty);
_ = try sema.checkIntType(block, ty_src, dest_ty);
try sema.checkFloatType(block, operand_src, operand_ty);
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
@@ -8493,8 +8493,8 @@ fn zirTruncate(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
const operand = sema.resolveInst(extra.rhs);
const operand_ty = sema.typeOf(operand);
const mod = sema.mod;
const dest_is_comptime_int = try sema.requireIntegerType(block, dest_ty_src, dest_ty);
const src_is_comptime_int = try sema.requireIntegerType(block, operand_src, operand_ty);
const dest_is_comptime_int = try sema.checkIntType(block, dest_ty_src, dest_ty);
const src_is_comptime_int = try sema.checkIntType(block, operand_src, operand_ty);
if (dest_is_comptime_int) {
return sema.coerce(block, dest_ty, operand, operand_src);
@@ -8552,14 +8552,56 @@ fn zirAlignCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
fn zirClz(sema: *Sema, block: *Scope.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.mod.fail(&block.base, src, "TODO: Sema.zirClz", .{});
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.mod.fail(&block.base, 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);
return sema.addIntUnsigned(result_ty, val.clz(operand_ty, target));
} else operand_src;
try sema.requireRuntimeBlock(block, runtime_src);
return block.addTyOp(.clz, result_ty, operand);
}
fn zirCtz(sema: *Sema, block: *Scope.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.mod.fail(&block.base, src, "TODO: Sema.zirCtz", .{});
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.mod.fail(&block.base, 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);
return sema.mod.fail(&block.base, operand_src, "TODO: implement comptime @ctz", .{});
} else operand_src;
try sema.requireRuntimeBlock(block, runtime_src);
return block.addTyOp(.ctz, result_ty, operand);
}
fn zirPopCount(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -8616,17 +8658,12 @@ fn zirOffsetOf(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
return sema.mod.fail(&block.base, src, "TODO: Sema.zirOffsetOf", .{});
}
fn checkIntType(
sema: *Sema,
block: *Scope.Block,
ty_src: LazySrcLoc,
ty: Type,
) CompileError!void {
/// Returns `true` if the type was a comptime_int.
fn checkIntType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!bool {
switch (ty.zigTypeTag()) {
.ComptimeInt, .Int => {},
else => return sema.mod.fail(&block.base, ty_src, "expected integer type, found '{}'", .{
ty,
}),
.ComptimeInt => return true,
.Int => return false,
else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),
}
}
@@ -9416,14 +9453,6 @@ fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void
try sema.requireFunctionBlock(block, src);
}
fn requireIntegerType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !bool {
switch (ty.zigTypeTag()) {
.ComptimeInt => return true,
.Int => return false,
else => return sema.mod.fail(&block.base, src, "expected integer type, found '{}'", .{ty}),
}
}
/// Emit a compile error if type cannot be used for a runtime variable.
fn validateVarType(
sema: *Sema,

View File

@@ -896,6 +896,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.memset => try self.airMemset(inst),
.set_union_tag => try self.airSetUnionTag(inst),
.get_union_tag => try self.airGetUnionTag(inst),
.clz => try self.airClz(inst),
.ctz => try self.airCtz(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -1606,6 +1608,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airClz(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 airClz for {}", .{self.target.cpu.arch}),
};
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
fn airCtz(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 airCtz 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;

View File

@@ -962,6 +962,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.memcpy => try airMemcpy(f, inst),
.set_union_tag => try airSetUnionTag(f, inst),
.get_union_tag => try airGetUnionTag(f, inst),
.clz => try airBuiltinCall(f, inst, "clz"),
.ctz => try airBuiltinCall(f, inst, "ctz"),
.int_to_float,
.float_to_int,
@@ -2075,6 +2077,23 @@ fn airSimpleCast(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
const inst_ty = f.air.typeOfIndex(inst);
const local = try f.allocLocal(inst_ty, .Const);
const ty_op = f.air.instructions.items(.data)[inst].ty_op;
const writer = f.object.writer();
const operand = try f.resolveInst(ty_op.operand);
// TODO implement the function in zig.h and call it here
try writer.print(" = {s}(", .{fn_name});
try f.writeCValue(writer, operand);
try writer.writeAll(");\n");
return local;
}
fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;

View File

@@ -889,14 +889,14 @@ pub const DeclGen = struct {
const llvm_type = try self.llvmType(tv.ty);
if (bigint.eqZero()) return llvm_type.constNull();
if (bigint.limbs.len != 1) {
return self.todo("implement bigger bigint", .{});
}
const llvm_int = llvm_type.constInt(bigint.limbs[0], .False);
const unsigned_val = if (bigint.limbs.len == 1)
llvm_type.constInt(bigint.limbs[0], .False)
else
llvm_type.constIntOfArbitraryPrecision(@intCast(c_uint, bigint.limbs.len), bigint.limbs.ptr);
if (!bigint.positive) {
return llvm.constNeg(llvm_int);
return llvm.constNeg(unsigned_val);
}
return llvm_int;
return unsigned_val;
},
.Enum => {
const llvm_type = try self.llvmType(tv.ty);
@@ -1310,6 +1310,8 @@ pub const FuncGen = struct {
.memcpy => try self.airMemcpy(inst),
.set_union_tag => try self.airSetUnionTag(inst),
.get_union_tag => try self.airGetUnionTag(inst),
.clz => try self.airClzCtz(inst, "ctlz"),
.ctz => try self.airClzCtz(inst, "cttz"),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -2699,6 +2701,41 @@ pub const FuncGen = struct {
return self.builder.buildExtractValue(un, 1, "");
}
fn airClzCtz(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 llvm_i1 = self.context.intType(1);
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, llvm_i1 };
const fn_type = llvm.functionType(operand_llvm_ty, &param_types, param_types.len, .False);
break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
};
const params = [_]*const llvm.Value{ operand, llvm_i1.constNull() };
const wrong_size_result = self.builder.buildCall(fn_val, &params, params.len, "");
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 fieldPtr(
self: *FuncGen,
inst: Air.Inst.Index,

View File

@@ -172,6 +172,9 @@ pub const Type = opaque {
pub const constInt = LLVMConstInt;
extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value;
pub const constIntOfArbitraryPrecision = LLVMConstIntOfArbitraryPrecision;
extern fn LLVMConstIntOfArbitraryPrecision(IntTy: *const Type, NumWords: c_uint, Words: [*]const u64) *const Value;
pub const constReal = LLVMConstReal;
extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value;
@@ -300,7 +303,7 @@ extern fn LLVMGetInlineAsm(
pub const functionType = LLVMFunctionType;
extern fn LLVMFunctionType(
ReturnType: *const Type,
ParamTypes: [*]*const Type,
ParamTypes: [*]const *const Type,
ParamCount: c_uint,
IsVarArg: Bool,
) *const Type;
@@ -346,7 +349,7 @@ pub const Builder = opaque {
extern fn LLVMBuildCall(
*const Builder,
Fn: *const Value,
Args: [*]*const Value,
Args: [*]const *const Value,
NumArgs: c_uint,
Name: [*:0]const u8,
) *const Value;

View File

@@ -186,6 +186,8 @@ const Writer = struct {
.int_to_float,
.float_to_int,
.get_union_tag,
.clz,
.ctz,
=> try w.writeTyOp(s, inst),
.block,

View File

@@ -3902,16 +3902,16 @@ pub const Type = extern union {
const bits = bits: {
if (max == 0) break :bits 0;
const base = std.math.log2(max);
const upper = (@as(u64, 1) << base) - 1;
const upper = (@as(u64, 1) << @intCast(u6, base)) - 1;
break :bits base + @boolToInt(upper < max);
};
return switch (bits) {
return switch (@intCast(u16, bits)) {
1 => initTag(.u1),
8 => initTag(.u8),
16 => initTag(.u16),
32 => initTag(.u32),
64 => initTag(.u64),
else => return Tag.int_unsigned.create(arena, bits),
else => |b| return Tag.int_unsigned.create(arena, b),
};
}
};

View File

@@ -962,6 +962,45 @@ pub const Value = extern union {
};
}
pub fn clz(val: Value, ty: Type, target: Target) u64 {
const ty_bits = ty.intInfo(target).bits;
switch (val.tag()) {
.zero, .bool_false => return ty_bits,
.one, .bool_true => return ty_bits - 1,
.int_u64 => {
const big = @clz(u64, val.castTag(.int_u64).?.data);
return big + ty_bits - 64;
},
.int_i64 => {
@panic("TODO implement i64 Value clz");
},
.int_big_positive => {
// TODO: move this code into std lib big ints
const bigint = val.castTag(.int_big_positive).?.asBigInt();
// Limbs are stored in little-endian order but we need
// to iterate big-endian.
var total_limb_lz: u64 = 0;
var i: usize = bigint.limbs.len;
const bits_per_limb = @sizeOf(std.math.big.Limb) * 8;
while (i != 0) {
i -= 1;
const limb = bigint.limbs[i];
const this_limb_lz = @clz(std.math.big.Limb, limb);
total_limb_lz += this_limb_lz;
if (this_limb_lz != bits_per_limb) break;
}
const total_limb_bits = bigint.limbs.len * bits_per_limb;
return total_limb_lz + ty_bits - total_limb_bits;
},
.int_big_negative => {
@panic("TODO implement int_big_negative Value clz");
},
else => unreachable,
}
}
/// 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) usize {

View File

@@ -53,3 +53,185 @@ fn testThreeExprInARow(f: bool, t: bool) !void {
fn assertFalse(b: bool) !void {
try expect(!b);
}
test "@clz" {
try testClz();
comptime try testClz();
}
fn testClz() !void {
try expect(testOneClz(u8, 0b10001010) == 0);
try expect(testOneClz(u8, 0b00001010) == 4);
try expect(testOneClz(u8, 0b00011010) == 3);
try expect(testOneClz(u8, 0b00000000) == 8);
try expect(testOneClz(u128, 0xffffffffffffffff) == 64);
try expect(testOneClz(u128, 0x10000000000000000) == 63);
}
fn testOneClz(comptime T: type, x: T) u32 {
return @clz(T, x);
}
test "const number literal" {
const one = 1;
const eleven = ten + one;
try expect(eleven == 11);
}
const ten = 10;
test "float equality" {
const x: f64 = 0.012;
const y: f64 = x + 1.0;
try testFloatEqualityImpl(x, y);
comptime try testFloatEqualityImpl(x, y);
}
fn testFloatEqualityImpl(x: f64, y: f64) !void {
const y2 = x + 1.0;
try expect(y == y2);
}
test "hex float literal parsing" {
comptime try expect(0x1.0 == 1.0);
}
test "quad hex float literal parsing in range" {
const a = 0x1.af23456789bbaaab347645365cdep+5;
const b = 0x1.dedafcff354b6ae9758763545432p-9;
const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534;
const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;
_ = a;
_ = b;
_ = c;
_ = d;
}
test "underscore separator parsing" {
try expect(0_0_0_0 == 0);
try expect(1_234_567 == 1234567);
try expect(001_234_567 == 1234567);
try expect(0_0_1_2_3_4_5_6_7 == 1234567);
try expect(0b0_0_0_0 == 0);
try expect(0b1010_1010 == 0b10101010);
try expect(0b0000_1010_1010 == 0b10101010);
try expect(0b1_0_1_0_1_0_1_0 == 0b10101010);
try expect(0o0_0_0_0 == 0);
try expect(0o1010_1010 == 0o10101010);
try expect(0o0000_1010_1010 == 0o10101010);
try expect(0o1_0_1_0_1_0_1_0 == 0o10101010);
try expect(0x0_0_0_0 == 0);
try expect(0x1010_1010 == 0x10101010);
try expect(0x0000_1010_1010 == 0x10101010);
try expect(0x1_0_1_0_1_0_1_0 == 0x10101010);
try expect(123_456.789_000e1_0 == 123456.789000e10);
try expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10);
try expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10);
try expect(0x1_2_3_4_5_6_7_8.9_A_B_C_D_E_F_0p-0_0_0_1_0 == 0x12345678.9ABCDEF0p-10);
}
test "hex float literal within range" {
const a = 0x1.0p16383;
const b = 0x0.1p16387;
const c = 0x1.0p-16382;
_ = a;
_ = b;
_ = c;
}
test "comptime_int addition" {
comptime {
try expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950);
try expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380);
}
}
test "comptime_int multiplication" {
comptime {
try expect(
45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,
);
try expect(
594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,
);
}
}
test "comptime_int shifting" {
comptime {
try expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000);
}
}
test "comptime_int multi-limb shift and mask" {
comptime {
var a = 0xefffffffa0000001eeeeeeefaaaaaaab;
try expect(@as(u32, a & 0xffffffff) == 0xaaaaaaab);
a >>= 32;
try expect(@as(u32, a & 0xffffffff) == 0xeeeeeeef);
a >>= 32;
try expect(@as(u32, a & 0xffffffff) == 0xa0000001);
a >>= 32;
try expect(@as(u32, a & 0xffffffff) == 0xefffffff);
a >>= 32;
try expect(a == 0);
}
}
test "comptime_int multi-limb partial shift right" {
comptime {
var a = 0x1ffffffffeeeeeeee;
a >>= 16;
try expect(a == 0x1ffffffffeeee);
}
}
test "xor" {
try test_xor();
comptime try test_xor();
}
fn test_xor() !void {
try testOneXor(0xFF, 0x00, 0xFF);
try testOneXor(0xF0, 0x0F, 0xFF);
try testOneXor(0xFF, 0xF0, 0x0F);
try testOneXor(0xFF, 0x0F, 0xF0);
try testOneXor(0xFF, 0xFF, 0x00);
}
fn testOneXor(a: u8, b: u8, c: u8) !void {
try expect(a ^ b == c);
}
test "comptime_int xor" {
comptime {
try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF);
try expect(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000);
try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000);
try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF);
try expect(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000);
}
}
test "comptime_int param and return" {
const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
try expect(a == 137114567242441932203689521744947848950);
const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768);
try expect(b == 985095453608931032642182098849559179469148836107390954364380);
}
fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
return a + b;
}

View File

@@ -117,20 +117,6 @@ test "@*WithOverflow with u0 values" {
try expect(!@shlWithOverflow(u0, 0, 0, &result));
}
test "@clz" {
try testClz();
comptime try testClz();
}
fn testClz() !void {
try expect(@clz(u8, 0b10001010) == 0);
try expect(@clz(u8, 0b00001010) == 4);
try expect(@clz(u8, 0b00011010) == 3);
try expect(@clz(u8, 0b00000000) == 8);
try expect(@clz(u128, 0xffffffffffffffff) == 64);
try expect(@clz(u128, 0x10000000000000000) == 63);
}
test "@clz vectors" {
try testClzVectors();
comptime try testClzVectors();
@@ -171,14 +157,6 @@ fn testCtzVectors() !void {
try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));
}
test "const number literal" {
const one = 1;
const eleven = ten + one;
try expect(eleven == 11);
}
const ten = 10;
test "unsigned wrapping" {
try testUnsignedWrappingEval(maxInt(u32));
comptime try testUnsignedWrappingEval(maxInt(u32));
@@ -274,19 +252,6 @@ test "small int addition" {
try expect(result == 0);
}
test "float equality" {
const x: f64 = 0.012;
const y: f64 = x + 1.0;
try testFloatEqualityImpl(x, y);
comptime try testFloatEqualityImpl(x, y);
}
fn testFloatEqualityImpl(x: f64, y: f64) !void {
const y2 = x + 1.0;
try expect(y == y2);
}
test "allow signed integer division/remainder when values are comptime known and positive or exact" {
try expect(5 / 3 == 1);
try expect(-5 / -3 == 1);
@@ -296,23 +261,6 @@ test "allow signed integer division/remainder when values are comptime known and
try expect(-6 % 3 == 0);
}
test "hex float literal parsing" {
comptime try expect(0x1.0 == 1.0);
}
test "quad hex float literal parsing in range" {
const a = 0x1.af23456789bbaaab347645365cdep+5;
const b = 0x1.dedafcff354b6ae9758763545432p-9;
const c = 0x1.2f34dd5f437e849b4baab754cdefp+4534;
const d = 0x1.edcbff8ad76ab5bf46463233214fp-435;
if (false) {
a;
b;
c;
d;
}
}
test "quad hex float literal parsing accurate" {
const a: f128 = 0x1.1111222233334444555566667777p+0;
@@ -403,45 +351,6 @@ test "quad hex float literal parsing accurate" {
comptime try S.doTheTest();
}
test "underscore separator parsing" {
try expect(0_0_0_0 == 0);
try expect(1_234_567 == 1234567);
try expect(001_234_567 == 1234567);
try expect(0_0_1_2_3_4_5_6_7 == 1234567);
try expect(0b0_0_0_0 == 0);
try expect(0b1010_1010 == 0b10101010);
try expect(0b0000_1010_1010 == 0b10101010);
try expect(0b1_0_1_0_1_0_1_0 == 0b10101010);
try expect(0o0_0_0_0 == 0);
try expect(0o1010_1010 == 0o10101010);
try expect(0o0000_1010_1010 == 0o10101010);
try expect(0o1_0_1_0_1_0_1_0 == 0o10101010);
try expect(0x0_0_0_0 == 0);
try expect(0x1010_1010 == 0x10101010);
try expect(0x0000_1010_1010 == 0x10101010);
try expect(0x1_0_1_0_1_0_1_0 == 0x10101010);
try expect(123_456.789_000e1_0 == 123456.789000e10);
try expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10);
try expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10);
try expect(0x1_2_3_4_5_6_7_8.9_A_B_C_D_E_F_0p-0_0_0_1_0 == 0x12345678.9ABCDEF0p-10);
}
test "hex float literal within range" {
const a = 0x1.0p16383;
const b = 0x0.1p16387;
const c = 0x1.0p-16382;
if (false) {
a;
b;
c;
}
}
test "truncating shift left" {
try testShlTrunc(maxInt(u16));
comptime try testShlTrunc(maxInt(u16));
@@ -497,81 +406,6 @@ test "shift left/right on u0 operand" {
comptime try S.doTheTest();
}
test "comptime_int addition" {
comptime {
try expect(35361831660712422535336160538497375248 + 101752735581729509668353361206450473702 == 137114567242441932203689521744947848950);
try expect(594491908217841670578297176641415611445982232488944558774612 + 390603545391089362063884922208143568023166603618446395589768 == 985095453608931032642182098849559179469148836107390954364380);
}
}
test "comptime_int multiplication" {
comptime {
try expect(
45960427431263824329884196484953148229 * 128339149605334697009938835852565949723 == 5898522172026096622534201617172456926982464453350084962781392314016180490567,
);
try expect(
594491908217841670578297176641415611445982232488944558774612 * 390603545391089362063884922208143568023166603618446395589768 == 232210647056203049913662402532976186578842425262306016094292237500303028346593132411865381225871291702600263463125370016,
);
}
}
test "comptime_int shifting" {
comptime {
try expect((@as(u128, 1) << 127) == 0x80000000000000000000000000000000);
}
}
test "comptime_int multi-limb shift and mask" {
comptime {
var a = 0xefffffffa0000001eeeeeeefaaaaaaab;
try expect(@as(u32, a & 0xffffffff) == 0xaaaaaaab);
a >>= 32;
try expect(@as(u32, a & 0xffffffff) == 0xeeeeeeef);
a >>= 32;
try expect(@as(u32, a & 0xffffffff) == 0xa0000001);
a >>= 32;
try expect(@as(u32, a & 0xffffffff) == 0xefffffff);
a >>= 32;
try expect(a == 0);
}
}
test "comptime_int multi-limb partial shift right" {
comptime {
var a = 0x1ffffffffeeeeeeee;
a >>= 16;
try expect(a == 0x1ffffffffeeee);
}
}
test "xor" {
try test_xor();
comptime try test_xor();
}
fn test_xor() !void {
try expect(0xFF ^ 0x00 == 0xFF);
try expect(0xF0 ^ 0x0F == 0xFF);
try expect(0xFF ^ 0xF0 == 0x0F);
try expect(0xFF ^ 0x0F == 0xF0);
try expect(0xFF ^ 0xFF == 0x00);
}
test "comptime_int xor" {
comptime {
try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0x00000000000000000000000000000000 == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0x0000000000000000FFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try expect(0xFFFFFFFFFFFFFFFF0000000000000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x0000000000000000FFFFFFFFFFFFFFFF);
try expect(0x0000000000000000FFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFFFFFFFFFF0000000000000000);
try expect(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000000000000000000000000000);
try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0x00000000FFFFFFFF00000000FFFFFFFF == 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
try expect(0xFFFFFFFF00000000FFFFFFFF00000000 ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0x00000000FFFFFFFF00000000FFFFFFFF);
try expect(0x00000000FFFFFFFF00000000FFFFFFFF ^ 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF == 0xFFFFFFFF00000000FFFFFFFF00000000);
}
}
test "f128" {
try test_f128();
comptime try test_f128();
@@ -757,18 +591,6 @@ fn testRound(comptime T: type, x: T) !void {
try expectEqual(x, z);
}
test "comptime_int param and return" {
const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
try expect(a == 137114567242441932203689521744947848950);
const b = comptimeAdd(594491908217841670578297176641415611445982232488944558774612, 390603545391089362063884922208143568023166603618446395589768);
try expect(b == 985095453608931032642182098849559179469148836107390954364380);
}
fn comptimeAdd(comptime a: comptime_int, comptime b: comptime_int) comptime_int {
return a + b;
}
test "vector integer addition" {
const S = struct {
fn doTheTest() !void {