authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-01-30 11:40:03+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-01-30 11:40:03+02:00
log924eb08b613e3333419512f4de02b167aba9336d
tree6366aa385ba28c3193541fcc3d9f6874aaf52d13
parentf8e418c47d13189674bdf5f131c19fd811964579
parent3c53667db8d44777c2fbceaf3c6d3a22a6c9caad

Merge branch 'fixcomptimesat'

Closes #10393

6 files changed, 216 insertions(+), 49 deletions(-)

src/Sema.zig+23-13
......@@ -7474,7 +7474,10 @@ fn zirShl(
74747474 }
74757475 const val = switch (air_tag) {
74767476 .shl_exact => return sema.fail(block, lhs_src, "TODO implement Sema for comptime shl_exact", .{}),
7477 .shl_sat => try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, sema.mod.getTarget()),
7477 .shl_sat => if (lhs_ty.zigTypeTag() == .ComptimeInt)
7478 try lhs_val.shl(rhs_val, sema.arena)
7479 else
7480 try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, sema.mod.getTarget()),
74787481 .shl => try lhs_val.shl(rhs_val, sema.arena),
74797482 else => unreachable,
74807483 };
......@@ -8189,10 +8192,12 @@ fn analyzeArithmetic(
81898192 return casted_lhs;
81908193 }
81918194 if (maybe_lhs_val) |lhs_val| {
8192 return sema.addConstant(
8193 scalar_type,
8194 try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target),
8195 );
8195 const val = if (scalar_tag == .ComptimeInt)
8196 try lhs_val.intAdd(rhs_val, sema.arena)
8197 else
8198 try lhs_val.intAddSat(rhs_val, scalar_type, sema.arena, target);
8199
8200 return sema.addConstant(scalar_type, val);
81968201 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
81978202 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
81988203 },
......@@ -8280,10 +8285,12 @@ fn analyzeArithmetic(
82808285 return sema.addConstUndef(scalar_type);
82818286 }
82828287 if (maybe_rhs_val) |rhs_val| {
8283 return sema.addConstant(
8284 scalar_type,
8285 try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target),
8286 );
8288 const val = if (scalar_tag == .ComptimeInt)
8289 try lhs_val.intSub(rhs_val, sema.arena)
8290 else
8291 try lhs_val.intSubSat(rhs_val, scalar_type, sema.arena, target);
8292
8293 return sema.addConstant(scalar_type, val);
82878294 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
82888295 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
82898296 },
......@@ -8663,10 +8670,13 @@ fn analyzeArithmetic(
86638670 if (lhs_val.isUndef()) {
86648671 return sema.addConstUndef(scalar_type);
86658672 }
8666 return sema.addConstant(
8667 scalar_type,
8668 try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target),
8669 );
8673
8674 const val = if (scalar_tag == .ComptimeInt)
8675 try lhs_val.intMul(rhs_val, sema.arena)
8676 else
8677 try lhs_val.intMulSat(rhs_val, scalar_type, sema.arena, target);
8678
8679 return sema.addConstant(scalar_type, val);
86708680 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
86718681 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };
86728682 },
src/stage1/bigint.cpp+22-21
......@@ -60,6 +60,9 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
6060 bigint_init_unsigned(dest, 0);
6161 return;
6262 }
63
64 BigInt pos_op = {0};
65
6366 if (op->is_negative) {
6467 BigInt negated = {0};
6568 bigint_negate(&negated, op);
......@@ -70,13 +73,14 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
7073 BigInt one = {0};
7174 bigint_init_unsigned(&one, 1);
7275
73 bigint_add(dest, &inverted, &one);
74 return;
76 bigint_add(&pos_op, &inverted, &one);
77 } else {
78 bigint_init_bigint(&pos_op, op);
7579 }
7680
7781 dest->is_negative = false;
78 const uint64_t *op_digits = bigint_ptr(op);
79 if (op->digit_count == 1) {
82 const uint64_t *op_digits = bigint_ptr(&pos_op);
83 if (pos_op.digit_count == 1) {
8084 dest->data.digit = op_digits[0];
8185 if (bit_count < 64) {
8286 dest->data.digit &= (1ULL << bit_count) - 1;
......@@ -98,11 +102,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
98102 }
99103 dest->data.digits = heap::c_allocator.allocate_nonzero<uint64_t>(dest->digit_count);
100104 for (size_t i = 0; i < digits_to_copy; i += 1) {
101 uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
105 uint64_t digit = (i < pos_op.digit_count) ? op_digits[i] : 0;
102106 dest->data.digits[i] = digit;
103107 }
104108 if (leftover_bits != 0) {
105 uint64_t digit = (digits_to_copy < op->digit_count) ? op_digits[digits_to_copy] : 0;
109 uint64_t digit = (digits_to_copy < pos_op.digit_count) ? op_digits[digits_to_copy] : 0;
106110 dest->data.digits[digits_to_copy] = digit & ((1ULL << leftover_bits) - 1);
107111 }
108112 bigint_normalize(dest);
......@@ -469,18 +473,18 @@ void bigint_min(BigInt* dest, const BigInt *op1, const BigInt *op2) {
469473}
470474
471475/// clamps op within bit_count/signedness boundaries
472/// signed bounds are [-2^(bit_count-1)..2^(bit_count-1)-1]
473/// unsigned bounds are [0..2^bit_count-1]
476/// signed bounds are [-2^(bit_count-1)..2^(bit_count-1)-1]
477/// unsigned bounds are [0..2^bit_count-1]
474478void bigint_clamp_by_bitcount(BigInt* dest, uint32_t bit_count, bool is_signed) {
475 // compute the number of bits required to store the value, and use that
479 // compute the number of bits required to store the value, and use that
476480 // to decide whether to clamp the result
477481 bool is_negative = dest->is_negative;
478 // to workaround the fact this bits_needed calculation would yield 65 or more for
479 // all negative numbers, set is_negative to false. this is a cheap way to find
480 // bits_needed(abs(dest)).
482 // to workaround the fact this bits_needed calculation would yield 65 or more for
483 // all negative numbers, set is_negative to false. this is a cheap way to find
484 // bits_needed(abs(dest)).
481485 dest->is_negative = false;
482486 // because we've set is_negative to false, we have to account for the extra bit here
483 // by adding 1 additional bit_needed when (is_negative && !is_signed).
487 // by adding 1 additional bit_needed when (is_negative && !is_signed).
484488 size_t full_bits = dest->digit_count * 64;
485489 size_t leading_zero_count = bigint_clz(dest, full_bits);
486490 size_t bits_needed = full_bits - leading_zero_count + (is_negative && !is_signed);
......@@ -491,7 +495,7 @@ void bigint_clamp_by_bitcount(BigInt* dest, uint32_t bit_count, bool is_signed)
491495 bigint_init_unsigned(&one, 1);
492496 BigInt bit_count_big;
493497 bigint_init_unsigned(&bit_count_big, bit_count);
494
498
495499 if(is_signed) {
496500 if(is_negative) {
497501 BigInt bound;
......@@ -639,25 +643,22 @@ void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2) {
639643 size_t i = 1;
640644
641645 for (;;) {
642 bool found_digit = false;
643646 uint64_t x = bigger_op_digits[i];
644647 uint64_t prev_overflow = overflow;
645648 overflow = 0;
646649
647650 if (i < smaller_op->digit_count) {
648 found_digit = true;
649651 uint64_t digit = smaller_op_digits[i];
650652 overflow += sub_u64_overflow(x, digit, &x);
651653 }
652 if (sub_u64_overflow(x, prev_overflow, &x)) {
653 found_digit = true;
654 overflow += 1;
655 }
654
655 overflow += sub_u64_overflow(x, prev_overflow, &x);
656656 dest->data.digits[i] = x;
657657 i += 1;
658658
659 if (!found_digit || i >= bigger_op->digit_count)
659 if (i >= bigger_op->digit_count) {
660660 break;
661 }
661662 }
662663 assert(overflow == 0);
663664 dest->digit_count = i;
src/stage1/ir.cpp+23-15
......@@ -10230,13 +10230,7 @@ static Stage1AirInst *ir_analyze_bit_shift(IrAnalyze *ira, Stage1ZirInstBinOp *b
1023010230 // comptime_int has no finite bit width
1023110231 casted_op2 = op2;
1023210232
10233 if (op_id == IrBinOpShlSat) {
10234 ir_add_error_node(ira, bin_op_instruction->base.source_node,
10235 buf_sprintf("saturating shift on a comptime_int which has unlimited bits"));
10236 return ira->codegen->invalid_inst_gen;
10237 }
10238
10239 if (op_id == IrBinOpBitShiftLeftLossy) {
10233 if (op_id == IrBinOpBitShiftLeftLossy || op_id == IrBinOpShlSat) {
1024010234 op_id = IrBinOpBitShiftLeftExact;
1024110235 }
1024210236
......@@ -10398,6 +10392,25 @@ static bool ok_float_op(IrBinOp op) {
1039810392 zig_unreachable();
1039910393}
1040010394
10395static IrBinOp map_comptime_arithmetic_op(IrBinOp op) {
10396 switch (op) {
10397 case IrBinOpAddWrap:
10398 case IrBinOpAddSat:
10399 return IrBinOpAdd;
10400
10401 case IrBinOpSubWrap:
10402 case IrBinOpSubSat:
10403 return IrBinOpSub;
10404
10405 case IrBinOpMultWrap:
10406 case IrBinOpMultSat:
10407 return IrBinOpMult;
10408
10409 default:
10410 return op;
10411 }
10412}
10413
1040110414static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
1040210415 switch (op) {
1040310416 case IrBinOpAdd:
......@@ -10620,15 +10633,10 @@ static Stage1AirInst *ir_analyze_bin_op_math(IrAnalyze *ira, Stage1ZirInstBinOp
1062010633 if (type_is_invalid(casted_op2->value->type))
1062110634 return ira->codegen->invalid_inst_gen;
1062210635
10623 // Comptime integers have no fixed size
10636 // Comptime integers have no fixed size, so wrapping or saturating operations should be mapped
10637 // to their non wrapping or saturating equivalents
1062410638 if (scalar_type->id == ZigTypeIdComptimeInt) {
10625 if (op_id == IrBinOpAddWrap) {
10626 op_id = IrBinOpAdd;
10627 } else if (op_id == IrBinOpSubWrap) {
10628 op_id = IrBinOpSub;
10629 } else if (op_id == IrBinOpMultWrap) {
10630 op_id = IrBinOpMult;
10631 }
10639 op_id = map_comptime_arithmetic_op(op_id);
1063210640 }
1063310641
1063410642 if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) {
src/value.zig+12
......@@ -2275,6 +2275,10 @@ pub const Value = extern union {
22752275 ) !Value {
22762276 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
22772277
2278 if (ty.zigTypeTag() == .ComptimeInt) {
2279 return intAdd(lhs, rhs, arena);
2280 }
2281
22782282 if (ty.isAnyFloat()) {
22792283 return floatAdd(lhs, rhs, ty, arena);
22802284 }
......@@ -2361,6 +2365,10 @@ pub const Value = extern union {
23612365 ) !Value {
23622366 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
23632367
2368 if (ty.zigTypeTag() == .ComptimeInt) {
2369 return intSub(lhs, rhs, arena);
2370 }
2371
23642372 if (ty.isAnyFloat()) {
23652373 return floatSub(lhs, rhs, ty, arena);
23662374 }
......@@ -2440,6 +2448,10 @@ pub const Value = extern union {
24402448 ) !Value {
24412449 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
24422450
2451 if (ty.zigTypeTag() == .ComptimeInt) {
2452 return intMul(lhs, rhs, arena);
2453 }
2454
24432455 if (ty.isAnyFloat()) {
24442456 return floatMul(lhs, rhs, ty, arena);
24452457 }
test/behavior/saturating_arithmetic.zig+26
......@@ -29,8 +29,14 @@ test "saturating add" {
2929 try expect(x == expected);
3030 }
3131 };
32
3233 try S.doTheTest();
3334 comptime try S.doTheTest();
35
36 comptime try S.testSatAdd(comptime_int, 0, 0, 0);
37 comptime try S.testSatAdd(comptime_int, 3, 2, 5);
38 comptime try S.testSatAdd(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 1119305249183743626545271163355074748512);
39 comptime try S.testSatAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501);
3440}
3541
3642test "saturating subtraction" {
......@@ -56,8 +62,14 @@ test "saturating subtraction" {
5662 try expect(x == expected);
5763 }
5864 };
65
5966 try S.doTheTest();
6067 comptime try S.doTheTest();
68
69 comptime try S.testSatSub(comptime_int, 0, 0, 0);
70 comptime try S.testSatSub(comptime_int, 3, 2, 1);
71 comptime try S.testSatSub(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 182846383813587550256162760261375991602);
72 comptime try S.testSatSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515);
6173}
6274
6375test "saturating multiplication" {
......@@ -90,6 +102,11 @@ test "saturating multiplication" {
90102
91103 try S.doTheTest();
92104 comptime try S.doTheTest();
105
106 comptime try S.testSatMul(comptime_int, 0, 0, 0);
107 comptime try S.testSatMul(comptime_int, 3, 2, 6);
108 comptime try S.testSatMul(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 304852860194144160265083087140337419215516305999637969803722975979232817921935);
109 comptime try S.testSatMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556);
93110}
94111
95112test "saturating shift-left" {
......@@ -107,6 +124,7 @@ test "saturating shift-left" {
107124 try testSatShl(u8, 1, 2, 4);
108125 try testSatShl(u8, 255, 1, 255);
109126 }
127
110128 fn testSatShl(comptime T: type, lhs: T, rhs: T, expected: T) !void {
111129 try expect((lhs <<| rhs) == expected);
112130
......@@ -115,8 +133,14 @@ test "saturating shift-left" {
115133 try expect(x == expected);
116134 }
117135 };
136
118137 try S.doTheTest();
119138 comptime try S.doTheTest();
139
140 comptime try S.testSatShl(comptime_int, 0, 0, 0);
141 comptime try S.testSatShl(comptime_int, 1, 2, 4);
142 comptime try S.testSatShl(comptime_int, 13, 150, 18554220005177478453757717602843436772975706112);
143 comptime try S.testSatShl(comptime_int, -582769, 180, -893090893854873184096635538665358532628308979495815656505344);
120144}
121145
122146test "saturating shl uses the LHS type" {
......@@ -139,4 +163,6 @@ test "saturating shl uses the LHS type" {
139163 try expect((@as(u8, 1) <<| 8) == 255);
140164 try expect((@as(u8, 1) <<| rhs_const) == 255);
141165 try expect((@as(u8, 1) <<| rhs_var) == 255);
166
167 try expect((1 <<| @as(u8, 200)) == 1606938044258990275541962092341162602522202993782792835301376);
142168}
test/behavior/wrapping_arithmetic.zig created+110
......@@ -0,0 +1,110 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const minInt = std.math.minInt;
4const maxInt = std.math.maxInt;
5const expect = std.testing.expect;
6
7test "wrapping add" {
8 const S = struct {
9 fn doTheTest() !void {
10 try testWrapAdd(i8, -3, 10, 7);
11 try testWrapAdd(i8, -128, -128, 0);
12 try testWrapAdd(i2, 1, 1, -2);
13 try testWrapAdd(i64, maxInt(i64), 1, minInt(i64));
14 try testWrapAdd(i128, maxInt(i128), -maxInt(i128), 0);
15 try testWrapAdd(i128, minInt(i128), maxInt(i128), -1);
16 try testWrapAdd(i8, 127, 127, -2);
17 try testWrapAdd(u8, 3, 10, 13);
18 try testWrapAdd(u8, 255, 255, 254);
19 try testWrapAdd(u2, 3, 2, 1);
20 try testWrapAdd(u3, 7, 1, 0);
21 try testWrapAdd(u128, maxInt(u128), 1, minInt(u128));
22 }
23
24 fn testWrapAdd(comptime T: type, lhs: T, rhs: T, expected: T) !void {
25 try expect((lhs +% rhs) == expected);
26
27 var x = lhs;
28 x +%= rhs;
29 try expect(x == expected);
30 }
31 };
32
33 try S.doTheTest();
34 comptime try S.doTheTest();
35
36 comptime try S.testWrapAdd(comptime_int, 0, 0, 0);
37 comptime try S.testWrapAdd(comptime_int, 3, 2, 5);
38 comptime try S.testWrapAdd(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 1119305249183743626545271163355074748512);
39 comptime try S.testWrapAdd(comptime_int, 7, -593423721213448152027139550640105366508, -593423721213448152027139550640105366501);
40}
41
42test "wrapping subtraction" {
43 const S = struct {
44 fn doTheTest() !void {
45 try testWrapSub(i8, -3, 10, -13);
46 try testWrapSub(i8, -128, -128, 0);
47 try testWrapSub(i8, -1, 127, -128);
48 try testWrapSub(i64, minInt(i64), 1, maxInt(i64));
49 try testWrapSub(i128, maxInt(i128), -1, minInt(i128));
50 try testWrapSub(i128, minInt(i128), -maxInt(i128), -1);
51 try testWrapSub(u8, 10, 3, 7);
52 try testWrapSub(u8, 0, 255, 1);
53 try testWrapSub(u5, 0, 31, 1);
54 try testWrapSub(u128, 0, maxInt(u128), 1);
55 }
56
57 fn testWrapSub(comptime T: type, lhs: T, rhs: T, expected: T) !void {
58 try expect((lhs -% rhs) == expected);
59
60 var x = lhs;
61 x -%= rhs;
62 try expect(x == expected);
63 }
64 };
65
66 try S.doTheTest();
67 comptime try S.doTheTest();
68
69 comptime try S.testWrapSub(comptime_int, 0, 0, 0);
70 comptime try S.testWrapSub(comptime_int, 3, 2, 1);
71 comptime try S.testWrapSub(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 182846383813587550256162760261375991602);
72 comptime try S.testWrapSub(comptime_int, 7, -593423721213448152027139550640105366508, 593423721213448152027139550640105366515);
73}
74
75test "wrapping multiplication" {
76 // TODO: once #9660 has been solved, remove this line
77 if (builtin.cpu.arch == .wasm32) return error.SkipZigTest;
78
79 const S = struct {
80 fn doTheTest() !void {
81 try testWrapMul(i8, -3, 10, -30);
82 try testWrapMul(i4, 2, 4, -8);
83 try testWrapMul(i8, 2, 127, -2);
84 try testWrapMul(i8, -128, -128, 0);
85 try testWrapMul(i8, maxInt(i8), maxInt(i8), 1);
86 try testWrapMul(i16, maxInt(i16), -1, minInt(i16) + 1);
87 try testWrapMul(i128, maxInt(i128), -1, minInt(i128) + 1);
88 try testWrapMul(i128, minInt(i128), -1, minInt(i128));
89 try testWrapMul(u8, 10, 3, 30);
90 try testWrapMul(u8, 2, 255, 254);
91 try testWrapMul(u128, maxInt(u128), maxInt(u128), 1);
92 }
93
94 fn testWrapMul(comptime T: type, lhs: T, rhs: T, expected: T) !void {
95 try expect((lhs *% rhs) == expected);
96
97 var x = lhs;
98 x *%= rhs;
99 try expect(x == expected);
100 }
101 };
102
103 try S.doTheTest();
104 comptime try S.doTheTest();
105
106 comptime try S.testWrapMul(comptime_int, 0, 0, 0);
107 comptime try S.testWrapMul(comptime_int, 3, 2, 6);
108 comptime try S.testWrapMul(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 304852860194144160265083087140337419215516305999637969803722975979232817921935);
109 comptime try S.testWrapMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556);
110}