authorgravatar for 94326797+riverbl@users.noreply.github.comriverbl <94326797+riverbl@users.noreply.github.com> 2021-12-21 12:45:48+00:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-01-29 18:12:28+02:00
logaa29f4a8037ae74fb2d97793312ef8c5262d025a
tree440bd0236e200476ba5292b2d661a6546907fbb4
parentaca665cebd2b6ec9ec3db669cf5446ee45bbb5d0

stage1: fix saturating arithmetic producing incorrect results on type comptime_int, allow saturating left shift on type comptime int


3 files changed, 159 insertions(+), 15 deletions(-)

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)) {
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}