mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 21:38:33 +00:00
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:
parent
7efc2a0626
commit
33e77f127d
10
src/Air.zig
10
src/Air.zig
@ -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,
|
||||
|
||||
@ -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 });
|
||||
|
||||
83
src/Sema.zig
83
src/Sema.zig
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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, ¶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, llvm_i1.constNull() };
|
||||
const wrong_size_result = self.builder.buildCall(fn_val, ¶ms, 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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -186,6 +186,8 @@ const Writer = struct {
|
||||
.int_to_float,
|
||||
.float_to_int,
|
||||
.get_union_tag,
|
||||
.clz,
|
||||
.ctz,
|
||||
=> try w.writeTyOp(s, inst),
|
||||
|
||||
.block,
|
||||
|
||||
@ -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),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user