From 33e77f127d8237088b561fae2ca0f4412bc1d6c9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 28 Sep 2021 22:38:51 -0700 Subject: [PATCH] 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. --- src/Air.zig | 10 ++ src/Liveness.zig | 2 + src/Sema.zig | 83 +++++++++++----- src/codegen.zig | 18 ++++ src/codegen/c.zig | 19 ++++ src/codegen/llvm.zig | 49 +++++++-- src/codegen/llvm/bindings.zig | 7 +- src/print_air.zig | 2 + src/type.zig | 6 +- src/value.zig | 39 ++++++++ test/behavior/math.zig | 182 ++++++++++++++++++++++++++++++++++ test/behavior/math_stage1.zig | 178 --------------------------------- 12 files changed, 379 insertions(+), 216 deletions(-) diff --git a/src/Air.zig b/src/Air.zig index cdc5ff2287..f7eccfd5a5 100644 --- a/src/Air.zig +++ b/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, diff --git a/src/Liveness.zig b/src/Liveness.zig index 93f28ad7b2..71a0414383 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -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 }); diff --git a/src/Sema.zig b/src/Sema.zig index 0fb93f3fbe..51ebb496f3 100644 --- a/src/Sema.zig +++ b/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, diff --git a/src/codegen.zig b/src/codegen.zig index 79105dc4a7..dfaedf041a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 95ce95f2e5..d2ce9cc6de 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -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; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index b27afa9b54..2c278221ae 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -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, diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 4fac6656c8..68d91f6c68 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -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; diff --git a/src/print_air.zig b/src/print_air.zig index 885c1b62bd..dda3b4458b 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -186,6 +186,8 @@ const Writer = struct { .int_to_float, .float_to_int, .get_union_tag, + .clz, + .ctz, => try w.writeTyOp(s, inst), .block, diff --git a/src/type.zig b/src/type.zig index e13ede852a..cb2cc6d58a 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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), }; } }; diff --git a/src/value.zig b/src/value.zig index 0ead2ff1d9..ac52654041 100644 --- a/src/value.zig +++ b/src/value.zig @@ -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 { diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 510cc3d438..56fbdc124d 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -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; +} diff --git a/test/behavior/math_stage1.zig b/test/behavior/math_stage1.zig index 9f412930b5..f0c160ebc4 100644 --- a/test/behavior/math_stage1.zig +++ b/test/behavior/math_stage1.zig @@ -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 {