From 7f70c27e9d57c8234545120da81861e2cfb354b5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 21 Oct 2021 19:05:26 -0700 Subject: [PATCH] stage2: more division support AIR: * div is renamed to div_trunc. * Add div_float, div_floor, div_exact. - Implemented in Sema and LLVM codegen. C backend has a stub. Improvements to std.math.big.Int: * Add `eqZero` function to `Mutable`. * Fix incorrect results for `divFloor`. Compiler-rt: * Add muloti4 to the stage2 section. --- lib/std/math/big/int.zig | 18 ++- lib/std/math/big/int_test.zig | 57 +++++++ lib/std/special/compiler_rt.zig | 9 +- src/Air.zig | 25 +++- src/Liveness.zig | 5 +- src/Sema.zig | 253 +++++++++++++++++++++++++++----- src/arch/aarch64/CodeGen.zig | 3 +- src/codegen.zig | 3 +- src/codegen/c.zig | 3 +- src/codegen/llvm.zig | 103 ++++++++++++- src/codegen/llvm/bindings.zig | 6 + src/codegen/spirv.zig | 1 - src/codegen/wasm.zig | 2 +- src/print_air.zig | 5 +- src/value.zig | 103 ++++++++++++- test/behavior.zig | 5 +- test/behavior/array.zig | 52 +++++++ test/behavior/array_stage1.zig | 52 ------- test/behavior/eval.zig | 49 +++++++ test/behavior/eval_stage1.zig | 49 ------- test/behavior/fn.zig | 201 ------------------------- test/behavior/fn_stage1.zig | 206 ++++++++++++++++++++++++++ test/behavior/math.zig | 187 +++++++++++++++++++++++ test/behavior/math_stage1.zig | 187 ----------------------- 24 files changed, 1027 insertions(+), 557 deletions(-) create mode 100644 test/behavior/fn_stage1.zig diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 68a0c7f740..7bcd76b221 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -135,6 +135,11 @@ pub const Mutable = struct { }; } + /// Returns true if `a == 0`. + pub fn eqZero(self: Mutable) bool { + return self.toConst().eqZero(); + } + /// Asserts that the allocator owns the limbs memory. If this is not the case, /// use `toConst().toManaged()`. pub fn toManaged(self: Mutable, allocator: *Allocator) Managed { @@ -773,12 +778,15 @@ pub const Mutable = struct { div(q, r, a, b, limbs_buffer, allocator); // Trunc -> Floor. - if (!q.positive) { + if (a.positive and b.positive) return; + + if ((!q.positive or q.eqZero()) and !r.eqZero()) { const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true }; q.sub(q.toConst(), one); - r.add(q.toConst(), one); } - r.positive = b.positive; + + r.mulNoAlias(q.toConst(), b, allocator); + r.sub(a, r.toConst()); } /// q = a / b (rem r) @@ -1220,12 +1228,12 @@ pub const Mutable = struct { var x: Mutable = .{ .limbs = x_limbs, - .positive = a.positive, + .positive = true, .len = a.limbs.len - ab_zero_limb_count, }; var y: Mutable = .{ .limbs = y_limbs, - .positive = b.positive, + .positive = true, .len = b.limbs.len - ab_zero_limb_count, }; diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 00594b640f..2ca7b253e0 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -1399,6 +1399,63 @@ test "big.int div floor single-single -/-" { try testing.expect((try r.to(i32)) == er); } +test "big.int div floor no remainder negative quotient" { + const u: i32 = -0x80000000; + const v: i32 = 1; + + var a = try Managed.initSet(testing.allocator, u); + defer a.deinit(); + var b = try Managed.initSet(testing.allocator, v); + defer b.deinit(); + + var q = try Managed.init(testing.allocator); + defer q.deinit(); + var r = try Managed.init(testing.allocator); + defer r.deinit(); + try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + + try testing.expect((try q.to(i32)) == -0x80000000); + try testing.expect((try r.to(i32)) == 0); +} + +test "big.int div floor negative close to zero" { + const u: i32 = -2; + const v: i32 = 12; + + var a = try Managed.initSet(testing.allocator, u); + defer a.deinit(); + var b = try Managed.initSet(testing.allocator, v); + defer b.deinit(); + + var q = try Managed.init(testing.allocator); + defer q.deinit(); + var r = try Managed.init(testing.allocator); + defer r.deinit(); + try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + + try testing.expect((try q.to(i32)) == -1); + try testing.expect((try r.to(i32)) == 10); +} + +test "big.int div floor positive close to zero" { + const u: i32 = 10; + const v: i32 = 12; + + var a = try Managed.initSet(testing.allocator, u); + defer a.deinit(); + var b = try Managed.initSet(testing.allocator, v); + defer b.deinit(); + + var q = try Managed.init(testing.allocator); + defer q.deinit(); + var r = try Managed.init(testing.allocator); + defer r.deinit(); + try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); + + try testing.expect((try q.to(i32)) == 0); + try testing.expect((try r.to(i32)) == 10); +} + test "big.int div multi-multi with rem" { var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999); defer a.deinit(); diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 3582b93070..caf43ded19 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -74,6 +74,11 @@ comptime { @export(__getf2, .{ .name = "__gttf2", .linkage = linkage }); @export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage }); + + const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4; + @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); + const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4; + @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); } if (!builtin.zig_is_stage2) { @@ -621,10 +626,6 @@ comptime { const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3; @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage }); } - const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4; - @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage }); - const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4; - @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage }); _ = @import("compiler_rt/atomics.zig"); diff --git a/src/Air.zig b/src/Air.zig index 380e7fbbc2..d39a78f1ad 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -80,11 +80,27 @@ pub const Inst = struct { /// is the same as both operands. /// Uses the `bin_op` field. mul_sat, - /// Integer or float division. For integers, wrapping is undefined behavior. + /// Float division. /// Both operands are guaranteed to be the same type, and the result type /// is the same as both operands. /// Uses the `bin_op` field. - div, + div_float, + /// Truncating integer or float division. For integers, wrapping is undefined behavior. + /// Both operands are guaranteed to be the same type, and the result type + /// is the same as both operands. + /// Uses the `bin_op` field. + div_trunc, + /// Flooring integer or float division. For integers, wrapping is undefined behavior. + /// Both operands are guaranteed to be the same type, and the result type + /// is the same as both operands. + /// Uses the `bin_op` field. + div_floor, + /// Integer or float division. Guaranteed no remainder. + /// For integers, wrapping is undefined behavior. + /// Both operands are guaranteed to be the same type, and the result type + /// is the same as both operands. + /// Uses the `bin_op` field. + div_exact, /// Integer or float remainder division. /// Both operands are guaranteed to be the same type, and the result type /// is the same as both operands. @@ -644,7 +660,10 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { .mul, .mulwrap, .mul_sat, - .div, + .div_float, + .div_trunc, + .div_floor, + .div_exact, .rem, .mod, .bit_and, diff --git a/src/Liveness.zig b/src/Liveness.zig index 054c8e38dc..5f0919a6cb 100644 --- a/src/Liveness.zig +++ b/src/Liveness.zig @@ -233,7 +233,10 @@ fn analyzeInst( .mul, .mulwrap, .mul_sat, - .div, + .div_float, + .div_trunc, + .div_floor, + .div_exact, .rem, .mod, .ptr_add, diff --git a/src/Sema.zig b/src/Sema.zig index b76274c446..7d172eb771 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -641,9 +641,6 @@ pub fn analyzeBody( .pop_count => try sema.zirPopCount(block, inst), .byte_swap => try sema.zirByteSwap(block, inst), .bit_reverse => try sema.zirBitReverse(block, inst), - .div_exact => try sema.zirDivExact(block, inst), - .div_floor => try sema.zirDivFloor(block, inst), - .div_trunc => try sema.zirDivTrunc(block, inst), .shr_exact => try sema.zirShrExact(block, inst), .bit_offset_of => try sema.zirBitOffsetOf(block, inst), .offset_of => try sema.zirOffsetOf(block, inst), @@ -683,19 +680,22 @@ pub fn analyzeBody( .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon), .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func), - .add => try sema.zirArithmetic(block, inst, .add), - .addwrap => try sema.zirArithmetic(block, inst, .addwrap), - .add_sat => try sema.zirArithmetic(block, inst, .add_sat), - .div => try sema.zirArithmetic(block, inst, .div), - .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), - .mod => try sema.zirArithmetic(block, inst, .mod), - .rem => try sema.zirArithmetic(block, inst, .rem), - .mul => try sema.zirArithmetic(block, inst, .mul), - .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap), - .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat), - .sub => try sema.zirArithmetic(block, inst, .sub), - .subwrap => try sema.zirArithmetic(block, inst, .subwrap), - .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat), + .add => try sema.zirArithmetic(block, inst, .add), + .addwrap => try sema.zirArithmetic(block, inst, .addwrap), + .add_sat => try sema.zirArithmetic(block, inst, .add_sat), + .div => try sema.zirArithmetic(block, inst, .div), + .div_exact => try sema.zirArithmetic(block, inst, .div_exact), + .div_floor => try sema.zirArithmetic(block, inst, .div_floor), + .div_trunc => try sema.zirArithmetic(block, inst, .div_trunc), + .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem), + .mod => try sema.zirArithmetic(block, inst, .mod), + .rem => try sema.zirArithmetic(block, inst, .rem), + .mul => try sema.zirArithmetic(block, inst, .mul), + .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap), + .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat), + .sub => try sema.zirArithmetic(block, inst, .sub), + .subwrap => try sema.zirArithmetic(block, inst, .subwrap), + .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat), .maximum => try sema.zirMinMax(block, inst, .max), .minimum => try sema.zirMinMax(block, inst, .min), @@ -7350,6 +7350,9 @@ fn analyzeArithmetic( } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat }; }, .div => { + // TODO: emit compile error when .div is used on integers and there would be an + // ambiguous result between div_floor and div_trunc. + // For integers: // If the lhs is zero, then zero is returned regardless of rhs. // If the rhs is zero, compile error for division by zero. @@ -7359,9 +7362,11 @@ fn analyzeArithmetic( // * if lhs type is signed: // * if rhs is comptime-known and not -1, result is undefined // * if rhs is -1 or runtime-known, compile error because there is a - // possible value (-min_int * -1) for which division would be + // possible value (-min_int / -1) for which division would be // illegal behavior. // * if lhs type is unsigned, undef is returned regardless of rhs. + // TODO: emit runtime safety for division by zero + // // For floats: // If the rhs is zero, compile error for division by zero. // If the rhs is undefined, compile error because there is a possible @@ -7407,8 +7412,198 @@ fn analyzeArithmetic( try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), ); } - } else break :rs .{ .src = rhs_src, .air_tag = .div }; - } else break :rs .{ .src = lhs_src, .air_tag = .div }; + } else { + if (is_int) { + break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; + } else { + break :rs .{ .src = rhs_src, .air_tag = .div_float }; + } + } + } else { + if (is_int) { + break :rs .{ .src = lhs_src, .air_tag = .div_trunc }; + } else { + break :rs .{ .src = lhs_src, .air_tag = .div_float }; + } + } + }, + .div_trunc => { + // For integers: + // If the lhs is zero, then zero is returned regardless of rhs. + // If the rhs is zero, compile error for division by zero. + // If the rhs is undefined, compile error because there is a possible + // value (zero) for which the division would be illegal behavior. + // If the lhs is undefined: + // * if lhs type is signed: + // * if rhs is comptime-known and not -1, result is undefined + // * if rhs is -1 or runtime-known, compile error because there is a + // possible value (-min_int / -1) for which division would be + // illegal behavior. + // * if lhs type is unsigned, undef is returned regardless of rhs. + // TODO: emit runtime safety for division by zero + // + // For floats: + // If the rhs is zero, compile error for division by zero. + // If the rhs is undefined, compile error because there is a possible + // value (zero) for which the division would be illegal behavior. + // If the lhs is undefined, result is undefined. + if (maybe_lhs_val) |lhs_val| { + if (!lhs_val.isUndef()) { + if (lhs_val.compareWithZero(.eq)) { + return sema.addConstant(scalar_type, Value.zero); + } + } + } + if (maybe_rhs_val) |rhs_val| { + if (rhs_val.isUndef()) { + return sema.failWithUseOfUndef(block, rhs_src); + } + if (rhs_val.compareWithZero(.eq)) { + return sema.failWithDivideByZero(block, rhs_src); + } + } + if (maybe_lhs_val) |lhs_val| { + if (lhs_val.isUndef()) { + if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { + if (maybe_rhs_val) |rhs_val| { + if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { + return sema.addConstUndef(scalar_type); + } + } + return sema.failWithUseOfUndef(block, rhs_src); + } + return sema.addConstUndef(scalar_type); + } + + if (maybe_rhs_val) |rhs_val| { + if (is_int) { + return sema.addConstant( + scalar_type, + try lhs_val.intDiv(rhs_val, sema.arena), + ); + } else { + return sema.addConstant( + scalar_type, + try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena), + ); + } + } else break :rs .{ .src = rhs_src, .air_tag = .div_trunc }; + } else break :rs .{ .src = lhs_src, .air_tag = .div_trunc }; + }, + .div_floor => { + // For integers: + // If the lhs is zero, then zero is returned regardless of rhs. + // If the rhs is zero, compile error for division by zero. + // If the rhs is undefined, compile error because there is a possible + // value (zero) for which the division would be illegal behavior. + // If the lhs is undefined: + // * if lhs type is signed: + // * if rhs is comptime-known and not -1, result is undefined + // * if rhs is -1 or runtime-known, compile error because there is a + // possible value (-min_int / -1) for which division would be + // illegal behavior. + // * if lhs type is unsigned, undef is returned regardless of rhs. + // TODO: emit runtime safety for division by zero + // + // For floats: + // If the rhs is zero, compile error for division by zero. + // If the rhs is undefined, compile error because there is a possible + // value (zero) for which the division would be illegal behavior. + // If the lhs is undefined, result is undefined. + if (maybe_lhs_val) |lhs_val| { + if (!lhs_val.isUndef()) { + if (lhs_val.compareWithZero(.eq)) { + return sema.addConstant(scalar_type, Value.zero); + } + } + } + if (maybe_rhs_val) |rhs_val| { + if (rhs_val.isUndef()) { + return sema.failWithUseOfUndef(block, rhs_src); + } + if (rhs_val.compareWithZero(.eq)) { + return sema.failWithDivideByZero(block, rhs_src); + } + } + if (maybe_lhs_val) |lhs_val| { + if (lhs_val.isUndef()) { + if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) { + if (maybe_rhs_val) |rhs_val| { + if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) { + return sema.addConstUndef(scalar_type); + } + } + return sema.failWithUseOfUndef(block, rhs_src); + } + return sema.addConstUndef(scalar_type); + } + + if (maybe_rhs_val) |rhs_val| { + if (is_int) { + return sema.addConstant( + scalar_type, + try lhs_val.intDivFloor(rhs_val, sema.arena), + ); + } else { + return sema.addConstant( + scalar_type, + try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena), + ); + } + } else break :rs .{ .src = rhs_src, .air_tag = .div_floor }; + } else break :rs .{ .src = lhs_src, .air_tag = .div_floor }; + }, + .div_exact => { + // For integers: + // If the lhs is zero, then zero is returned regardless of rhs. + // If the rhs is zero, compile error for division by zero. + // If the rhs is undefined, compile error because there is a possible + // value (zero) for which the division would be illegal behavior. + // If the lhs is undefined, compile error because there is a possible + // value for which the division would result in a remainder. + // TODO: emit runtime safety for if there is a remainder + // TODO: emit runtime safety for division by zero + // + // For floats: + // If the rhs is zero, compile error for division by zero. + // If the rhs is undefined, compile error because there is a possible + // value (zero) for which the division would be illegal behavior. + // If the lhs is undefined, compile error because there is a possible + // value for which the division would result in a remainder. + if (maybe_lhs_val) |lhs_val| { + if (lhs_val.isUndef()) { + return sema.failWithUseOfUndef(block, rhs_src); + } else { + if (lhs_val.compareWithZero(.eq)) { + return sema.addConstant(scalar_type, Value.zero); + } + } + } + if (maybe_rhs_val) |rhs_val| { + if (rhs_val.isUndef()) { + return sema.failWithUseOfUndef(block, rhs_src); + } + if (rhs_val.compareWithZero(.eq)) { + return sema.failWithDivideByZero(block, rhs_src); + } + } + if (maybe_lhs_val) |lhs_val| { + if (maybe_rhs_val) |rhs_val| { + if (is_int) { + // TODO: emit compile error if there is a remainder + return sema.addConstant( + scalar_type, + try lhs_val.intDiv(rhs_val, sema.arena), + ); + } else { + // TODO: emit compile error if there is a remainder + return sema.addConstant( + scalar_type, + try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena), + ); + } + } else break :rs .{ .src = rhs_src, .air_tag = .div_exact }; + } else break :rs .{ .src = lhs_src, .air_tag = .div_exact }; }, .mul => { // For integers: @@ -8824,7 +9019,7 @@ fn analyzeRet( fn floatOpAllowed(tag: Zir.Inst.Tag) bool { // extend this swich as additional operators are implemented return switch (tag) { - .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true, + .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true, else => false, }; } @@ -9602,24 +9797,6 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! return sema.fail(block, src, "TODO: Sema.zirBitReverse", .{}); } -fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirDivExact", .{}); -} - -fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirDivFloor", .{}); -} - -fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { - const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirDivTrunc", .{}); -} - fn zirShrExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 8dd4526f4d..155ac6de68 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -410,7 +410,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .mul => try self.airMul(inst), .mulwrap => try self.airMulWrap(inst), .mul_sat => try self.airMulSat(inst), - .div => try self.airDiv(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), .shl, .shl_exact => try self.airShl(inst), @@ -419,6 +418,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), + .cmp_lt => try self.airCmp(inst, .lt), .cmp_lte => try self.airCmp(inst, .lte), .cmp_eq => try self.airCmp(inst, .eq), diff --git a/src/codegen.zig b/src/codegen.zig index 5963cdd376..ac9eaeda2a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -758,7 +758,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .mul => try self.airMul(inst), .mulwrap => try self.airMulWrap(inst), .mul_sat => try self.airMulSat(inst), - .div => try self.airDiv(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), .shl, .shl_exact => try self.airShl(inst), @@ -767,6 +766,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .max => try self.airMax(inst), .slice => try self.airSlice(inst), + .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), + .cmp_lt => try self.airCmp(inst, .lt), .cmp_lte => try self.airCmp(inst, .lte), .cmp_eq => try self.airCmp(inst, .eq), diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 9050499fce..aa1ece7ba3 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -976,7 +976,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .mul => try airBinOp (f, inst, " * "), // TODO use a different strategy for div that communicates to the optimizer // that wrapping is UB. - .div => try airBinOp( f, inst, " / "), + .div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "), + .div_floor => try airBinOp( f, inst, " divfloor "), .rem => try airBinOp( f, inst, " % "), .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a607b7a753..4fc4e866ba 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1667,7 +1667,10 @@ pub const FuncGen = struct { .mul => try self.airMul(inst), .mulwrap => try self.airMulWrap(inst), .mul_sat => try self.airMulSat(inst), - .div => try self.airDiv(inst), + .div_float => try self.airDivFloat(inst), + .div_trunc => try self.airDivTrunc(inst), + .div_floor => try self.airDivFloor(inst), + .div_exact => try self.airDivExact(inst), .rem => try self.airRem(inst), .mod => try self.airMod(inst), .ptr_add => try self.airPtrAdd(inst), @@ -2830,9 +2833,70 @@ pub const FuncGen = struct { return self.builder.buildUMulFixSat(lhs, rhs, ""); } - fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { - if (self.liveness.isUnused(inst)) - return null; + fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + + return self.builder.buildFDiv(lhs, rhs, ""); + } + + fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const inst_ty = self.air.typeOfIndex(inst); + + if (inst_ty.isRuntimeFloat()) { + const result_llvm_ty = try self.dg.llvmType(inst_ty); + const zero = result_llvm_ty.constNull(); + const result = self.builder.buildFDiv(lhs, rhs, ""); + const ceiled = try self.callCeil(result, inst_ty); + const floored = try self.callFloor(result, inst_ty); + const ltz = self.builder.buildFCmp(.OLT, lhs, zero, ""); + return self.builder.buildSelect(ltz, ceiled, floored, ""); + } + if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); + return self.builder.buildUDiv(lhs, rhs, ""); + } + + fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const bin_op = self.air.instructions.items(.data)[inst].bin_op; + const lhs = try self.resolveInst(bin_op.lhs); + const rhs = try self.resolveInst(bin_op.rhs); + const inst_ty = self.air.typeOfIndex(inst); + + if (inst_ty.isRuntimeFloat()) { + const result = self.builder.buildFDiv(lhs, rhs, ""); + return try self.callFloor(result, inst_ty); + } + if (inst_ty.isSignedInt()) { + // const d = @divTrunc(a, b); + // const r = @rem(a, b); + // return if (r == 0) d else d - ((a < 0) ^ (b < 0)); + const result_llvm_ty = try self.dg.llvmType(inst_ty); + const zero = result_llvm_ty.constNull(); + const div_trunc = self.builder.buildSDiv(lhs, rhs, ""); + const rem = self.builder.buildSRem(lhs, rhs, ""); + const rem_eq_0 = self.builder.buildICmp(.EQ, rem, zero, ""); + const a_lt_0 = self.builder.buildICmp(.SLT, lhs, zero, ""); + const b_lt_0 = self.builder.buildICmp(.SLT, rhs, zero, ""); + const a_b_xor = self.builder.buildXor(a_lt_0, b_lt_0, ""); + const a_b_xor_ext = self.builder.buildZExt(a_b_xor, div_trunc.typeOf(), ""); + const d_sub_xor = self.builder.buildSub(div_trunc, a_b_xor_ext, ""); + return self.builder.buildSelect(rem_eq_0, div_trunc, d_sub_xor, ""); + } + return self.builder.buildUDiv(lhs, rhs, ""); + } + + fn airDivExact(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[inst].bin_op; const lhs = try self.resolveInst(bin_op.lhs); @@ -2840,8 +2904,8 @@ pub const FuncGen = struct { const inst_ty = self.air.typeOfIndex(inst); if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, ""); - if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, ""); - return self.builder.buildUDiv(lhs, rhs, ""); + if (inst_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, ""); + return self.builder.buildExactUDiv(lhs, rhs, ""); } fn airRem(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { @@ -3546,6 +3610,33 @@ pub const FuncGen = struct { } } + fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { + return self.callFloatUnary(arg, ty, "floor"); + } + + fn callCeil(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value { + return self.callFloatUnary(arg, ty, "ceil"); + } + + fn callFloatUnary(self: *FuncGen, arg: *const llvm.Value, ty: Type, name: []const u8) !*const llvm.Value { + const target = self.dg.module.getTarget(); + + var fn_name_buf: [100]u8 = undefined; + const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.f{d}", .{ + name, ty.floatBits(target), + }) catch unreachable; + + const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: { + const operand_llvm_ty = try self.dg.llvmType(ty); + const param_types = [_]*const llvm.Type{operand_llvm_ty}; + const fn_type = llvm.functionType(operand_llvm_ty, ¶m_types, param_types.len, .False); + break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); + }; + + const args: [1]*const llvm.Value = .{arg}; + return self.builder.buildCall(llvm_fn, &args, args.len, .C, .Auto, ""); + } + fn fieldPtr( self: *FuncGen, inst: Air.Inst.Index, diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 297450a032..9a62fa791f 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -756,6 +756,12 @@ pub const Builder = opaque { pub const buildSMin = ZigLLVMBuildSMin; extern fn ZigLLVMBuildSMin(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value; + + pub const buildExactUDiv = LLVMBuildExactUDiv; + extern fn LLVMBuildExactUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; + + pub const buildExactSDiv = LLVMBuildExactSDiv; + extern fn LLVMBuildExactSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value; }; pub const IntPredicate = enum(c_uint) { diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 25a1d228e0..da2fa66fee 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -669,7 +669,6 @@ pub const DeclGen = struct { .add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}), .sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}), .mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}), - .div => try self.airArithOp(inst, .{.OpFDiv, .OpSDiv, .OpUDiv}), .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd), .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr), diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index f0d9e43439..75e6a1d78e 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -822,7 +822,7 @@ pub const Context = struct { .subwrap => self.airWrapBinOp(inst, .sub), .mul => self.airBinOp(inst, .mul), .mulwrap => self.airWrapBinOp(inst, .mul), - .div => self.airBinOp(inst, .div), + .div_trunc => self.airBinOp(inst, .div), .bit_and => self.airBinOp(inst, .@"and"), .bit_or => self.airBinOp(inst, .@"or"), .bool_and => self.airBinOp(inst, .@"and"), diff --git a/src/print_air.zig b/src/print_air.zig index a6d99eee9f..5aec562241 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -111,7 +111,10 @@ const Writer = struct { .mul, .mulwrap, .mul_sat, - .div, + .div_float, + .div_trunc, + .div_floor, + .div_exact, .rem, .mod, .ptr_add, diff --git a/src/value.zig b/src/value.zig index bb904cea30..57fe6a9c19 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2200,7 +2200,8 @@ pub const Value = extern union { const rhs_bigint = rhs.toBigInt(&rhs_space); const limbs = try arena.alloc( std.math.big.Limb, - std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), + // + 1 for negatives + std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.bitAnd(lhs_bigint, rhs_bigint); @@ -2264,7 +2265,8 @@ pub const Value = extern union { const rhs_bigint = rhs.toBigInt(&rhs_space); const limbs = try arena.alloc( std.math.big.Limb, - std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len), + // + 1 for negatives + std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, ); var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; result_bigint.bitXor(lhs_bigint, rhs_bigint); @@ -2352,7 +2354,7 @@ pub const Value = extern union { } } - pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { + pub fn intDivFloor(lhs: Value, rhs: Value, allocator: *Allocator) !Value { // TODO is this a performance issue? maybe we should try the operation without // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; @@ -2373,6 +2375,39 @@ pub const Value = extern union { ); var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; + result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null); + const result_limbs = result_q.limbs[0..result_q.len]; + + if (result_q.positive) { + return Value.Tag.int_big_positive.create(allocator, result_limbs); + } else { + return Value.Tag.int_big_negative.create(allocator, result_limbs); + } + } + + pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value { + // TODO is this a performance issue? maybe we should try the operation without + // resorting to BigInt first. + var lhs_space: Value.BigIntSpace = undefined; + var rhs_space: Value.BigIntSpace = undefined; + const lhs_bigint = lhs.toBigInt(&lhs_space); + const rhs_bigint = rhs.toBigInt(&rhs_space); + const limbs_q = try allocator.alloc( + std.math.big.Limb, + lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1, + ); + const limbs_r = try allocator.alloc( + std.math.big.Limb, + // TODO: audit this size, and also consider reworking Sema to re-use Values rather than + // always producing new Value objects. + rhs_bigint.limbs.len + 1, + ); + const limbs_buffer = try allocator.alloc( + std.math.big.Limb, + std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len), + ); + var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined }; + var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined }; result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null); const result_limbs = result_r.limbs[0..result_r.len]; @@ -2662,6 +2697,68 @@ pub const Value = extern union { } } + pub fn floatDivFloor( + lhs: Value, + rhs: Value, + float_type: Type, + arena: *Allocator, + ) !Value { + switch (float_type.tag()) { + .f16 => { + const lhs_val = lhs.toFloat(f16); + const rhs_val = rhs.toFloat(f16); + return Value.Tag.float_16.create(arena, @divFloor(lhs_val, rhs_val)); + }, + .f32 => { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + return Value.Tag.float_32.create(arena, @divFloor(lhs_val, rhs_val)); + }, + .f64 => { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val)); + }, + .f128, .comptime_float, .c_longdouble => { + const lhs_val = lhs.toFloat(f128); + const rhs_val = rhs.toFloat(f128); + return Value.Tag.float_128.create(arena, @divFloor(lhs_val, rhs_val)); + }, + else => unreachable, + } + } + + pub fn floatDivTrunc( + lhs: Value, + rhs: Value, + float_type: Type, + arena: *Allocator, + ) !Value { + switch (float_type.tag()) { + .f16 => { + const lhs_val = lhs.toFloat(f16); + const rhs_val = rhs.toFloat(f16); + return Value.Tag.float_16.create(arena, @divTrunc(lhs_val, rhs_val)); + }, + .f32 => { + const lhs_val = lhs.toFloat(f32); + const rhs_val = rhs.toFloat(f32); + return Value.Tag.float_32.create(arena, @divTrunc(lhs_val, rhs_val)); + }, + .f64 => { + const lhs_val = lhs.toFloat(f64); + const rhs_val = rhs.toFloat(f64); + return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val)); + }, + .f128, .comptime_float, .c_longdouble => { + const lhs_val = lhs.toFloat(f128); + const rhs_val = rhs.toFloat(f128); + return Value.Tag.float_128.create(arena, @divTrunc(lhs_val, rhs_val)); + }, + else => unreachable, + } + } + pub fn floatMul( lhs: Value, rhs: Value, diff --git a/test/behavior.zig b/test/behavior.zig index be55bb85e6..b4d2c276bd 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -33,6 +33,7 @@ test { _ = @import("behavior/error.zig"); _ = @import("behavior/eval.zig"); _ = @import("behavior/floatop.zig"); + _ = @import("behavior/fn.zig"); _ = @import("behavior/for.zig"); _ = @import("behavior/generics.zig"); _ = @import("behavior/hasdecl.zig"); @@ -126,7 +127,7 @@ test { _ = @import("behavior/eval_stage1.zig"); _ = @import("behavior/field_parent_ptr.zig"); _ = @import("behavior/floatop_stage1.zig"); - _ = @import("behavior/fn.zig"); + _ = @import("behavior/fn_stage1.zig"); _ = @import("behavior/fn_delegation.zig"); _ = @import("behavior/fn_in_struct_in_comptime.zig"); _ = @import("behavior/for_stage1.zig"); @@ -150,7 +151,7 @@ test { _ = @import("behavior/reflection.zig"); { // Checklist for getting saturating_arithmetic.zig passing for stage2: - // * add __muloti4 to compiler-rt + // * add __udivti3 to compiler-rt _ = @import("behavior/saturating_arithmetic.zig"); } _ = @import("behavior/select.zig"); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 0b4babca67..3ff339b140 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -112,3 +112,55 @@ test "void arrays" { try expect(@sizeOf(@TypeOf(array)) == 0); try expect(array.len == 4); } + +test "nested arrays" { + const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; + for (array_of_strings) |s, i| { + if (i == 0) try expect(mem.eql(u8, s, "hello")); + if (i == 1) try expect(mem.eql(u8, s, "this")); + if (i == 2) try expect(mem.eql(u8, s, "is")); + if (i == 3) try expect(mem.eql(u8, s, "my")); + if (i == 4) try expect(mem.eql(u8, s, "thing")); + } +} + +var s_array: [8]Sub = undefined; +const Sub = struct { b: u8 }; +const Str = struct { a: []Sub }; +test "set global var array via slice embedded in struct" { + var s = Str{ .a = s_array[0..] }; + + s.a[0].b = 1; + s.a[1].b = 2; + s.a[2].b = 3; + + try expect(s_array[0].b == 1); + try expect(s_array[1].b == 2); + try expect(s_array[2].b == 3); +} + +test "implicit comptime in array type size" { + var arr: [plusOne(10)]bool = undefined; + try expect(arr.len == 11); +} + +fn plusOne(x: u32) u32 { + return x + 1; +} + +test "read/write through global variable array of struct fields initialized via array mult" { + const S = struct { + fn doTheTest() !void { + try expect(storage[0].term == 1); + storage[0] = MyStruct{ .term = 123 }; + try expect(storage[0].term == 123); + } + + pub const MyStruct = struct { + term: usize, + }; + + var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; + }; + try S.doTheTest(); +} diff --git a/test/behavior/array_stage1.zig b/test/behavior/array_stage1.zig index 62860a9deb..b374c9dd06 100644 --- a/test/behavior/array_stage1.zig +++ b/test/behavior/array_stage1.zig @@ -4,32 +4,6 @@ const mem = std.mem; const expect = testing.expect; const expectEqual = testing.expectEqual; -test "nested arrays" { - const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" }; - for (array_of_strings) |s, i| { - if (i == 0) try expect(mem.eql(u8, s, "hello")); - if (i == 1) try expect(mem.eql(u8, s, "this")); - if (i == 2) try expect(mem.eql(u8, s, "is")); - if (i == 3) try expect(mem.eql(u8, s, "my")); - if (i == 4) try expect(mem.eql(u8, s, "thing")); - } -} - -var s_array: [8]Sub = undefined; -const Sub = struct { b: u8 }; -const Str = struct { a: []Sub }; -test "set global var array via slice embedded in struct" { - var s = Str{ .a = s_array[0..] }; - - s.a[0].b = 1; - s.a[1].b = 2; - s.a[2].b = 3; - - try expect(s_array[0].b == 1); - try expect(s_array[1].b == 2); - try expect(s_array[2].b == 3); -} - test "single-item pointer to array indexing and slicing" { try testSingleItemPtrArrayIndexSlice(); comptime try testSingleItemPtrArrayIndexSlice(); @@ -75,15 +49,6 @@ test "comptime evaluating function that takes array by value" { _ = comptime testArrayByValAtComptime(arr); } -test "implicit comptime in array type size" { - var arr: [plusOne(10)]bool = undefined; - try expect(arr.len == 11); -} - -fn plusOne(x: u32) u32 { - return x + 1; -} - test "runtime initialize array elem and then implicit cast to slice" { var two: i32 = 2; const x: []const i32 = &[_]i32{two}; @@ -171,23 +136,6 @@ test "double nested array to const slice cast in array literal" { comptime try S.entry(2); } -test "read/write through global variable array of struct fields initialized via array mult" { - const S = struct { - fn doTheTest() !void { - try expect(storage[0].term == 1); - storage[0] = MyStruct{ .term = 123 }; - try expect(storage[0].term == 123); - } - - pub const MyStruct = struct { - term: usize, - }; - - var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; - }; - try S.doTheTest(); -} - test "implicit cast zero sized array ptr to slice" { { var b = "".*; diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 83e121101a..84af3ecfbb 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -402,3 +402,52 @@ test "f64 at compile time is lossy" { test { comptime try expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192); } + +fn copyWithPartialInline(s: []u32, b: []u8) void { + comptime var i: usize = 0; + inline while (i < 4) : (i += 1) { + s[i] = 0; + s[i] |= @as(u32, b[i * 4 + 0]) << 24; + s[i] |= @as(u32, b[i * 4 + 1]) << 16; + s[i] |= @as(u32, b[i * 4 + 2]) << 8; + s[i] |= @as(u32, b[i * 4 + 3]) << 0; + } +} + +test "binary math operator in partially inlined function" { + var s: [4]u32 = undefined; + var b: [16]u8 = undefined; + + for (b) |*r, i| + r.* = @intCast(u8, i + 1); + + copyWithPartialInline(s[0..], b[0..]); + try expect(s[0] == 0x1020304); + try expect(s[1] == 0x5060708); + try expect(s[2] == 0x90a0b0c); + try expect(s[3] == 0xd0e0f10); +} + +test "comptime shl" { + var a: u128 = 3; + var b: u7 = 63; + var c: u128 = 3 << 63; + try expect((a << b) == c); +} + +test "comptime bitwise operators" { + comptime { + try expect(3 & 1 == 1); + try expect(3 & -1 == 3); + try expect(-3 & -1 == -3); + try expect(3 | -1 == -1); + try expect(-3 | -1 == -1); + try expect(3 ^ -1 == -4); + try expect(-3 ^ -1 == 2); + try expect(~@as(i8, -1) == 0); + try expect(~@as(i128, -1) == 0); + try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611); + try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); + try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff); + } +} diff --git a/test/behavior/eval_stage1.zig b/test/behavior/eval_stage1.zig index 2f9f33cfc5..348c685a26 100644 --- a/test/behavior/eval_stage1.zig +++ b/test/behavior/eval_stage1.zig @@ -132,31 +132,6 @@ test "string literal used as comptime slice is memoized" { comptime try expect(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node); } -fn copyWithPartialInline(s: []u32, b: []u8) void { - comptime var i: usize = 0; - inline while (i < 4) : (i += 1) { - s[i] = 0; - s[i] |= @as(u32, b[i * 4 + 0]) << 24; - s[i] |= @as(u32, b[i * 4 + 1]) << 16; - s[i] |= @as(u32, b[i * 4 + 2]) << 8; - s[i] |= @as(u32, b[i * 4 + 3]) << 0; - } -} - -test "binary math operator in partially inlined function" { - var s: [4]u32 = undefined; - var b: [16]u8 = undefined; - - for (b) |*r, i| - r.* = @intCast(u8, i + 1); - - copyWithPartialInline(s[0..], b[0..]); - try expect(s[0] == 0x1020304); - try expect(s[1] == 0x5060708); - try expect(s[2] == 0x90a0b0c); - try expect(s[3] == 0xd0e0f10); -} - test "comptime function with mutable pointer is not memoized" { comptime { var x: i32 = 1; @@ -203,13 +178,6 @@ test "comptime shlWithOverflow" { try expect(ct_shifted == rt_shifted); } -test "comptime shl" { - var a: u128 = 3; - var b: u7 = 63; - var c: u128 = 3 << 63; - try expectEqual(a << b, c); -} - test "runtime 128 bit integer division" { var a: u128 = 152313999999999991610955792383; var b: u128 = 10000000000000000000; @@ -278,23 +246,6 @@ test "bit shift a u1" { try expect(y == 1); } -test "comptime bitwise operators" { - comptime { - try expect(3 & 1 == 1); - try expect(3 & -1 == 3); - try expect(-3 & -1 == -3); - try expect(3 | -1 == -1); - try expect(-3 | -1 == -1); - try expect(3 ^ -1 == -4); - try expect(-3 ^ -1 == 2); - try expect(~@as(i8, -1) == 0); - try expect(~@as(i128, -1) == 0); - try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611); - try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615); - try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff); - } -} - test "*align(1) u16 is the same as *align(1:0:2) u16" { comptime { try expect(*align(1:0:2) u16 == *align(1) u16); diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 2567582cd3..b00c2f7a59 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -19,17 +19,6 @@ fn testLocVars(b: i32) void { if (a + b != 3) unreachable; } -test "void parameters" { - try voidFun(1, void{}, 2, {}); -} -fn voidFun(a: i32, b: void, c: i32, d: void) !void { - _ = d; - const v = b; - const vv: void = if (a == 1) v else {}; - try expect(a + c == 3); - return vv; -} - test "mutable local variables" { var zero: i32 = 0; try expect(zero == 0); @@ -54,14 +43,6 @@ test "separate block scopes" { try expect(c == 10); } -test "call function with empty string" { - acceptsString(""); -} - -fn acceptsString(foo: []u8) void { - _ = foo; -} - fn @"weird function name"() i32 { return 1234; } @@ -69,51 +50,6 @@ test "weird function name" { try expect(@"weird function name"() == 1234); } -test "implicit cast function unreachable return" { - wantsFnWithVoid(fnWithUnreachable); -} - -fn wantsFnWithVoid(f: fn () void) void { - _ = f; -} - -fn fnWithUnreachable() noreturn { - unreachable; -} - -test "function pointers" { - const fns = [_]@TypeOf(fn1){ - fn1, - fn2, - fn3, - fn4, - }; - for (fns) |f, i| { - try expect(f() == @intCast(u32, i) + 5); - } -} -fn fn1() u32 { - return 5; -} -fn fn2() u32 { - return 6; -} -fn fn3() u32 { - return 7; -} -fn fn4() u32 { - return 8; -} - -test "number literal as an argument" { - try numberLiteralArg(3); - comptime try numberLiteralArg(3); -} - -fn numberLiteralArg(a: anytype) !void { - try expect(a == 3); -} - test "assign inline fn to const variable" { const a = inlineFn; a(); @@ -121,64 +57,6 @@ test "assign inline fn to const variable" { inline fn inlineFn() void {} -test "pass by non-copying value" { - try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); -} - -const Point = struct { - x: i32, - y: i32, -}; - -fn addPointCoords(pt: Point) i32 { - return pt.x + pt.y; -} - -test "pass by non-copying value through var arg" { - try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3); -} - -fn addPointCoordsVar(pt: anytype) !i32 { - comptime try expect(@TypeOf(pt) == Point); - return pt.x + pt.y; -} - -test "pass by non-copying value as method" { - var pt = Point2{ .x = 1, .y = 2 }; - try expect(pt.addPointCoords() == 3); -} - -const Point2 = struct { - x: i32, - y: i32, - - fn addPointCoords(self: Point2) i32 { - return self.x + self.y; - } -}; - -test "pass by non-copying value as method, which is generic" { - var pt = Point3{ .x = 1, .y = 2 }; - try expect(pt.addPointCoords(i32) == 3); -} - -const Point3 = struct { - x: i32, - y: i32, - - fn addPointCoords(self: Point3, comptime T: type) i32 { - _ = T; - return self.x + self.y; - } -}; - -test "pass by non-copying value as method, at comptime" { - comptime { - var pt = Point2{ .x = 1, .y = 2 }; - try expect(pt.addPointCoords() == 3); - } -} - fn outer(y: u32) fn (u32) u32 { const Y = @TypeOf(y); const st = struct { @@ -194,43 +72,6 @@ test "return inner function which references comptime variable of outer function try expect(func(3) == 7); } -test "extern struct with stdcallcc fn pointer" { - const S = extern struct { - ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32, - - fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 { - return 1234; - } - }; - - var s: S = undefined; - s.ptr = S.foo; - try expect(s.ptr() == 1234); -} - -test "implicit cast fn call result to optional in field result" { - const S = struct { - fn entry() !void { - var x = Foo{ - .field = optionalPtr(), - }; - try expect(x.field.?.* == 999); - } - - const glob: i32 = 999; - - fn optionalPtr() *const i32 { - return &glob; - } - - const Foo = struct { - field: ?*const i32, - }; - }; - try S.entry(); - comptime try S.entry(); -} - test "discard the result of a function that returns a struct" { const S = struct { fn entry() void { @@ -249,45 +90,3 @@ test "discard the result of a function that returns a struct" { S.entry(); comptime S.entry(); } - -test "function call with anon list literal" { - const S = struct { - fn doTheTest() !void { - try consumeVec(.{ 9, 8, 7 }); - } - - fn consumeVec(vec: [3]f32) !void { - try expect(vec[0] == 9); - try expect(vec[1] == 8); - try expect(vec[2] == 7); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "ability to give comptime types and non comptime types to same parameter" { - const S = struct { - fn doTheTest() !void { - var x: i32 = 1; - try expect(foo(x) == 10); - try expect(foo(i32) == 20); - } - - fn foo(arg: anytype) i32 { - if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20; - return 9 + arg; - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - -test "function with inferred error set but returning no error" { - const S = struct { - fn foo() !void {} - }; - - const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; - try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len); -} diff --git a/test/behavior/fn_stage1.zig b/test/behavior/fn_stage1.zig new file mode 100644 index 0000000000..06c5d8fdb2 --- /dev/null +++ b/test/behavior/fn_stage1.zig @@ -0,0 +1,206 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const testing = std.testing; +const expect = testing.expect; +const expectEqual = testing.expectEqual; + +test "void parameters" { + try voidFun(1, void{}, 2, {}); +} +fn voidFun(a: i32, b: void, c: i32, d: void) !void { + _ = d; + const v = b; + const vv: void = if (a == 1) v else {}; + try expect(a + c == 3); + return vv; +} + +test "call function with empty string" { + acceptsString(""); +} + +fn acceptsString(foo: []u8) void { + _ = foo; +} + +test "implicit cast function unreachable return" { + wantsFnWithVoid(fnWithUnreachable); +} + +fn wantsFnWithVoid(f: fn () void) void { + _ = f; +} + +fn fnWithUnreachable() noreturn { + unreachable; +} + +test "function pointers" { + const fns = [_]@TypeOf(fn1){ + fn1, + fn2, + fn3, + fn4, + }; + for (fns) |f, i| { + try expect(f() == @intCast(u32, i) + 5); + } +} +fn fn1() u32 { + return 5; +} +fn fn2() u32 { + return 6; +} +fn fn3() u32 { + return 7; +} +fn fn4() u32 { + return 8; +} + +test "number literal as an argument" { + try numberLiteralArg(3); + comptime try numberLiteralArg(3); +} + +fn numberLiteralArg(a: anytype) !void { + try expect(a == 3); +} + +test "pass by non-copying value" { + try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3); +} + +const Point = struct { + x: i32, + y: i32, +}; + +fn addPointCoords(pt: Point) i32 { + return pt.x + pt.y; +} + +test "pass by non-copying value through var arg" { + try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3); +} + +fn addPointCoordsVar(pt: anytype) !i32 { + comptime try expect(@TypeOf(pt) == Point); + return pt.x + pt.y; +} + +test "pass by non-copying value as method" { + var pt = Point2{ .x = 1, .y = 2 }; + try expect(pt.addPointCoords() == 3); +} + +const Point2 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point2) i32 { + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, which is generic" { + var pt = Point3{ .x = 1, .y = 2 }; + try expect(pt.addPointCoords(i32) == 3); +} + +const Point3 = struct { + x: i32, + y: i32, + + fn addPointCoords(self: Point3, comptime T: type) i32 { + _ = T; + return self.x + self.y; + } +}; + +test "pass by non-copying value as method, at comptime" { + comptime { + var pt = Point2{ .x = 1, .y = 2 }; + try expect(pt.addPointCoords() == 3); + } +} + +test "extern struct with stdcallcc fn pointer" { + const S = extern struct { + ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32, + + fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 { + return 1234; + } + }; + + var s: S = undefined; + s.ptr = S.foo; + try expect(s.ptr() == 1234); +} + +test "implicit cast fn call result to optional in field result" { + const S = struct { + fn entry() !void { + var x = Foo{ + .field = optionalPtr(), + }; + try expect(x.field.?.* == 999); + } + + const glob: i32 = 999; + + fn optionalPtr() *const i32 { + return &glob; + } + + const Foo = struct { + field: ?*const i32, + }; + }; + try S.entry(); + comptime try S.entry(); +} + +test "function call with anon list literal" { + const S = struct { + fn doTheTest() !void { + try consumeVec(.{ 9, 8, 7 }); + } + + fn consumeVec(vec: [3]f32) !void { + try expect(vec[0] == 9); + try expect(vec[1] == 8); + try expect(vec[2] == 7); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "ability to give comptime types and non comptime types to same parameter" { + const S = struct { + fn doTheTest() !void { + var x: i32 = 1; + try expect(foo(x) == 10); + try expect(foo(i32) == 20); + } + + fn foo(arg: anytype) i32 { + if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20; + return 9 + arg; + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} + +test "function with inferred error set but returning no error" { + const S = struct { + fn foo() !void {} + }; + + const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?; + try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len); +} diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 8ca757b0cc..158e144f5d 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -249,3 +249,190 @@ test "binary not" { fn testBinaryNot(x: u16) !void { try expect(~x == 0b0101010101010101); } + +test "division" { + try testDivision(); + comptime try testDivision(); +} +fn testDivision() !void { + try expect(div(u32, 13, 3) == 4); + try expect(div(f16, 1.0, 2.0) == 0.5); + try expect(div(f32, 1.0, 2.0) == 0.5); + + try expect(divExact(u32, 55, 11) == 5); + try expect(divExact(i32, -55, 11) == -5); + try expect(divExact(f16, 55.0, 11.0) == 5.0); + try expect(divExact(f16, -55.0, 11.0) == -5.0); + try expect(divExact(f32, 55.0, 11.0) == 5.0); + try expect(divExact(f32, -55.0, 11.0) == -5.0); + + try expect(divFloor(i32, 5, 3) == 1); + try expect(divFloor(i32, -5, 3) == -2); + try expect(divFloor(f16, 5.0, 3.0) == 1.0); + try expect(divFloor(f16, -5.0, 3.0) == -2.0); + try expect(divFloor(f32, 5.0, 3.0) == 1.0); + try expect(divFloor(f32, -5.0, 3.0) == -2.0); + try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); + try expect(divFloor(i32, 0, -0x80000000) == 0); + try expect(divFloor(i32, -0x40000001, 0x40000000) == -2); + try expect(divFloor(i32, -0x80000000, 1) == -0x80000000); + try expect(divFloor(i32, 10, 12) == 0); + try expect(divFloor(i32, -14, 12) == -2); + try expect(divFloor(i32, -2, 12) == -1); + + try expect(divTrunc(i32, 5, 3) == 1); + try expect(divTrunc(i32, -5, 3) == -1); + try expect(divTrunc(f16, 5.0, 3.0) == 1.0); + try expect(divTrunc(f16, -5.0, 3.0) == -1.0); + try expect(divTrunc(f32, 5.0, 3.0) == 1.0); + try expect(divTrunc(f32, -5.0, 3.0) == -1.0); + try expect(divTrunc(f64, 5.0, 3.0) == 1.0); + try expect(divTrunc(f64, -5.0, 3.0) == -1.0); + try expect(divTrunc(i32, 10, 12) == 0); + try expect(divTrunc(i32, -14, 12) == -1); + try expect(divTrunc(i32, -2, 12) == 0); + + try expect(mod(i32, 10, 12) == 10); + try expect(mod(i32, -14, 12) == 10); + try expect(mod(i32, -2, 12) == 10); + + comptime { + try expect( + 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, + ); + try expect( + @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, + ); + try expect( + 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, + ); + try expect( + @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, + ); + try expect( + @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, + ); + try expect( + @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, + ); + try expect( + 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, + ); + } +} +fn div(comptime T: type, a: T, b: T) T { + return a / b; +} +fn divExact(comptime T: type, a: T, b: T) T { + return @divExact(a, b); +} +fn divFloor(comptime T: type, a: T, b: T) T { + return @divFloor(a, b); +} +fn divTrunc(comptime T: type, a: T, b: T) T { + return @divTrunc(a, b); +} +fn mod(comptime T: type, a: T, b: T) T { + return @mod(a, b); +} + +test "unsigned wrapping" { + try testUnsignedWrappingEval(maxInt(u32)); + comptime try testUnsignedWrappingEval(maxInt(u32)); +} +fn testUnsignedWrappingEval(x: u32) !void { + const zero = x +% 1; + try expect(zero == 0); + const orig = zero -% 1; + try expect(orig == maxInt(u32)); +} + +test "signed wrapping" { + try testSignedWrappingEval(maxInt(i32)); + comptime try testSignedWrappingEval(maxInt(i32)); +} +fn testSignedWrappingEval(x: i32) !void { + const min_val = x +% 1; + try expect(min_val == minInt(i32)); + const max_val = min_val -% 1; + try expect(max_val == maxInt(i32)); +} + +test "signed negation wrapping" { + try testSignedNegationWrappingEval(minInt(i16)); + comptime try testSignedNegationWrappingEval(minInt(i16)); +} +fn testSignedNegationWrappingEval(x: i16) !void { + try expect(x == -32768); + const neg = -%x; + try expect(neg == -32768); +} + +test "unsigned negation wrapping" { + try testUnsignedNegationWrappingEval(1); + comptime try testUnsignedNegationWrappingEval(1); +} +fn testUnsignedNegationWrappingEval(x: u16) !void { + try expect(x == 1); + const neg = -%x; + try expect(neg == maxInt(u16)); +} + +test "unsigned 64-bit division" { + try test_u64_div(); + comptime try test_u64_div(); +} +fn test_u64_div() !void { + const result = divWithResult(1152921504606846976, 34359738365); + try expect(result.quotient == 33554432); + try expect(result.remainder == 100663296); +} +fn divWithResult(a: u64, b: u64) DivResult { + return DivResult{ + .quotient = a / b, + .remainder = a % b, + }; +} +const DivResult = struct { + quotient: u64, + remainder: u64, +}; + +test "truncating shift right" { + try testShrTrunc(maxInt(u16)); + comptime try testShrTrunc(maxInt(u16)); +} +fn testShrTrunc(x: u16) !void { + const shifted = x >> 1; + try expect(shifted == 32767); +} + +test "f128" { + try test_f128(); + comptime try test_f128(); +} + +fn make_f128(x: f128) f128 { + return x; +} + +fn test_f128() !void { + try expect(@sizeOf(f128) == 16); + try expect(make_f128(1.0) == 1.0); + try expect(make_f128(1.0) != 1.1); + try expect(make_f128(1.0) > 0.9); + try expect(make_f128(1.0) >= 0.9); + try expect(make_f128(1.0) >= 1.0); + try should_not_be_zero(1.0); +} + +fn should_not_be_zero(x: f128) !void { + try expect(x != 0.0); +} + +test "128-bit multiplication" { + var a: i128 = 3; + var b: i128 = 2; + var c = a * b; + try expect(c == 6); +} diff --git a/test/behavior/math_stage1.zig b/test/behavior/math_stage1.zig index caec185414..a316c0f3dd 100644 --- a/test/behavior/math_stage1.zig +++ b/test/behavior/math_stage1.zig @@ -6,92 +6,6 @@ const maxInt = std.math.maxInt; const minInt = std.math.minInt; const mem = std.mem; -test "division" { - try testDivision(); - comptime try testDivision(); -} -fn testDivision() !void { - try expect(div(u32, 13, 3) == 4); - try expect(div(f16, 1.0, 2.0) == 0.5); - try expect(div(f32, 1.0, 2.0) == 0.5); - - try expect(divExact(u32, 55, 11) == 5); - try expect(divExact(i32, -55, 11) == -5); - try expect(divExact(f16, 55.0, 11.0) == 5.0); - try expect(divExact(f16, -55.0, 11.0) == -5.0); - try expect(divExact(f32, 55.0, 11.0) == 5.0); - try expect(divExact(f32, -55.0, 11.0) == -5.0); - - try expect(divFloor(i32, 5, 3) == 1); - try expect(divFloor(i32, -5, 3) == -2); - try expect(divFloor(f16, 5.0, 3.0) == 1.0); - try expect(divFloor(f16, -5.0, 3.0) == -2.0); - try expect(divFloor(f32, 5.0, 3.0) == 1.0); - try expect(divFloor(f32, -5.0, 3.0) == -2.0); - try expect(divFloor(i32, -0x80000000, -2) == 0x40000000); - try expect(divFloor(i32, 0, -0x80000000) == 0); - try expect(divFloor(i32, -0x40000001, 0x40000000) == -2); - try expect(divFloor(i32, -0x80000000, 1) == -0x80000000); - try expect(divFloor(i32, 10, 12) == 0); - try expect(divFloor(i32, -14, 12) == -2); - try expect(divFloor(i32, -2, 12) == -1); - - try expect(divTrunc(i32, 5, 3) == 1); - try expect(divTrunc(i32, -5, 3) == -1); - try expect(divTrunc(f16, 5.0, 3.0) == 1.0); - try expect(divTrunc(f16, -5.0, 3.0) == -1.0); - try expect(divTrunc(f32, 5.0, 3.0) == 1.0); - try expect(divTrunc(f32, -5.0, 3.0) == -1.0); - try expect(divTrunc(f64, 5.0, 3.0) == 1.0); - try expect(divTrunc(f64, -5.0, 3.0) == -1.0); - try expect(divTrunc(i32, 10, 12) == 0); - try expect(divTrunc(i32, -14, 12) == -1); - try expect(divTrunc(i32, -2, 12) == 0); - - try expect(mod(i32, 10, 12) == 10); - try expect(mod(i32, -14, 12) == 10); - try expect(mod(i32, -2, 12) == 10); - - comptime { - try expect( - 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600, - ); - try expect( - @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600, - ); - try expect( - 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2, - ); - try expect( - @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2, - ); - try expect( - @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2, - ); - try expect( - @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2, - ); - try expect( - 4126227191251978491697987544882340798050766755606969681711 % 10 == 1, - ); - } -} -fn div(comptime T: type, a: T, b: T) T { - return a / b; -} -fn divExact(comptime T: type, a: T, b: T) T { - return @divExact(a, b); -} -fn divFloor(comptime T: type, a: T, b: T) T { - return @divFloor(a, b); -} -fn divTrunc(comptime T: type, a: T, b: T) T { - return @divTrunc(a, b); -} -fn mod(comptime T: type, a: T, b: T) T { - return @mod(a, b); -} - test "@addWithOverflow" { var result: u8 = undefined; try expect(@addWithOverflow(u8, 250, 100, &result)); @@ -157,68 +71,6 @@ fn testCtzVectors() !void { try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16))); } -test "unsigned wrapping" { - try testUnsignedWrappingEval(maxInt(u32)); - comptime try testUnsignedWrappingEval(maxInt(u32)); -} -fn testUnsignedWrappingEval(x: u32) !void { - const zero = x +% 1; - try expect(zero == 0); - const orig = zero -% 1; - try expect(orig == maxInt(u32)); -} - -test "signed wrapping" { - try testSignedWrappingEval(maxInt(i32)); - comptime try testSignedWrappingEval(maxInt(i32)); -} -fn testSignedWrappingEval(x: i32) !void { - const min_val = x +% 1; - try expect(min_val == minInt(i32)); - const max_val = min_val -% 1; - try expect(max_val == maxInt(i32)); -} - -test "signed negation wrapping" { - try testSignedNegationWrappingEval(minInt(i16)); - comptime try testSignedNegationWrappingEval(minInt(i16)); -} -fn testSignedNegationWrappingEval(x: i16) !void { - try expect(x == -32768); - const neg = -%x; - try expect(neg == -32768); -} - -test "unsigned negation wrapping" { - try testUnsignedNegationWrappingEval(1); - comptime try testUnsignedNegationWrappingEval(1); -} -fn testUnsignedNegationWrappingEval(x: u16) !void { - try expect(x == 1); - const neg = -%x; - try expect(neg == maxInt(u16)); -} - -test "unsigned 64-bit division" { - try test_u64_div(); - comptime try test_u64_div(); -} -fn test_u64_div() !void { - const result = divWithResult(1152921504606846976, 34359738365); - try expect(result.quotient == 33554432); - try expect(result.remainder == 100663296); -} -fn divWithResult(a: u64, b: u64) DivResult { - return DivResult{ - .quotient = a / b, - .remainder = a % b, - }; -} -const DivResult = struct { - quotient: u64, - remainder: u64, -}; - test "small int addition" { var x: u2 = 0; try expect(x == 0); @@ -346,15 +198,6 @@ fn testShlTrunc(x: u16) !void { try expect(shifted == 65534); } -test "truncating shift right" { - try testShrTrunc(maxInt(u16)); - comptime try testShrTrunc(maxInt(u16)); -} -fn testShrTrunc(x: u16) !void { - const shifted = x >> 1; - try expect(shifted == 32767); -} - test "exact shift left" { try testShlExact(0b00110101); comptime try testShlExact(0b00110101); @@ -392,29 +235,6 @@ test "shift left/right on u0 operand" { comptime try S.doTheTest(); } -test "f128" { - try test_f128(); - comptime try test_f128(); -} - -fn make_f128(x: f128) f128 { - return x; -} - -fn test_f128() !void { - try expect(@sizeOf(f128) == 16); - try expect(make_f128(1.0) == 1.0); - try expect(make_f128(1.0) != 1.1); - try expect(make_f128(1.0) > 0.9); - try expect(make_f128(1.0) >= 0.9); - try expect(make_f128(1.0) >= 1.0); - try should_not_be_zero(1.0); -} - -fn should_not_be_zero(x: f128) !void { - try expect(x != 0.0); -} - test "comptime float rem int" { comptime { var x = @as(f32, 1) % 2; @@ -614,13 +434,6 @@ fn testNanEqNan(comptime F: type) !void { try expect(!(nan1 <= nan2)); } -test "128-bit multiplication" { - var a: i128 = 3; - var b: i128 = 2; - var c = a * b; - try expect(c == 6); -} - test "vector comparison" { const S = struct { fn doTheTest() !void {