authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-21 19:05:26-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-21 19:05:26-07:00
log7f70c27e9d57c8234545120da81861e2cfb354b5
tree47948dcf37e5e5767db2b428a8ef8177ef8eee86
parenta3c9bfef301e0a4675fcea57356653334e07df45

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.

24 files changed, 1026 insertions(+), 556 deletions(-)

lib/std/math/big/int.zig+13-5
......@@ -135,6 +135,11 @@ pub const Mutable = struct {
135135 };
136136 }
137137
138 /// Returns true if `a == 0`.
139 pub fn eqZero(self: Mutable) bool {
140 return self.toConst().eqZero();
141 }
142
138143 /// Asserts that the allocator owns the limbs memory. If this is not the case,
139144 /// use `toConst().toManaged()`.
140145 pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {
......@@ -773,12 +778,15 @@ pub const Mutable = struct {
773778 div(q, r, a, b, limbs_buffer, allocator);
774779
775780 // Trunc -> Floor.
776 if (!q.positive) {
781 if (a.positive and b.positive) return;
782
783 if ((!q.positive or q.eqZero()) and !r.eqZero()) {
777784 const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true };
778785 q.sub(q.toConst(), one);
779 r.add(q.toConst(), one);
780786 }
781 r.positive = b.positive;
787
788 r.mulNoAlias(q.toConst(), b, allocator);
789 r.sub(a, r.toConst());
782790 }
783791
784792 /// q = a / b (rem r)
......@@ -1220,12 +1228,12 @@ pub const Mutable = struct {
12201228
12211229 var x: Mutable = .{
12221230 .limbs = x_limbs,
1223 .positive = a.positive,
1231 .positive = true,
12241232 .len = a.limbs.len - ab_zero_limb_count,
12251233 };
12261234 var y: Mutable = .{
12271235 .limbs = y_limbs,
1228 .positive = b.positive,
1236 .positive = true,
12291237 .len = b.limbs.len - ab_zero_limb_count,
12301238 };
12311239
lib/std/math/big/int_test.zig+57
......@@ -1399,6 +1399,63 @@ test "big.int div floor single-single -/-" {
13991399 try testing.expect((try r.to(i32)) == er);
14001400}
14011401
1402test "big.int div floor no remainder negative quotient" {
1403 const u: i32 = -0x80000000;
1404 const v: i32 = 1;
1405
1406 var a = try Managed.initSet(testing.allocator, u);
1407 defer a.deinit();
1408 var b = try Managed.initSet(testing.allocator, v);
1409 defer b.deinit();
1410
1411 var q = try Managed.init(testing.allocator);
1412 defer q.deinit();
1413 var r = try Managed.init(testing.allocator);
1414 defer r.deinit();
1415 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1416
1417 try testing.expect((try q.to(i32)) == -0x80000000);
1418 try testing.expect((try r.to(i32)) == 0);
1419}
1420
1421test "big.int div floor negative close to zero" {
1422 const u: i32 = -2;
1423 const v: i32 = 12;
1424
1425 var a = try Managed.initSet(testing.allocator, u);
1426 defer a.deinit();
1427 var b = try Managed.initSet(testing.allocator, v);
1428 defer b.deinit();
1429
1430 var q = try Managed.init(testing.allocator);
1431 defer q.deinit();
1432 var r = try Managed.init(testing.allocator);
1433 defer r.deinit();
1434 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1435
1436 try testing.expect((try q.to(i32)) == -1);
1437 try testing.expect((try r.to(i32)) == 10);
1438}
1439
1440test "big.int div floor positive close to zero" {
1441 const u: i32 = 10;
1442 const v: i32 = 12;
1443
1444 var a = try Managed.initSet(testing.allocator, u);
1445 defer a.deinit();
1446 var b = try Managed.initSet(testing.allocator, v);
1447 defer b.deinit();
1448
1449 var q = try Managed.init(testing.allocator);
1450 defer q.deinit();
1451 var r = try Managed.init(testing.allocator);
1452 defer r.deinit();
1453 try Managed.divFloor(&q, &r, a.toConst(), b.toConst());
1454
1455 try testing.expect((try q.to(i32)) == 0);
1456 try testing.expect((try r.to(i32)) == 10);
1457}
1458
14021459test "big.int div multi-multi with rem" {
14031460 var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);
14041461 defer a.deinit();
lib/std/special/compiler_rt.zig+5-4
......@@ -74,6 +74,11 @@ comptime {
7474 @export(__getf2, .{ .name = "__gttf2", .linkage = linkage });
7575
7676 @export(__extendhfsf2, .{ .name = "__gnu_h2f_ieee", .linkage = linkage });
77
78 const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4;
79 @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage });
80 const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4;
81 @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage });
7782 }
7883
7984 if (!builtin.zig_is_stage2) {
......@@ -621,10 +626,6 @@ comptime {
621626 const __umodti3 = @import("compiler_rt/umodti3.zig").__umodti3;
622627 @export(__umodti3, .{ .name = "__umodti3", .linkage = linkage });
623628 }
624 const __muloti4 = @import("compiler_rt/muloti4.zig").__muloti4;
625 @export(__muloti4, .{ .name = "__muloti4", .linkage = linkage });
626 const __mulodi4 = @import("compiler_rt/mulodi4.zig").__mulodi4;
627 @export(__mulodi4, .{ .name = "__mulodi4", .linkage = linkage });
628629
629630 _ = @import("compiler_rt/atomics.zig");
630631
src/Air.zig+22-3
......@@ -80,11 +80,27 @@ pub const Inst = struct {
8080 /// is the same as both operands.
8181 /// Uses the `bin_op` field.
8282 mul_sat,
83 /// Integer or float division. For integers, wrapping is undefined behavior.
83 /// Float division.
8484 /// Both operands are guaranteed to be the same type, and the result type
8585 /// is the same as both operands.
8686 /// Uses the `bin_op` field.
87 div,
87 div_float,
88 /// Truncating integer or float division. For integers, wrapping is undefined behavior.
89 /// Both operands are guaranteed to be the same type, and the result type
90 /// is the same as both operands.
91 /// Uses the `bin_op` field.
92 div_trunc,
93 /// Flooring integer or float division. For integers, wrapping is undefined behavior.
94 /// Both operands are guaranteed to be the same type, and the result type
95 /// is the same as both operands.
96 /// Uses the `bin_op` field.
97 div_floor,
98 /// Integer or float division. Guaranteed no remainder.
99 /// For integers, wrapping is undefined behavior.
100 /// Both operands are guaranteed to be the same type, and the result type
101 /// is the same as both operands.
102 /// Uses the `bin_op` field.
103 div_exact,
88104 /// Integer or float remainder division.
89105 /// Both operands are guaranteed to be the same type, and the result type
90106 /// is the same as both operands.
......@@ -644,7 +660,10 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
644660 .mul,
645661 .mulwrap,
646662 .mul_sat,
647 .div,
663 .div_float,
664 .div_trunc,
665 .div_floor,
666 .div_exact,
648667 .rem,
649668 .mod,
650669 .bit_and,
src/Liveness.zig+4-1
......@@ -233,7 +233,10 @@ fn analyzeInst(
233233 .mul,
234234 .mulwrap,
235235 .mul_sat,
236 .div,
236 .div_float,
237 .div_trunc,
238 .div_floor,
239 .div_exact,
237240 .rem,
238241 .mod,
239242 .ptr_add,
src/Sema.zig+215-38
......@@ -641,9 +641,6 @@ pub fn analyzeBody(
641641 .pop_count => try sema.zirPopCount(block, inst),
642642 .byte_swap => try sema.zirByteSwap(block, inst),
643643 .bit_reverse => try sema.zirBitReverse(block, inst),
644 .div_exact => try sema.zirDivExact(block, inst),
645 .div_floor => try sema.zirDivFloor(block, inst),
646 .div_trunc => try sema.zirDivTrunc(block, inst),
647644 .shr_exact => try sema.zirShrExact(block, inst),
648645 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),
649646 .offset_of => try sema.zirOffsetOf(block, inst),
......@@ -683,19 +680,22 @@ pub fn analyzeBody(
683680 .error_set_decl_anon => try sema.zirErrorSetDecl(block, inst, .anon),
684681 .error_set_decl_func => try sema.zirErrorSetDecl(block, inst, .func),
685682
686 .add => try sema.zirArithmetic(block, inst, .add),
687 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
688 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
689 .div => try sema.zirArithmetic(block, inst, .div),
690 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
691 .mod => try sema.zirArithmetic(block, inst, .mod),
692 .rem => try sema.zirArithmetic(block, inst, .rem),
693 .mul => try sema.zirArithmetic(block, inst, .mul),
694 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
695 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
696 .sub => try sema.zirArithmetic(block, inst, .sub),
697 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),
698 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
683 .add => try sema.zirArithmetic(block, inst, .add),
684 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
685 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
686 .div => try sema.zirArithmetic(block, inst, .div),
687 .div_exact => try sema.zirArithmetic(block, inst, .div_exact),
688 .div_floor => try sema.zirArithmetic(block, inst, .div_floor),
689 .div_trunc => try sema.zirArithmetic(block, inst, .div_trunc),
690 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
691 .mod => try sema.zirArithmetic(block, inst, .mod),
692 .rem => try sema.zirArithmetic(block, inst, .rem),
693 .mul => try sema.zirArithmetic(block, inst, .mul),
694 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
695 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
696 .sub => try sema.zirArithmetic(block, inst, .sub),
697 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),
698 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
699699
700700 .maximum => try sema.zirMinMax(block, inst, .max),
701701 .minimum => try sema.zirMinMax(block, inst, .min),
......@@ -7350,6 +7350,9 @@ fn analyzeArithmetic(
73507350 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
73517351 },
73527352 .div => {
7353 // TODO: emit compile error when .div is used on integers and there would be an
7354 // ambiguous result between div_floor and div_trunc.
7355
73537356 // For integers:
73547357 // If the lhs is zero, then zero is returned regardless of rhs.
73557358 // If the rhs is zero, compile error for division by zero.
......@@ -7359,9 +7362,11 @@ fn analyzeArithmetic(
73597362 // * if lhs type is signed:
73607363 // * if rhs is comptime-known and not -1, result is undefined
73617364 // * if rhs is -1 or runtime-known, compile error because there is a
7362 // possible value (-min_int * -1) for which division would be
7365 // possible value (-min_int / -1) for which division would be
73637366 // illegal behavior.
73647367 // * if lhs type is unsigned, undef is returned regardless of rhs.
7368 // TODO: emit runtime safety for division by zero
7369 //
73657370 // For floats:
73667371 // If the rhs is zero, compile error for division by zero.
73677372 // If the rhs is undefined, compile error because there is a possible
......@@ -7407,8 +7412,198 @@ fn analyzeArithmetic(
74077412 try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena),
74087413 );
74097414 }
7410 } else break :rs .{ .src = rhs_src, .air_tag = .div };
7411 } else break :rs .{ .src = lhs_src, .air_tag = .div };
7415 } else {
7416 if (is_int) {
7417 break :rs .{ .src = rhs_src, .air_tag = .div_trunc };
7418 } else {
7419 break :rs .{ .src = rhs_src, .air_tag = .div_float };
7420 }
7421 }
7422 } else {
7423 if (is_int) {
7424 break :rs .{ .src = lhs_src, .air_tag = .div_trunc };
7425 } else {
7426 break :rs .{ .src = lhs_src, .air_tag = .div_float };
7427 }
7428 }
7429 },
7430 .div_trunc => {
7431 // For integers:
7432 // If the lhs is zero, then zero is returned regardless of rhs.
7433 // If the rhs is zero, compile error for division by zero.
7434 // If the rhs is undefined, compile error because there is a possible
7435 // value (zero) for which the division would be illegal behavior.
7436 // If the lhs is undefined:
7437 // * if lhs type is signed:
7438 // * if rhs is comptime-known and not -1, result is undefined
7439 // * if rhs is -1 or runtime-known, compile error because there is a
7440 // possible value (-min_int / -1) for which division would be
7441 // illegal behavior.
7442 // * if lhs type is unsigned, undef is returned regardless of rhs.
7443 // TODO: emit runtime safety for division by zero
7444 //
7445 // For floats:
7446 // If the rhs is zero, compile error for division by zero.
7447 // If the rhs is undefined, compile error because there is a possible
7448 // value (zero) for which the division would be illegal behavior.
7449 // If the lhs is undefined, result is undefined.
7450 if (maybe_lhs_val) |lhs_val| {
7451 if (!lhs_val.isUndef()) {
7452 if (lhs_val.compareWithZero(.eq)) {
7453 return sema.addConstant(scalar_type, Value.zero);
7454 }
7455 }
7456 }
7457 if (maybe_rhs_val) |rhs_val| {
7458 if (rhs_val.isUndef()) {
7459 return sema.failWithUseOfUndef(block, rhs_src);
7460 }
7461 if (rhs_val.compareWithZero(.eq)) {
7462 return sema.failWithDivideByZero(block, rhs_src);
7463 }
7464 }
7465 if (maybe_lhs_val) |lhs_val| {
7466 if (lhs_val.isUndef()) {
7467 if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
7468 if (maybe_rhs_val) |rhs_val| {
7469 if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
7470 return sema.addConstUndef(scalar_type);
7471 }
7472 }
7473 return sema.failWithUseOfUndef(block, rhs_src);
7474 }
7475 return sema.addConstUndef(scalar_type);
7476 }
7477
7478 if (maybe_rhs_val) |rhs_val| {
7479 if (is_int) {
7480 return sema.addConstant(
7481 scalar_type,
7482 try lhs_val.intDiv(rhs_val, sema.arena),
7483 );
7484 } else {
7485 return sema.addConstant(
7486 scalar_type,
7487 try lhs_val.floatDivTrunc(rhs_val, scalar_type, sema.arena),
7488 );
7489 }
7490 } else break :rs .{ .src = rhs_src, .air_tag = .div_trunc };
7491 } else break :rs .{ .src = lhs_src, .air_tag = .div_trunc };
7492 },
7493 .div_floor => {
7494 // For integers:
7495 // If the lhs is zero, then zero is returned regardless of rhs.
7496 // If the rhs is zero, compile error for division by zero.
7497 // If the rhs is undefined, compile error because there is a possible
7498 // value (zero) for which the division would be illegal behavior.
7499 // If the lhs is undefined:
7500 // * if lhs type is signed:
7501 // * if rhs is comptime-known and not -1, result is undefined
7502 // * if rhs is -1 or runtime-known, compile error because there is a
7503 // possible value (-min_int / -1) for which division would be
7504 // illegal behavior.
7505 // * if lhs type is unsigned, undef is returned regardless of rhs.
7506 // TODO: emit runtime safety for division by zero
7507 //
7508 // For floats:
7509 // If the rhs is zero, compile error for division by zero.
7510 // If the rhs is undefined, compile error because there is a possible
7511 // value (zero) for which the division would be illegal behavior.
7512 // If the lhs is undefined, result is undefined.
7513 if (maybe_lhs_val) |lhs_val| {
7514 if (!lhs_val.isUndef()) {
7515 if (lhs_val.compareWithZero(.eq)) {
7516 return sema.addConstant(scalar_type, Value.zero);
7517 }
7518 }
7519 }
7520 if (maybe_rhs_val) |rhs_val| {
7521 if (rhs_val.isUndef()) {
7522 return sema.failWithUseOfUndef(block, rhs_src);
7523 }
7524 if (rhs_val.compareWithZero(.eq)) {
7525 return sema.failWithDivideByZero(block, rhs_src);
7526 }
7527 }
7528 if (maybe_lhs_val) |lhs_val| {
7529 if (lhs_val.isUndef()) {
7530 if (lhs_ty.isSignedInt() and rhs_ty.isSignedInt()) {
7531 if (maybe_rhs_val) |rhs_val| {
7532 if (rhs_val.compare(.neq, Value.negative_one, scalar_type)) {
7533 return sema.addConstUndef(scalar_type);
7534 }
7535 }
7536 return sema.failWithUseOfUndef(block, rhs_src);
7537 }
7538 return sema.addConstUndef(scalar_type);
7539 }
7540
7541 if (maybe_rhs_val) |rhs_val| {
7542 if (is_int) {
7543 return sema.addConstant(
7544 scalar_type,
7545 try lhs_val.intDivFloor(rhs_val, sema.arena),
7546 );
7547 } else {
7548 return sema.addConstant(
7549 scalar_type,
7550 try lhs_val.floatDivFloor(rhs_val, scalar_type, sema.arena),
7551 );
7552 }
7553 } else break :rs .{ .src = rhs_src, .air_tag = .div_floor };
7554 } else break :rs .{ .src = lhs_src, .air_tag = .div_floor };
7555 },
7556 .div_exact => {
7557 // For integers:
7558 // If the lhs is zero, then zero is returned regardless of rhs.
7559 // If the rhs is zero, compile error for division by zero.
7560 // If the rhs is undefined, compile error because there is a possible
7561 // value (zero) for which the division would be illegal behavior.
7562 // If the lhs is undefined, compile error because there is a possible
7563 // value for which the division would result in a remainder.
7564 // TODO: emit runtime safety for if there is a remainder
7565 // TODO: emit runtime safety for division by zero
7566 //
7567 // For floats:
7568 // If the rhs is zero, compile error for division by zero.
7569 // If the rhs is undefined, compile error because there is a possible
7570 // value (zero) for which the division would be illegal behavior.
7571 // If the lhs is undefined, compile error because there is a possible
7572 // value for which the division would result in a remainder.
7573 if (maybe_lhs_val) |lhs_val| {
7574 if (lhs_val.isUndef()) {
7575 return sema.failWithUseOfUndef(block, rhs_src);
7576 } else {
7577 if (lhs_val.compareWithZero(.eq)) {
7578 return sema.addConstant(scalar_type, Value.zero);
7579 }
7580 }
7581 }
7582 if (maybe_rhs_val) |rhs_val| {
7583 if (rhs_val.isUndef()) {
7584 return sema.failWithUseOfUndef(block, rhs_src);
7585 }
7586 if (rhs_val.compareWithZero(.eq)) {
7587 return sema.failWithDivideByZero(block, rhs_src);
7588 }
7589 }
7590 if (maybe_lhs_val) |lhs_val| {
7591 if (maybe_rhs_val) |rhs_val| {
7592 if (is_int) {
7593 // TODO: emit compile error if there is a remainder
7594 return sema.addConstant(
7595 scalar_type,
7596 try lhs_val.intDiv(rhs_val, sema.arena),
7597 );
7598 } else {
7599 // TODO: emit compile error if there is a remainder
7600 return sema.addConstant(
7601 scalar_type,
7602 try lhs_val.floatDiv(rhs_val, scalar_type, sema.arena),
7603 );
7604 }
7605 } else break :rs .{ .src = rhs_src, .air_tag = .div_exact };
7606 } else break :rs .{ .src = lhs_src, .air_tag = .div_exact };
74127607 },
74137608 .mul => {
74147609 // For integers:
......@@ -8824,7 +9019,7 @@ fn analyzeRet(
88249019fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
88259020 // extend this swich as additional operators are implemented
88269021 return switch (tag) {
8827 .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true,
9022 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,
88289023 else => false,
88299024 };
88309025}
......@@ -9602,24 +9797,6 @@ fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
96029797 return sema.fail(block, src, "TODO: Sema.zirBitReverse", .{});
96039798}
96049799
9605fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9606 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9607 const src = inst_data.src();
9608 return sema.fail(block, src, "TODO: Sema.zirDivExact", .{});
9609}
9610
9611fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9612 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9613 const src = inst_data.src();
9614 return sema.fail(block, src, "TODO: Sema.zirDivFloor", .{});
9615}
9616
9617fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9618 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9619 const src = inst_data.src();
9620 return sema.fail(block, src, "TODO: Sema.zirDivTrunc", .{});
9621}
9622
96239800fn zirShrExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
96249801 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
96259802 const src = inst_data.src();
src/arch/aarch64/CodeGen.zig+2-1
......@@ -410,7 +410,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
410410 .mul => try self.airMul(inst),
411411 .mulwrap => try self.airMulWrap(inst),
412412 .mul_sat => try self.airMulSat(inst),
413 .div => try self.airDiv(inst),
414413 .rem => try self.airRem(inst),
415414 .mod => try self.airMod(inst),
416415 .shl, .shl_exact => try self.airShl(inst),
......@@ -419,6 +418,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
419418 .max => try self.airMax(inst),
420419 .slice => try self.airSlice(inst),
421420
421 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
422
422423 .cmp_lt => try self.airCmp(inst, .lt),
423424 .cmp_lte => try self.airCmp(inst, .lte),
424425 .cmp_eq => try self.airCmp(inst, .eq),
src/codegen.zig+2-1
......@@ -758,7 +758,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
758758 .mul => try self.airMul(inst),
759759 .mulwrap => try self.airMulWrap(inst),
760760 .mul_sat => try self.airMulSat(inst),
761 .div => try self.airDiv(inst),
762761 .rem => try self.airRem(inst),
763762 .mod => try self.airMod(inst),
764763 .shl, .shl_exact => try self.airShl(inst),
......@@ -767,6 +766,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
767766 .max => try self.airMax(inst),
768767 .slice => try self.airSlice(inst),
769768
769 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
770
770771 .cmp_lt => try self.airCmp(inst, .lt),
771772 .cmp_lte => try self.airCmp(inst, .lte),
772773 .cmp_eq => try self.airCmp(inst, .eq),
src/codegen/c.zig+2-1
......@@ -976,7 +976,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
976976 .mul => try airBinOp (f, inst, " * "),
977977 // TODO use a different strategy for div that communicates to the optimizer
978978 // that wrapping is UB.
979 .div => try airBinOp( f, inst, " / "),
979 .div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "),
980 .div_floor => try airBinOp( f, inst, " divfloor "),
980981 .rem => try airBinOp( f, inst, " % "),
981982 .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
982983
src/codegen/llvm.zig+96-5
......@@ -1667,7 +1667,10 @@ pub const FuncGen = struct {
16671667 .mul => try self.airMul(inst),
16681668 .mulwrap => try self.airMulWrap(inst),
16691669 .mul_sat => try self.airMulSat(inst),
1670 .div => try self.airDiv(inst),
1670 .div_float => try self.airDivFloat(inst),
1671 .div_trunc => try self.airDivTrunc(inst),
1672 .div_floor => try self.airDivFloor(inst),
1673 .div_exact => try self.airDivExact(inst),
16711674 .rem => try self.airRem(inst),
16721675 .mod => try self.airMod(inst),
16731676 .ptr_add => try self.airPtrAdd(inst),
......@@ -2830,20 +2833,81 @@ pub const FuncGen = struct {
28302833 return self.builder.buildUMulFixSat(lhs, rhs, "");
28312834 }
28322835
2833 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2834 if (self.liveness.isUnused(inst))
2835 return null;
2836 fn airDivFloat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2837 if (self.liveness.isUnused(inst)) return null;
2838
2839 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2840 const lhs = try self.resolveInst(bin_op.lhs);
2841 const rhs = try self.resolveInst(bin_op.rhs);
2842
2843 return self.builder.buildFDiv(lhs, rhs, "");
2844 }
2845
2846 fn airDivTrunc(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2847 if (self.liveness.isUnused(inst)) return null;
28362848
28372849 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
28382850 const lhs = try self.resolveInst(bin_op.lhs);
28392851 const rhs = try self.resolveInst(bin_op.rhs);
28402852 const inst_ty = self.air.typeOfIndex(inst);
28412853
2842 if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
2854 if (inst_ty.isRuntimeFloat()) {
2855 const result_llvm_ty = try self.dg.llvmType(inst_ty);
2856 const zero = result_llvm_ty.constNull();
2857 const result = self.builder.buildFDiv(lhs, rhs, "");
2858 const ceiled = try self.callCeil(result, inst_ty);
2859 const floored = try self.callFloor(result, inst_ty);
2860 const ltz = self.builder.buildFCmp(.OLT, lhs, zero, "");
2861 return self.builder.buildSelect(ltz, ceiled, floored, "");
2862 }
28432863 if (inst_ty.isSignedInt()) return self.builder.buildSDiv(lhs, rhs, "");
28442864 return self.builder.buildUDiv(lhs, rhs, "");
28452865 }
28462866
2867 fn airDivFloor(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2868 if (self.liveness.isUnused(inst)) return null;
2869
2870 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2871 const lhs = try self.resolveInst(bin_op.lhs);
2872 const rhs = try self.resolveInst(bin_op.rhs);
2873 const inst_ty = self.air.typeOfIndex(inst);
2874
2875 if (inst_ty.isRuntimeFloat()) {
2876 const result = self.builder.buildFDiv(lhs, rhs, "");
2877 return try self.callFloor(result, inst_ty);
2878 }
2879 if (inst_ty.isSignedInt()) {
2880 // const d = @divTrunc(a, b);
2881 // const r = @rem(a, b);
2882 // return if (r == 0) d else d - ((a < 0) ^ (b < 0));
2883 const result_llvm_ty = try self.dg.llvmType(inst_ty);
2884 const zero = result_llvm_ty.constNull();
2885 const div_trunc = self.builder.buildSDiv(lhs, rhs, "");
2886 const rem = self.builder.buildSRem(lhs, rhs, "");
2887 const rem_eq_0 = self.builder.buildICmp(.EQ, rem, zero, "");
2888 const a_lt_0 = self.builder.buildICmp(.SLT, lhs, zero, "");
2889 const b_lt_0 = self.builder.buildICmp(.SLT, rhs, zero, "");
2890 const a_b_xor = self.builder.buildXor(a_lt_0, b_lt_0, "");
2891 const a_b_xor_ext = self.builder.buildZExt(a_b_xor, div_trunc.typeOf(), "");
2892 const d_sub_xor = self.builder.buildSub(div_trunc, a_b_xor_ext, "");
2893 return self.builder.buildSelect(rem_eq_0, div_trunc, d_sub_xor, "");
2894 }
2895 return self.builder.buildUDiv(lhs, rhs, "");
2896 }
2897
2898 fn airDivExact(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2899 if (self.liveness.isUnused(inst)) return null;
2900
2901 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2902 const lhs = try self.resolveInst(bin_op.lhs);
2903 const rhs = try self.resolveInst(bin_op.rhs);
2904 const inst_ty = self.air.typeOfIndex(inst);
2905
2906 if (inst_ty.isRuntimeFloat()) return self.builder.buildFDiv(lhs, rhs, "");
2907 if (inst_ty.isSignedInt()) return self.builder.buildExactSDiv(lhs, rhs, "");
2908 return self.builder.buildExactUDiv(lhs, rhs, "");
2909 }
2910
28472911 fn airRem(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
28482912 if (self.liveness.isUnused(inst)) return null;
28492913
......@@ -3546,6 +3610,33 @@ pub const FuncGen = struct {
35463610 }
35473611 }
35483612
3613 fn callFloor(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
3614 return self.callFloatUnary(arg, ty, "floor");
3615 }
3616
3617 fn callCeil(self: *FuncGen, arg: *const llvm.Value, ty: Type) !*const llvm.Value {
3618 return self.callFloatUnary(arg, ty, "ceil");
3619 }
3620
3621 fn callFloatUnary(self: *FuncGen, arg: *const llvm.Value, ty: Type, name: []const u8) !*const llvm.Value {
3622 const target = self.dg.module.getTarget();
3623
3624 var fn_name_buf: [100]u8 = undefined;
3625 const llvm_fn_name = std.fmt.bufPrintZ(&fn_name_buf, "llvm.{s}.f{d}", .{
3626 name, ty.floatBits(target),
3627 }) catch unreachable;
3628
3629 const llvm_fn = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
3630 const operand_llvm_ty = try self.dg.llvmType(ty);
3631 const param_types = [_]*const llvm.Type{operand_llvm_ty};
3632 const fn_type = llvm.functionType(operand_llvm_ty, &param_types, param_types.len, .False);
3633 break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
3634 };
3635
3636 const args: [1]*const llvm.Value = .{arg};
3637 return self.builder.buildCall(llvm_fn, &args, args.len, .C, .Auto, "");
3638 }
3639
35493640 fn fieldPtr(
35503641 self: *FuncGen,
35513642 inst: Air.Inst.Index,
src/codegen/llvm/bindings.zig+6
......@@ -756,6 +756,12 @@ pub const Builder = opaque {
756756
757757 pub const buildSMin = ZigLLVMBuildSMin;
758758 extern fn ZigLLVMBuildSMin(builder: *const Builder, LHS: *const Value, RHS: *const Value, name: [*:0]const u8) *const Value;
759
760 pub const buildExactUDiv = LLVMBuildExactUDiv;
761 extern fn LLVMBuildExactUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
762
763 pub const buildExactSDiv = LLVMBuildExactSDiv;
764 extern fn LLVMBuildExactSDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
759765};
760766
761767pub const IntPredicate = enum(c_uint) {
src/codegen/spirv.zig-1
......@@ -669,7 +669,6 @@ pub const DeclGen = struct {
669669 .add, .addwrap => try self.airArithOp(inst, .{.OpFAdd, .OpIAdd, .OpIAdd}),
670670 .sub, .subwrap => try self.airArithOp(inst, .{.OpFSub, .OpISub, .OpISub}),
671671 .mul, .mulwrap => try self.airArithOp(inst, .{.OpFMul, .OpIMul, .OpIMul}),
672 .div => try self.airArithOp(inst, .{.OpFDiv, .OpSDiv, .OpUDiv}),
673672
674673 .bit_and => try self.airBinOpSimple(inst, .OpBitwiseAnd),
675674 .bit_or => try self.airBinOpSimple(inst, .OpBitwiseOr),
src/codegen/wasm.zig+1-1
......@@ -822,7 +822,7 @@ pub const Context = struct {
822822 .subwrap => self.airWrapBinOp(inst, .sub),
823823 .mul => self.airBinOp(inst, .mul),
824824 .mulwrap => self.airWrapBinOp(inst, .mul),
825 .div => self.airBinOp(inst, .div),
825 .div_trunc => self.airBinOp(inst, .div),
826826 .bit_and => self.airBinOp(inst, .@"and"),
827827 .bit_or => self.airBinOp(inst, .@"or"),
828828 .bool_and => self.airBinOp(inst, .@"and"),
src/print_air.zig+4-1
......@@ -111,7 +111,10 @@ const Writer = struct {
111111 .mul,
112112 .mulwrap,
113113 .mul_sat,
114 .div,
114 .div_float,
115 .div_trunc,
116 .div_floor,
117 .div_exact,
115118 .rem,
116119 .mod,
117120 .ptr_add,
src/value.zig+100-3
......@@ -2200,7 +2200,8 @@ pub const Value = extern union {
22002200 const rhs_bigint = rhs.toBigInt(&rhs_space);
22012201 const limbs = try arena.alloc(
22022202 std.math.big.Limb,
2203 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2203 // + 1 for negatives
2204 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
22042205 );
22052206 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
22062207 result_bigint.bitAnd(lhs_bigint, rhs_bigint);
......@@ -2264,7 +2265,8 @@ pub const Value = extern union {
22642265 const rhs_bigint = rhs.toBigInt(&rhs_space);
22652266 const limbs = try arena.alloc(
22662267 std.math.big.Limb,
2267 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2268 // + 1 for negatives
2269 std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1,
22682270 );
22692271 var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined };
22702272 result_bigint.bitXor(lhs_bigint, rhs_bigint);
......@@ -2352,7 +2354,7 @@ pub const Value = extern union {
23522354 }
23532355 }
23542356
2355 pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
2357 pub fn intDivFloor(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
23562358 // TODO is this a performance issue? maybe we should try the operation without
23572359 // resorting to BigInt first.
23582360 var lhs_space: Value.BigIntSpace = undefined;
......@@ -2373,6 +2375,39 @@ pub const Value = extern union {
23732375 );
23742376 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
23752377 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2378 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
2379 const result_limbs = result_q.limbs[0..result_q.len];
2380
2381 if (result_q.positive) {
2382 return Value.Tag.int_big_positive.create(allocator, result_limbs);
2383 } else {
2384 return Value.Tag.int_big_negative.create(allocator, result_limbs);
2385 }
2386 }
2387
2388 pub fn intRem(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
2389 // TODO is this a performance issue? maybe we should try the operation without
2390 // resorting to BigInt first.
2391 var lhs_space: Value.BigIntSpace = undefined;
2392 var rhs_space: Value.BigIntSpace = undefined;
2393 const lhs_bigint = lhs.toBigInt(&lhs_space);
2394 const rhs_bigint = rhs.toBigInt(&rhs_space);
2395 const limbs_q = try allocator.alloc(
2396 std.math.big.Limb,
2397 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,
2398 );
2399 const limbs_r = try allocator.alloc(
2400 std.math.big.Limb,
2401 // TODO: audit this size, and also consider reworking Sema to re-use Values rather than
2402 // always producing new Value objects.
2403 rhs_bigint.limbs.len + 1,
2404 );
2405 const limbs_buffer = try allocator.alloc(
2406 std.math.big.Limb,
2407 std.math.big.int.calcDivLimbsBufferLen(lhs_bigint.limbs.len, rhs_bigint.limbs.len),
2408 );
2409 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2410 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
23762411 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);
23772412 const result_limbs = result_r.limbs[0..result_r.len];
23782413
......@@ -2662,6 +2697,68 @@ pub const Value = extern union {
26622697 }
26632698 }
26642699
2700 pub fn floatDivFloor(
2701 lhs: Value,
2702 rhs: Value,
2703 float_type: Type,
2704 arena: *Allocator,
2705 ) !Value {
2706 switch (float_type.tag()) {
2707 .f16 => {
2708 const lhs_val = lhs.toFloat(f16);
2709 const rhs_val = rhs.toFloat(f16);
2710 return Value.Tag.float_16.create(arena, @divFloor(lhs_val, rhs_val));
2711 },
2712 .f32 => {
2713 const lhs_val = lhs.toFloat(f32);
2714 const rhs_val = rhs.toFloat(f32);
2715 return Value.Tag.float_32.create(arena, @divFloor(lhs_val, rhs_val));
2716 },
2717 .f64 => {
2718 const lhs_val = lhs.toFloat(f64);
2719 const rhs_val = rhs.toFloat(f64);
2720 return Value.Tag.float_64.create(arena, @divFloor(lhs_val, rhs_val));
2721 },
2722 .f128, .comptime_float, .c_longdouble => {
2723 const lhs_val = lhs.toFloat(f128);
2724 const rhs_val = rhs.toFloat(f128);
2725 return Value.Tag.float_128.create(arena, @divFloor(lhs_val, rhs_val));
2726 },
2727 else => unreachable,
2728 }
2729 }
2730
2731 pub fn floatDivTrunc(
2732 lhs: Value,
2733 rhs: Value,
2734 float_type: Type,
2735 arena: *Allocator,
2736 ) !Value {
2737 switch (float_type.tag()) {
2738 .f16 => {
2739 const lhs_val = lhs.toFloat(f16);
2740 const rhs_val = rhs.toFloat(f16);
2741 return Value.Tag.float_16.create(arena, @divTrunc(lhs_val, rhs_val));
2742 },
2743 .f32 => {
2744 const lhs_val = lhs.toFloat(f32);
2745 const rhs_val = rhs.toFloat(f32);
2746 return Value.Tag.float_32.create(arena, @divTrunc(lhs_val, rhs_val));
2747 },
2748 .f64 => {
2749 const lhs_val = lhs.toFloat(f64);
2750 const rhs_val = rhs.toFloat(f64);
2751 return Value.Tag.float_64.create(arena, @divTrunc(lhs_val, rhs_val));
2752 },
2753 .f128, .comptime_float, .c_longdouble => {
2754 const lhs_val = lhs.toFloat(f128);
2755 const rhs_val = rhs.toFloat(f128);
2756 return Value.Tag.float_128.create(arena, @divTrunc(lhs_val, rhs_val));
2757 },
2758 else => unreachable,
2759 }
2760 }
2761
26652762 pub fn floatMul(
26662763 lhs: Value,
26672764 rhs: Value,
test/behavior.zig+3-2
......@@ -33,6 +33,7 @@ test {
3333 _ = @import("behavior/error.zig");
3434 _ = @import("behavior/eval.zig");
3535 _ = @import("behavior/floatop.zig");
36 _ = @import("behavior/fn.zig");
3637 _ = @import("behavior/for.zig");
3738 _ = @import("behavior/generics.zig");
3839 _ = @import("behavior/hasdecl.zig");
......@@ -126,7 +127,7 @@ test {
126127 _ = @import("behavior/eval_stage1.zig");
127128 _ = @import("behavior/field_parent_ptr.zig");
128129 _ = @import("behavior/floatop_stage1.zig");
129 _ = @import("behavior/fn.zig");
130 _ = @import("behavior/fn_stage1.zig");
130131 _ = @import("behavior/fn_delegation.zig");
131132 _ = @import("behavior/fn_in_struct_in_comptime.zig");
132133 _ = @import("behavior/for_stage1.zig");
......@@ -150,7 +151,7 @@ test {
150151 _ = @import("behavior/reflection.zig");
151152 {
152153 // Checklist for getting saturating_arithmetic.zig passing for stage2:
153 // * add __muloti4 to compiler-rt
154 // * add __udivti3 to compiler-rt
154155 _ = @import("behavior/saturating_arithmetic.zig");
155156 }
156157 _ = @import("behavior/select.zig");
test/behavior/array.zig+52
......@@ -112,3 +112,55 @@ test "void arrays" {
112112 try expect(@sizeOf(@TypeOf(array)) == 0);
113113 try expect(array.len == 4);
114114}
115
116test "nested arrays" {
117 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
118 for (array_of_strings) |s, i| {
119 if (i == 0) try expect(mem.eql(u8, s, "hello"));
120 if (i == 1) try expect(mem.eql(u8, s, "this"));
121 if (i == 2) try expect(mem.eql(u8, s, "is"));
122 if (i == 3) try expect(mem.eql(u8, s, "my"));
123 if (i == 4) try expect(mem.eql(u8, s, "thing"));
124 }
125}
126
127var s_array: [8]Sub = undefined;
128const Sub = struct { b: u8 };
129const Str = struct { a: []Sub };
130test "set global var array via slice embedded in struct" {
131 var s = Str{ .a = s_array[0..] };
132
133 s.a[0].b = 1;
134 s.a[1].b = 2;
135 s.a[2].b = 3;
136
137 try expect(s_array[0].b == 1);
138 try expect(s_array[1].b == 2);
139 try expect(s_array[2].b == 3);
140}
141
142test "implicit comptime in array type size" {
143 var arr: [plusOne(10)]bool = undefined;
144 try expect(arr.len == 11);
145}
146
147fn plusOne(x: u32) u32 {
148 return x + 1;
149}
150
151test "read/write through global variable array of struct fields initialized via array mult" {
152 const S = struct {
153 fn doTheTest() !void {
154 try expect(storage[0].term == 1);
155 storage[0] = MyStruct{ .term = 123 };
156 try expect(storage[0].term == 123);
157 }
158
159 pub const MyStruct = struct {
160 term: usize,
161 };
162
163 var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
164 };
165 try S.doTheTest();
166}
test/behavior/array_stage1.zig-52
......@@ -4,32 +4,6 @@ const mem = std.mem;
44const expect = testing.expect;
55const expectEqual = testing.expectEqual;
66
7test "nested arrays" {
8 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
9 for (array_of_strings) |s, i| {
10 if (i == 0) try expect(mem.eql(u8, s, "hello"));
11 if (i == 1) try expect(mem.eql(u8, s, "this"));
12 if (i == 2) try expect(mem.eql(u8, s, "is"));
13 if (i == 3) try expect(mem.eql(u8, s, "my"));
14 if (i == 4) try expect(mem.eql(u8, s, "thing"));
15 }
16}
17
18var s_array: [8]Sub = undefined;
19const Sub = struct { b: u8 };
20const Str = struct { a: []Sub };
21test "set global var array via slice embedded in struct" {
22 var s = Str{ .a = s_array[0..] };
23
24 s.a[0].b = 1;
25 s.a[1].b = 2;
26 s.a[2].b = 3;
27
28 try expect(s_array[0].b == 1);
29 try expect(s_array[1].b == 2);
30 try expect(s_array[2].b == 3);
31}
32
337test "single-item pointer to array indexing and slicing" {
348 try testSingleItemPtrArrayIndexSlice();
359 comptime try testSingleItemPtrArrayIndexSlice();
......@@ -75,15 +49,6 @@ test "comptime evaluating function that takes array by value" {
7549 _ = comptime testArrayByValAtComptime(arr);
7650}
7751
78test "implicit comptime in array type size" {
79 var arr: [plusOne(10)]bool = undefined;
80 try expect(arr.len == 11);
81}
82
83fn plusOne(x: u32) u32 {
84 return x + 1;
85}
86
8752test "runtime initialize array elem and then implicit cast to slice" {
8853 var two: i32 = 2;
8954 const x: []const i32 = &[_]i32{two};
......@@ -171,23 +136,6 @@ test "double nested array to const slice cast in array literal" {
171136 comptime try S.entry(2);
172137}
173138
174test "read/write through global variable array of struct fields initialized via array mult" {
175 const S = struct {
176 fn doTheTest() !void {
177 try expect(storage[0].term == 1);
178 storage[0] = MyStruct{ .term = 123 };
179 try expect(storage[0].term == 123);
180 }
181
182 pub const MyStruct = struct {
183 term: usize,
184 };
185
186 var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
187 };
188 try S.doTheTest();
189}
190
191139test "implicit cast zero sized array ptr to slice" {
192140 {
193141 var b = "".*;
test/behavior/eval.zig+49
......@@ -402,3 +402,52 @@ test "f64 at compile time is lossy" {
402402test {
403403 comptime try expect(@as(f128, 1 << 113) == 10384593717069655257060992658440192);
404404}
405
406fn copyWithPartialInline(s: []u32, b: []u8) void {
407 comptime var i: usize = 0;
408 inline while (i < 4) : (i += 1) {
409 s[i] = 0;
410 s[i] |= @as(u32, b[i * 4 + 0]) << 24;
411 s[i] |= @as(u32, b[i * 4 + 1]) << 16;
412 s[i] |= @as(u32, b[i * 4 + 2]) << 8;
413 s[i] |= @as(u32, b[i * 4 + 3]) << 0;
414 }
415}
416
417test "binary math operator in partially inlined function" {
418 var s: [4]u32 = undefined;
419 var b: [16]u8 = undefined;
420
421 for (b) |*r, i|
422 r.* = @intCast(u8, i + 1);
423
424 copyWithPartialInline(s[0..], b[0..]);
425 try expect(s[0] == 0x1020304);
426 try expect(s[1] == 0x5060708);
427 try expect(s[2] == 0x90a0b0c);
428 try expect(s[3] == 0xd0e0f10);
429}
430
431test "comptime shl" {
432 var a: u128 = 3;
433 var b: u7 = 63;
434 var c: u128 = 3 << 63;
435 try expect((a << b) == c);
436}
437
438test "comptime bitwise operators" {
439 comptime {
440 try expect(3 & 1 == 1);
441 try expect(3 & -1 == 3);
442 try expect(-3 & -1 == -3);
443 try expect(3 | -1 == -1);
444 try expect(-3 | -1 == -1);
445 try expect(3 ^ -1 == -4);
446 try expect(-3 ^ -1 == 2);
447 try expect(~@as(i8, -1) == 0);
448 try expect(~@as(i128, -1) == 0);
449 try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611);
450 try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615);
451 try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff);
452 }
453}
test/behavior/eval_stage1.zig-49
......@@ -132,31 +132,6 @@ test "string literal used as comptime slice is memoized" {
132132 comptime try expect(TypeWithCompTimeSlice("link").Node == TypeWithCompTimeSlice("link").Node);
133133}
134134
135fn copyWithPartialInline(s: []u32, b: []u8) void {
136 comptime var i: usize = 0;
137 inline while (i < 4) : (i += 1) {
138 s[i] = 0;
139 s[i] |= @as(u32, b[i * 4 + 0]) << 24;
140 s[i] |= @as(u32, b[i * 4 + 1]) << 16;
141 s[i] |= @as(u32, b[i * 4 + 2]) << 8;
142 s[i] |= @as(u32, b[i * 4 + 3]) << 0;
143 }
144}
145
146test "binary math operator in partially inlined function" {
147 var s: [4]u32 = undefined;
148 var b: [16]u8 = undefined;
149
150 for (b) |*r, i|
151 r.* = @intCast(u8, i + 1);
152
153 copyWithPartialInline(s[0..], b[0..]);
154 try expect(s[0] == 0x1020304);
155 try expect(s[1] == 0x5060708);
156 try expect(s[2] == 0x90a0b0c);
157 try expect(s[3] == 0xd0e0f10);
158}
159
160135test "comptime function with mutable pointer is not memoized" {
161136 comptime {
162137 var x: i32 = 1;
......@@ -203,13 +178,6 @@ test "comptime shlWithOverflow" {
203178 try expect(ct_shifted == rt_shifted);
204179}
205180
206test "comptime shl" {
207 var a: u128 = 3;
208 var b: u7 = 63;
209 var c: u128 = 3 << 63;
210 try expectEqual(a << b, c);
211}
212
213181test "runtime 128 bit integer division" {
214182 var a: u128 = 152313999999999991610955792383;
215183 var b: u128 = 10000000000000000000;
......@@ -278,23 +246,6 @@ test "bit shift a u1" {
278246 try expect(y == 1);
279247}
280248
281test "comptime bitwise operators" {
282 comptime {
283 try expect(3 & 1 == 1);
284 try expect(3 & -1 == 3);
285 try expect(-3 & -1 == -3);
286 try expect(3 | -1 == -1);
287 try expect(-3 | -1 == -1);
288 try expect(3 ^ -1 == -4);
289 try expect(-3 ^ -1 == 2);
290 try expect(~@as(i8, -1) == 0);
291 try expect(~@as(i128, -1) == 0);
292 try expect(18446744073709551615 & 18446744073709551611 == 18446744073709551611);
293 try expect(-18446744073709551615 & -18446744073709551611 == -18446744073709551615);
294 try expect(~@as(u128, 0) == 0xffffffffffffffffffffffffffffffff);
295 }
296}
297
298249test "*align(1) u16 is the same as *align(1:0:2) u16" {
299250 comptime {
300251 try expect(*align(1:0:2) u16 == *align(1) u16);
test/behavior/fn.zig-201
......@@ -19,17 +19,6 @@ fn testLocVars(b: i32) void {
1919 if (a + b != 3) unreachable;
2020}
2121
22test "void parameters" {
23 try voidFun(1, void{}, 2, {});
24}
25fn voidFun(a: i32, b: void, c: i32, d: void) !void {
26 _ = d;
27 const v = b;
28 const vv: void = if (a == 1) v else {};
29 try expect(a + c == 3);
30 return vv;
31}
32
3322test "mutable local variables" {
3423 var zero: i32 = 0;
3524 try expect(zero == 0);
......@@ -54,14 +43,6 @@ test "separate block scopes" {
5443 try expect(c == 10);
5544}
5645
57test "call function with empty string" {
58 acceptsString("");
59}
60
61fn acceptsString(foo: []u8) void {
62 _ = foo;
63}
64
6546fn @"weird function name"() i32 {
6647 return 1234;
6748}
......@@ -69,51 +50,6 @@ test "weird function name" {
6950 try expect(@"weird function name"() == 1234);
7051}
7152
72test "implicit cast function unreachable return" {
73 wantsFnWithVoid(fnWithUnreachable);
74}
75
76fn wantsFnWithVoid(f: fn () void) void {
77 _ = f;
78}
79
80fn fnWithUnreachable() noreturn {
81 unreachable;
82}
83
84test "function pointers" {
85 const fns = [_]@TypeOf(fn1){
86 fn1,
87 fn2,
88 fn3,
89 fn4,
90 };
91 for (fns) |f, i| {
92 try expect(f() == @intCast(u32, i) + 5);
93 }
94}
95fn fn1() u32 {
96 return 5;
97}
98fn fn2() u32 {
99 return 6;
100}
101fn fn3() u32 {
102 return 7;
103}
104fn fn4() u32 {
105 return 8;
106}
107
108test "number literal as an argument" {
109 try numberLiteralArg(3);
110 comptime try numberLiteralArg(3);
111}
112
113fn numberLiteralArg(a: anytype) !void {
114 try expect(a == 3);
115}
116
11753test "assign inline fn to const variable" {
11854 const a = inlineFn;
11955 a();
......@@ -121,64 +57,6 @@ test "assign inline fn to const variable" {
12157
12258inline fn inlineFn() void {}
12359
124test "pass by non-copying value" {
125 try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3);
126}
127
128const Point = struct {
129 x: i32,
130 y: i32,
131};
132
133fn addPointCoords(pt: Point) i32 {
134 return pt.x + pt.y;
135}
136
137test "pass by non-copying value through var arg" {
138 try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3);
139}
140
141fn addPointCoordsVar(pt: anytype) !i32 {
142 comptime try expect(@TypeOf(pt) == Point);
143 return pt.x + pt.y;
144}
145
146test "pass by non-copying value as method" {
147 var pt = Point2{ .x = 1, .y = 2 };
148 try expect(pt.addPointCoords() == 3);
149}
150
151const Point2 = struct {
152 x: i32,
153 y: i32,
154
155 fn addPointCoords(self: Point2) i32 {
156 return self.x + self.y;
157 }
158};
159
160test "pass by non-copying value as method, which is generic" {
161 var pt = Point3{ .x = 1, .y = 2 };
162 try expect(pt.addPointCoords(i32) == 3);
163}
164
165const Point3 = struct {
166 x: i32,
167 y: i32,
168
169 fn addPointCoords(self: Point3, comptime T: type) i32 {
170 _ = T;
171 return self.x + self.y;
172 }
173};
174
175test "pass by non-copying value as method, at comptime" {
176 comptime {
177 var pt = Point2{ .x = 1, .y = 2 };
178 try expect(pt.addPointCoords() == 3);
179 }
180}
181
18260fn outer(y: u32) fn (u32) u32 {
18361 const Y = @TypeOf(y);
18462 const st = struct {
......@@ -194,43 +72,6 @@ test "return inner function which references comptime variable of outer function
19472 try expect(func(3) == 7);
19573}
19674
197test "extern struct with stdcallcc fn pointer" {
198 const S = extern struct {
199 ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32,
200
201 fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 {
202 return 1234;
203 }
204 };
205
206 var s: S = undefined;
207 s.ptr = S.foo;
208 try expect(s.ptr() == 1234);
209}
210
211test "implicit cast fn call result to optional in field result" {
212 const S = struct {
213 fn entry() !void {
214 var x = Foo{
215 .field = optionalPtr(),
216 };
217 try expect(x.field.?.* == 999);
218 }
219
220 const glob: i32 = 999;
221
222 fn optionalPtr() *const i32 {
223 return &glob;
224 }
225
226 const Foo = struct {
227 field: ?*const i32,
228 };
229 };
230 try S.entry();
231 comptime try S.entry();
232}
233
23475test "discard the result of a function that returns a struct" {
23576 const S = struct {
23677 fn entry() void {
......@@ -249,45 +90,3 @@ test "discard the result of a function that returns a struct" {
24990 S.entry();
25091 comptime S.entry();
25192}
252
253test "function call with anon list literal" {
254 const S = struct {
255 fn doTheTest() !void {
256 try consumeVec(.{ 9, 8, 7 });
257 }
258
259 fn consumeVec(vec: [3]f32) !void {
260 try expect(vec[0] == 9);
261 try expect(vec[1] == 8);
262 try expect(vec[2] == 7);
263 }
264 };
265 try S.doTheTest();
266 comptime try S.doTheTest();
267}
268
269test "ability to give comptime types and non comptime types to same parameter" {
270 const S = struct {
271 fn doTheTest() !void {
272 var x: i32 = 1;
273 try expect(foo(x) == 10);
274 try expect(foo(i32) == 20);
275 }
276
277 fn foo(arg: anytype) i32 {
278 if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20;
279 return 9 + arg;
280 }
281 };
282 try S.doTheTest();
283 comptime try S.doTheTest();
284}
285
286test "function with inferred error set but returning no error" {
287 const S = struct {
288 fn foo() !void {}
289 };
290
291 const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?;
292 try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len);
293}
test/behavior/fn_stage1.zig created+206
......@@ -0,0 +1,206 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const testing = std.testing;
4const expect = testing.expect;
5const expectEqual = testing.expectEqual;
6
7test "void parameters" {
8 try voidFun(1, void{}, 2, {});
9}
10fn voidFun(a: i32, b: void, c: i32, d: void) !void {
11 _ = d;
12 const v = b;
13 const vv: void = if (a == 1) v else {};
14 try expect(a + c == 3);
15 return vv;
16}
17
18test "call function with empty string" {
19 acceptsString("");
20}
21
22fn acceptsString(foo: []u8) void {
23 _ = foo;
24}
25
26test "implicit cast function unreachable return" {
27 wantsFnWithVoid(fnWithUnreachable);
28}
29
30fn wantsFnWithVoid(f: fn () void) void {
31 _ = f;
32}
33
34fn fnWithUnreachable() noreturn {
35 unreachable;
36}
37
38test "function pointers" {
39 const fns = [_]@TypeOf(fn1){
40 fn1,
41 fn2,
42 fn3,
43 fn4,
44 };
45 for (fns) |f, i| {
46 try expect(f() == @intCast(u32, i) + 5);
47 }
48}
49fn fn1() u32 {
50 return 5;
51}
52fn fn2() u32 {
53 return 6;
54}
55fn fn3() u32 {
56 return 7;
57}
58fn fn4() u32 {
59 return 8;
60}
61
62test "number literal as an argument" {
63 try numberLiteralArg(3);
64 comptime try numberLiteralArg(3);
65}
66
67fn numberLiteralArg(a: anytype) !void {
68 try expect(a == 3);
69}
70
71test "pass by non-copying value" {
72 try expect(addPointCoords(Point{ .x = 1, .y = 2 }) == 3);
73}
74
75const Point = struct {
76 x: i32,
77 y: i32,
78};
79
80fn addPointCoords(pt: Point) i32 {
81 return pt.x + pt.y;
82}
83
84test "pass by non-copying value through var arg" {
85 try expect((try addPointCoordsVar(Point{ .x = 1, .y = 2 })) == 3);
86}
87
88fn addPointCoordsVar(pt: anytype) !i32 {
89 comptime try expect(@TypeOf(pt) == Point);
90 return pt.x + pt.y;
91}
92
93test "pass by non-copying value as method" {
94 var pt = Point2{ .x = 1, .y = 2 };
95 try expect(pt.addPointCoords() == 3);
96}
97
98const Point2 = struct {
99 x: i32,
100 y: i32,
101
102 fn addPointCoords(self: Point2) i32 {
103 return self.x + self.y;
104 }
105};
106
107test "pass by non-copying value as method, which is generic" {
108 var pt = Point3{ .x = 1, .y = 2 };
109 try expect(pt.addPointCoords(i32) == 3);
110}
111
112const Point3 = struct {
113 x: i32,
114 y: i32,
115
116 fn addPointCoords(self: Point3, comptime T: type) i32 {
117 _ = T;
118 return self.x + self.y;
119 }
120};
121
122test "pass by non-copying value as method, at comptime" {
123 comptime {
124 var pt = Point2{ .x = 1, .y = 2 };
125 try expect(pt.addPointCoords() == 3);
126 }
127}
128
129test "extern struct with stdcallcc fn pointer" {
130 const S = extern struct {
131 ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32,
132
133 fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 {
134 return 1234;
135 }
136 };
137
138 var s: S = undefined;
139 s.ptr = S.foo;
140 try expect(s.ptr() == 1234);
141}
142
143test "implicit cast fn call result to optional in field result" {
144 const S = struct {
145 fn entry() !void {
146 var x = Foo{
147 .field = optionalPtr(),
148 };
149 try expect(x.field.?.* == 999);
150 }
151
152 const glob: i32 = 999;
153
154 fn optionalPtr() *const i32 {
155 return &glob;
156 }
157
158 const Foo = struct {
159 field: ?*const i32,
160 };
161 };
162 try S.entry();
163 comptime try S.entry();
164}
165
166test "function call with anon list literal" {
167 const S = struct {
168 fn doTheTest() !void {
169 try consumeVec(.{ 9, 8, 7 });
170 }
171
172 fn consumeVec(vec: [3]f32) !void {
173 try expect(vec[0] == 9);
174 try expect(vec[1] == 8);
175 try expect(vec[2] == 7);
176 }
177 };
178 try S.doTheTest();
179 comptime try S.doTheTest();
180}
181
182test "ability to give comptime types and non comptime types to same parameter" {
183 const S = struct {
184 fn doTheTest() !void {
185 var x: i32 = 1;
186 try expect(foo(x) == 10);
187 try expect(foo(i32) == 20);
188 }
189
190 fn foo(arg: anytype) i32 {
191 if (@typeInfo(@TypeOf(arg)) == .Type and arg == i32) return 20;
192 return 9 + arg;
193 }
194 };
195 try S.doTheTest();
196 comptime try S.doTheTest();
197}
198
199test "function with inferred error set but returning no error" {
200 const S = struct {
201 fn foo() !void {}
202 };
203
204 const return_ty = @typeInfo(@TypeOf(S.foo)).Fn.return_type.?;
205 try expectEqual(0, @typeInfo(@typeInfo(return_ty).ErrorUnion.error_set).ErrorSet.?.len);
206}
test/behavior/math.zig+187
......@@ -249,3 +249,190 @@ test "binary not" {
249249fn testBinaryNot(x: u16) !void {
250250 try expect(~x == 0b0101010101010101);
251251}
252
253test "division" {
254 try testDivision();
255 comptime try testDivision();
256}
257fn testDivision() !void {
258 try expect(div(u32, 13, 3) == 4);
259 try expect(div(f16, 1.0, 2.0) == 0.5);
260 try expect(div(f32, 1.0, 2.0) == 0.5);
261
262 try expect(divExact(u32, 55, 11) == 5);
263 try expect(divExact(i32, -55, 11) == -5);
264 try expect(divExact(f16, 55.0, 11.0) == 5.0);
265 try expect(divExact(f16, -55.0, 11.0) == -5.0);
266 try expect(divExact(f32, 55.0, 11.0) == 5.0);
267 try expect(divExact(f32, -55.0, 11.0) == -5.0);
268
269 try expect(divFloor(i32, 5, 3) == 1);
270 try expect(divFloor(i32, -5, 3) == -2);
271 try expect(divFloor(f16, 5.0, 3.0) == 1.0);
272 try expect(divFloor(f16, -5.0, 3.0) == -2.0);
273 try expect(divFloor(f32, 5.0, 3.0) == 1.0);
274 try expect(divFloor(f32, -5.0, 3.0) == -2.0);
275 try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
276 try expect(divFloor(i32, 0, -0x80000000) == 0);
277 try expect(divFloor(i32, -0x40000001, 0x40000000) == -2);
278 try expect(divFloor(i32, -0x80000000, 1) == -0x80000000);
279 try expect(divFloor(i32, 10, 12) == 0);
280 try expect(divFloor(i32, -14, 12) == -2);
281 try expect(divFloor(i32, -2, 12) == -1);
282
283 try expect(divTrunc(i32, 5, 3) == 1);
284 try expect(divTrunc(i32, -5, 3) == -1);
285 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
286 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
287 try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
288 try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
289 try expect(divTrunc(f64, 5.0, 3.0) == 1.0);
290 try expect(divTrunc(f64, -5.0, 3.0) == -1.0);
291 try expect(divTrunc(i32, 10, 12) == 0);
292 try expect(divTrunc(i32, -14, 12) == -1);
293 try expect(divTrunc(i32, -2, 12) == 0);
294
295 try expect(mod(i32, 10, 12) == 10);
296 try expect(mod(i32, -14, 12) == 10);
297 try expect(mod(i32, -2, 12) == 10);
298
299 comptime {
300 try expect(
301 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,
302 );
303 try expect(
304 @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,
305 );
306 try expect(
307 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
308 );
309 try expect(
310 @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
311 );
312 try expect(
313 @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
314 );
315 try expect(
316 @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
317 );
318 try expect(
319 4126227191251978491697987544882340798050766755606969681711 % 10 == 1,
320 );
321 }
322}
323fn div(comptime T: type, a: T, b: T) T {
324 return a / b;
325}
326fn divExact(comptime T: type, a: T, b: T) T {
327 return @divExact(a, b);
328}
329fn divFloor(comptime T: type, a: T, b: T) T {
330 return @divFloor(a, b);
331}
332fn divTrunc(comptime T: type, a: T, b: T) T {
333 return @divTrunc(a, b);
334}
335fn mod(comptime T: type, a: T, b: T) T {
336 return @mod(a, b);
337}
338
339test "unsigned wrapping" {
340 try testUnsignedWrappingEval(maxInt(u32));
341 comptime try testUnsignedWrappingEval(maxInt(u32));
342}
343fn testUnsignedWrappingEval(x: u32) !void {
344 const zero = x +% 1;
345 try expect(zero == 0);
346 const orig = zero -% 1;
347 try expect(orig == maxInt(u32));
348}
349
350test "signed wrapping" {
351 try testSignedWrappingEval(maxInt(i32));
352 comptime try testSignedWrappingEval(maxInt(i32));
353}
354fn testSignedWrappingEval(x: i32) !void {
355 const min_val = x +% 1;
356 try expect(min_val == minInt(i32));
357 const max_val = min_val -% 1;
358 try expect(max_val == maxInt(i32));
359}
360
361test "signed negation wrapping" {
362 try testSignedNegationWrappingEval(minInt(i16));
363 comptime try testSignedNegationWrappingEval(minInt(i16));
364}
365fn testSignedNegationWrappingEval(x: i16) !void {
366 try expect(x == -32768);
367 const neg = -%x;
368 try expect(neg == -32768);
369}
370
371test "unsigned negation wrapping" {
372 try testUnsignedNegationWrappingEval(1);
373 comptime try testUnsignedNegationWrappingEval(1);
374}
375fn testUnsignedNegationWrappingEval(x: u16) !void {
376 try expect(x == 1);
377 const neg = -%x;
378 try expect(neg == maxInt(u16));
379}
380
381test "unsigned 64-bit division" {
382 try test_u64_div();
383 comptime try test_u64_div();
384}
385fn test_u64_div() !void {
386 const result = divWithResult(1152921504606846976, 34359738365);
387 try expect(result.quotient == 33554432);
388 try expect(result.remainder == 100663296);
389}
390fn divWithResult(a: u64, b: u64) DivResult {
391 return DivResult{
392 .quotient = a / b,
393 .remainder = a % b,
394 };
395}
396const DivResult = struct {
397 quotient: u64,
398 remainder: u64,
399};
400
401test "truncating shift right" {
402 try testShrTrunc(maxInt(u16));
403 comptime try testShrTrunc(maxInt(u16));
404}
405fn testShrTrunc(x: u16) !void {
406 const shifted = x >> 1;
407 try expect(shifted == 32767);
408}
409
410test "f128" {
411 try test_f128();
412 comptime try test_f128();
413}
414
415fn make_f128(x: f128) f128 {
416 return x;
417}
418
419fn test_f128() !void {
420 try expect(@sizeOf(f128) == 16);
421 try expect(make_f128(1.0) == 1.0);
422 try expect(make_f128(1.0) != 1.1);
423 try expect(make_f128(1.0) > 0.9);
424 try expect(make_f128(1.0) >= 0.9);
425 try expect(make_f128(1.0) >= 1.0);
426 try should_not_be_zero(1.0);
427}
428
429fn should_not_be_zero(x: f128) !void {
430 try expect(x != 0.0);
431}
432
433test "128-bit multiplication" {
434 var a: i128 = 3;
435 var b: i128 = 2;
436 var c = a * b;
437 try expect(c == 6);
438}
test/behavior/math_stage1.zig-187
......@@ -6,92 +6,6 @@ const maxInt = std.math.maxInt;
66const minInt = std.math.minInt;
77const mem = std.mem;
88
9test "division" {
10 try testDivision();
11 comptime try testDivision();
12}
13fn testDivision() !void {
14 try expect(div(u32, 13, 3) == 4);
15 try expect(div(f16, 1.0, 2.0) == 0.5);
16 try expect(div(f32, 1.0, 2.0) == 0.5);
17
18 try expect(divExact(u32, 55, 11) == 5);
19 try expect(divExact(i32, -55, 11) == -5);
20 try expect(divExact(f16, 55.0, 11.0) == 5.0);
21 try expect(divExact(f16, -55.0, 11.0) == -5.0);
22 try expect(divExact(f32, 55.0, 11.0) == 5.0);
23 try expect(divExact(f32, -55.0, 11.0) == -5.0);
24
25 try expect(divFloor(i32, 5, 3) == 1);
26 try expect(divFloor(i32, -5, 3) == -2);
27 try expect(divFloor(f16, 5.0, 3.0) == 1.0);
28 try expect(divFloor(f16, -5.0, 3.0) == -2.0);
29 try expect(divFloor(f32, 5.0, 3.0) == 1.0);
30 try expect(divFloor(f32, -5.0, 3.0) == -2.0);
31 try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
32 try expect(divFloor(i32, 0, -0x80000000) == 0);
33 try expect(divFloor(i32, -0x40000001, 0x40000000) == -2);
34 try expect(divFloor(i32, -0x80000000, 1) == -0x80000000);
35 try expect(divFloor(i32, 10, 12) == 0);
36 try expect(divFloor(i32, -14, 12) == -2);
37 try expect(divFloor(i32, -2, 12) == -1);
38
39 try expect(divTrunc(i32, 5, 3) == 1);
40 try expect(divTrunc(i32, -5, 3) == -1);
41 try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
42 try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
43 try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
44 try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
45 try expect(divTrunc(f64, 5.0, 3.0) == 1.0);
46 try expect(divTrunc(f64, -5.0, 3.0) == -1.0);
47 try expect(divTrunc(i32, 10, 12) == 0);
48 try expect(divTrunc(i32, -14, 12) == -1);
49 try expect(divTrunc(i32, -2, 12) == 0);
50
51 try expect(mod(i32, 10, 12) == 10);
52 try expect(mod(i32, -14, 12) == 10);
53 try expect(mod(i32, -2, 12) == 10);
54
55 comptime {
56 try expect(
57 1194735857077236777412821811143690633098347576 % 508740759824825164163191790951174292733114988 == 177254337427586449086438229241342047632117600,
58 );
59 try expect(
60 @rem(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -177254337427586449086438229241342047632117600,
61 );
62 try expect(
63 1194735857077236777412821811143690633098347576 / 508740759824825164163191790951174292733114988 == 2,
64 );
65 try expect(
66 @divTrunc(-1194735857077236777412821811143690633098347576, 508740759824825164163191790951174292733114988) == -2,
67 );
68 try expect(
69 @divTrunc(1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == -2,
70 );
71 try expect(
72 @divTrunc(-1194735857077236777412821811143690633098347576, -508740759824825164163191790951174292733114988) == 2,
73 );
74 try expect(
75 4126227191251978491697987544882340798050766755606969681711 % 10 == 1,
76 );
77 }
78}
79fn div(comptime T: type, a: T, b: T) T {
80 return a / b;
81}
82fn divExact(comptime T: type, a: T, b: T) T {
83 return @divExact(a, b);
84}
85fn divFloor(comptime T: type, a: T, b: T) T {
86 return @divFloor(a, b);
87}
88fn divTrunc(comptime T: type, a: T, b: T) T {
89 return @divTrunc(a, b);
90}
91fn mod(comptime T: type, a: T, b: T) T {
92 return @mod(a, b);
93}
94
959test "@addWithOverflow" {
9610 var result: u8 = undefined;
9711 try expect(@addWithOverflow(u8, 250, 100, &result));
......@@ -157,68 +71,6 @@ fn testCtzVectors() !void {
15771 try expectEqual(@ctz(u16, @splat(64, @as(u16, 0b00000000))), @splat(64, @as(u5, 16)));
15872}
15973
160test "unsigned wrapping" {
161 try testUnsignedWrappingEval(maxInt(u32));
162 comptime try testUnsignedWrappingEval(maxInt(u32));
163}
164fn testUnsignedWrappingEval(x: u32) !void {
165 const zero = x +% 1;
166 try expect(zero == 0);
167 const orig = zero -% 1;
168 try expect(orig == maxInt(u32));
169}
170
171test "signed wrapping" {
172 try testSignedWrappingEval(maxInt(i32));
173 comptime try testSignedWrappingEval(maxInt(i32));
174}
175fn testSignedWrappingEval(x: i32) !void {
176 const min_val = x +% 1;
177 try expect(min_val == minInt(i32));
178 const max_val = min_val -% 1;
179 try expect(max_val == maxInt(i32));
180}
181
182test "signed negation wrapping" {
183 try testSignedNegationWrappingEval(minInt(i16));
184 comptime try testSignedNegationWrappingEval(minInt(i16));
185}
186fn testSignedNegationWrappingEval(x: i16) !void {
187 try expect(x == -32768);
188 const neg = -%x;
189 try expect(neg == -32768);
190}
191
192test "unsigned negation wrapping" {
193 try testUnsignedNegationWrappingEval(1);
194 comptime try testUnsignedNegationWrappingEval(1);
195}
196fn testUnsignedNegationWrappingEval(x: u16) !void {
197 try expect(x == 1);
198 const neg = -%x;
199 try expect(neg == maxInt(u16));
200}
201
202test "unsigned 64-bit division" {
203 try test_u64_div();
204 comptime try test_u64_div();
205}
206fn test_u64_div() !void {
207 const result = divWithResult(1152921504606846976, 34359738365);
208 try expect(result.quotient == 33554432);
209 try expect(result.remainder == 100663296);
210}
211fn divWithResult(a: u64, b: u64) DivResult {
212 return DivResult{
213 .quotient = a / b,
214 .remainder = a % b,
215 };
216}
217const DivResult = struct {
218 quotient: u64,
219 remainder: u64,
220};
221
22274test "small int addition" {
22375 var x: u2 = 0;
22476 try expect(x == 0);
......@@ -346,15 +198,6 @@ fn testShlTrunc(x: u16) !void {
346198 try expect(shifted == 65534);
347199}
348200
349test "truncating shift right" {
350 try testShrTrunc(maxInt(u16));
351 comptime try testShrTrunc(maxInt(u16));
352}
353fn testShrTrunc(x: u16) !void {
354 const shifted = x >> 1;
355 try expect(shifted == 32767);
356}
357
358201test "exact shift left" {
359202 try testShlExact(0b00110101);
360203 comptime try testShlExact(0b00110101);
......@@ -392,29 +235,6 @@ test "shift left/right on u0 operand" {
392235 comptime try S.doTheTest();
393236}
394237
395test "f128" {
396 try test_f128();
397 comptime try test_f128();
398}
399
400fn make_f128(x: f128) f128 {
401 return x;
402}
403
404fn test_f128() !void {
405 try expect(@sizeOf(f128) == 16);
406 try expect(make_f128(1.0) == 1.0);
407 try expect(make_f128(1.0) != 1.1);
408 try expect(make_f128(1.0) > 0.9);
409 try expect(make_f128(1.0) >= 0.9);
410 try expect(make_f128(1.0) >= 1.0);
411 try should_not_be_zero(1.0);
412}
413
414fn should_not_be_zero(x: f128) !void {
415 try expect(x != 0.0);
416}
417
418238test "comptime float rem int" {
419239 comptime {
420240 var x = @as(f32, 1) % 2;
......@@ -614,13 +434,6 @@ fn testNanEqNan(comptime F: type) !void {
614434 try expect(!(nan1 <= nan2));
615435}
616436
617test "128-bit multiplication" {
618 var a: i128 = 3;
619 var b: i128 = 2;
620 var c = a * b;
621 try expect(c == 6);
622}
623
624437test "vector comparison" {
625438 const S = struct {
626439 fn doTheTest() !void {