authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 18:55:43-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 19:19:28-07:00
log54675824449d16029fdf6a1873e78cb8f2147f60
treea3495ecdbbca9a963f514938f20003f1aeb69b64
parent71da169c67ad544bd1d4dfc4bfff9fe302e8284d

saturating arithmetic modifications

* Remove the builtins `@addWithSaturation`, `@subWithSaturation`, `@mulWithSaturation`, and `@shlWithSaturation` now that we have first-class syntax for saturating arithmetic. * langref: Clarify the behavior of `@shlExact`. * Ast: rename `bit_shift_left` to `shl` and `bit_shift_right` to `shr` for consistency. * Air: rename to include underscore separator with consistency with the rest of the ops. * Air: add shl_exact instruction * Use non-extended tags for saturating arithmetic, to keep it simple so that all the arithmetic operations can be done the same way. - Sema: unify analyzeArithmetic with analyzeSatArithmetic - implement comptime `+|`, `-|`, and `*|` - allow float operands to saturating arithmetic * `<<|` allows any integer type for the RHS. * C backend: fix rebase conflicts * LLVM backend: reduce the amount of branching for arithmetic ops * zig.h: fix magic number not matching actual size of C integer types

23 files changed, 616 insertions(+), 664 deletions(-)

doc/langref.html.in+7-56
...@@ -1407,7 +1407,6 @@ a +|= b{#endsyntax#}</pre></th>...@@ -1407,7 +1407,6 @@ a +|= b{#endsyntax#}</pre></th>
1407 <td>Saturating Addition.1407 <td>Saturating Addition.
1408 <ul>1408 <ul>
1409 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>1409 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1410 <li>See also {#link|@addWithSaturation#}.</li>
1411 </ul>1410 </ul>
1412 </td>1411 </td>
1413 <td>1412 <td>
...@@ -1464,7 +1463,6 @@ a -|= b{#endsyntax#}</pre></th>...@@ -1464,7 +1463,6 @@ a -|= b{#endsyntax#}</pre></th>
1464 <td>Saturating Subtraction.1463 <td>Saturating Subtraction.
1465 <ul>1464 <ul>
1466 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>1465 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1467 <li>See also {#link|@subWithSaturation#}.</li>
1468 </ul>1466 </ul>
1469 </td>1467 </td>
1470 <td>1468 <td>
...@@ -1556,7 +1554,6 @@ a *|= b{#endsyntax#}</pre></th>...@@ -1556,7 +1554,6 @@ a *|= b{#endsyntax#}</pre></th>
1556 <td>Saturating Multiplication.1554 <td>Saturating Multiplication.
1557 <ul>1555 <ul>
1558 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>1556 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1559 <li>See also {#link|@mulWithSaturation#}.</li>
1560 </ul>1557 </ul>
1561 </td>1558 </td>
1562 <td>1559 <td>
...@@ -7235,15 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {...@@ -7235,15 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
7235 If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.7232 If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
7236 </p>7233 </p>
7237 {#header_close#}7234 {#header_close#}
7238 {#header_open|@addWithSaturation#}
7239 <pre>{#syntax#}@addWithSaturation(a: T, b: T) T{#endsyntax#}</pre>
7240 <p>
7241 Returns {#syntax#}a + b{#endsyntax#}. The result will be clamped between the type maximum and minimum.
7242 </p>
7243 <p>
7244 The syntax {#syntax#}a +| b{#endsyntax#} is equivalent to calling {#syntax#}@addWithSaturation(a, b){#endsyntax#}.
7245 </p>
7246 {#header_close#}
7247 {#header_open|@alignCast#}7235 {#header_open|@alignCast#}
7248 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>7236 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>
7249 <p>7237 <p>
...@@ -8365,21 +8353,6 @@ test "@wasmMemoryGrow" {...@@ -8365,21 +8353,6 @@ test "@wasmMemoryGrow" {
8365 </p>8353 </p>
8366 {#header_close#}8354 {#header_close#}
8367 8355
8368 {#header_open|@mulWithSaturation#}
8369 <pre>{#syntax#}@mulWithSaturation(a: T, b: T) T{#endsyntax#}</pre>
8370 <p>
8371 Returns {#syntax#}a * b{#endsyntax#}. The result will be clamped between the type maximum and minimum.
8372 </p>
8373 <p>
8374 The syntax {#syntax#}a *| b{#endsyntax#} is equivalent to calling {#syntax#}@mulWithSaturation(a, b){#endsyntax#}.
8375 </p>
8376 <p>
8377 NOTE: Currently there is a bug in the llvm.smul.fix.sat intrinsic which affects {#syntax#}@mulWithSaturation{#endsyntax#} of signed integers.
8378 This may result in an incorrect sign bit when there is overflow. This will be fixed in zig's 0.9.0 release.
8379 Check <a href="https://github.com/ziglang/zig/issues/9643">this issue</a> for more information.
8380 </p>
8381 {#header_close#}
8382
8383 {#header_open|@panic#}8356 {#header_open|@panic#}
8384 <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre>8357 <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre>
8385 <p>8358 <p>
...@@ -8597,14 +8570,16 @@ test "@setRuntimeSafety" {...@@ -8597,14 +8570,16 @@ test "@setRuntimeSafety" {
8597 {#header_open|@shlExact#}8570 {#header_open|@shlExact#}
8598 <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>8571 <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
8599 <p>8572 <p>
8600 Performs the left shift operation ({#syntax#}<<{#endsyntax#}). Caller guarantees8573 Performs the left shift operation ({#syntax#}<<{#endsyntax#}).
8601 that the shift will not shift any 1 bits out.8574 For unsigned integers, the result is {#link|undefined#} if any 1 bits
8575 are shifted out. For signed integers, the result is {#link|undefined#} if
8576 any bits that disagree with the resultant sign bit are shifted out.
8602 </p>8577 </p>
8603 <p>8578 <p>
8604 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8579 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8605 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8580 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8606 </p>8581 </p>
8607 {#see_also|@shrExact|@shlWithOverflow|@shlWithSaturation#}8582 {#see_also|@shrExact|@shlWithOverflow#}
8608 {#header_close#}8583 {#header_close#}
86098584
8610 {#header_open|@shlWithOverflow#}8585 {#header_open|@shlWithOverflow#}
...@@ -8618,23 +8593,9 @@ test "@setRuntimeSafety" {...@@ -8618,23 +8593,9 @@ test "@setRuntimeSafety" {
8618 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8593 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8619 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8594 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8620 </p>8595 </p>
8621 {#see_also|@shlExact|@shrExact|@shlWithSaturation#}8596 {#see_also|@shlExact|@shrExact#}
8622 {#header_close#}8597 {#header_close#}
8623 8598
8624 {#header_open|@shlWithSaturation#}
8625 <pre>{#syntax#}@shlWithSaturation(a: T, shift_amt: T) T{#endsyntax#}</pre>
8626 <p>
8627 Returns {#syntax#}a << b{#endsyntax#}. The result will be clamped between type minimum and maximum.
8628 </p>
8629 <p>
8630 The syntax {#syntax#}a <<| b{#endsyntax#} is equivalent to calling {#syntax#}@shlWithSaturation(a, b){#endsyntax#}.
8631 </p>
8632 <p>
8633 Unlike other @shl builtins, shift_amt doesn't need to be a Log2T as saturated overshifting is well defined.
8634 </p>
8635 {#see_also|@shlExact|@shrExact|@shlWithOverflow#}
8636 {#header_close#}
8637
8638 {#header_open|@shrExact#}8599 {#header_open|@shrExact#}
8639 <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>8600 <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
8640 <p>8601 <p>
...@@ -8645,7 +8606,7 @@ test "@setRuntimeSafety" {...@@ -8645,7 +8606,7 @@ test "@setRuntimeSafety" {
8645 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8606 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8646 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8607 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8647 </p>8608 </p>
8648 {#see_also|@shlExact|@shlWithOverflow|@shlWithSaturation#}8609 {#see_also|@shlExact|@shlWithOverflow#}
8649 {#header_close#}8610 {#header_close#}
86508611
8651 {#header_open|@shuffle#}8612 {#header_open|@shuffle#}
...@@ -8945,16 +8906,6 @@ fn doTheTest() !void {...@@ -8945,16 +8906,6 @@ fn doTheTest() !void {
8945 </p>8906 </p>
8946 {#header_close#}8907 {#header_close#}
8947 8908
8948 {#header_open|@subWithSaturation#}
8949 <pre>{#syntax#}@subWithSaturation(a: T, b: T) T{#endsyntax#}</pre>
8950 <p>
8951 Returns {#syntax#}a - b{#endsyntax#}. The result will be clamped between the type maximum and minimum.
8952 </p>
8953 <p>
8954 The syntax {#syntax#}a -| b{#endsyntax#} is equivalent to calling {#syntax#}@subWithSaturation(a, b){#endsyntax#}.
8955 </p>
8956 {#header_close#}
8957
8958 {#header_open|@tagName#}8909 {#header_open|@tagName#}
8959 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>8910 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>
8960 <p>8911 <p>
lib/std/zig/Ast.zig+18-18
...@@ -395,9 +395,9 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -395,9 +395,9 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
395 .assign_mod,395 .assign_mod,
396 .assign_add,396 .assign_add,
397 .assign_sub,397 .assign_sub,
398 .assign_bit_shift_left,398 .assign_shl,
399 .assign_bit_shift_left_sat,399 .assign_shl_sat,
400 .assign_bit_shift_right,400 .assign_shr,
401 .assign_bit_and,401 .assign_bit_and,
402 .assign_bit_xor,402 .assign_bit_xor,
403 .assign_bit_or,403 .assign_bit_or,
...@@ -422,9 +422,9 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -422,9 +422,9 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
422 .sub_wrap,422 .sub_wrap,
423 .add_sat,423 .add_sat,
424 .sub_sat,424 .sub_sat,
425 .bit_shift_left,425 .shl,
426 .bit_shift_left_sat,426 .shl_sat,
427 .bit_shift_right,427 .shr,
428 .bit_and,428 .bit_and,
429 .bit_xor,429 .bit_xor,
430 .bit_or,430 .bit_or,
...@@ -659,9 +659,9 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -659,9 +659,9 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
659 .assign_mod,659 .assign_mod,
660 .assign_add,660 .assign_add,
661 .assign_sub,661 .assign_sub,
662 .assign_bit_shift_left,662 .assign_shl,
663 .assign_bit_shift_left_sat,663 .assign_shl_sat,
664 .assign_bit_shift_right,664 .assign_shr,
665 .assign_bit_and,665 .assign_bit_and,
666 .assign_bit_xor,666 .assign_bit_xor,
667 .assign_bit_or,667 .assign_bit_or,
...@@ -686,9 +686,9 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -686,9 +686,9 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
686 .sub_wrap,686 .sub_wrap,
687 .add_sat,687 .add_sat,
688 .sub_sat,688 .sub_sat,
689 .bit_shift_left,689 .shl,
690 .bit_shift_left_sat,690 .shl_sat,
691 .bit_shift_right,691 .shr,
692 .bit_and,692 .bit_and,
693 .bit_xor,693 .bit_xor,
694 .bit_or,694 .bit_or,
...@@ -2540,11 +2540,11 @@ pub const Node = struct {...@@ -2540,11 +2540,11 @@ pub const Node = struct {
2540 /// `lhs -= rhs`. main_token is op.2540 /// `lhs -= rhs`. main_token is op.
2541 assign_sub,2541 assign_sub,
2542 /// `lhs <<= rhs`. main_token is op.2542 /// `lhs <<= rhs`. main_token is op.
2543 assign_bit_shift_left,2543 assign_shl,
2544 /// `lhs <<|= rhs`. main_token is op.2544 /// `lhs <<|= rhs`. main_token is op.
2545 assign_bit_shift_left_sat,2545 assign_shl_sat,
2546 /// `lhs >>= rhs`. main_token is op.2546 /// `lhs >>= rhs`. main_token is op.
2547 assign_bit_shift_right,2547 assign_shr,
2548 /// `lhs &= rhs`. main_token is op.2548 /// `lhs &= rhs`. main_token is op.
2549 assign_bit_and,2549 assign_bit_and,
2550 /// `lhs ^= rhs`. main_token is op.2550 /// `lhs ^= rhs`. main_token is op.
...@@ -2594,11 +2594,11 @@ pub const Node = struct {...@@ -2594,11 +2594,11 @@ pub const Node = struct {
2594 /// `lhs -| rhs`. main_token is the `-|`.2594 /// `lhs -| rhs`. main_token is the `-|`.
2595 sub_sat,2595 sub_sat,
2596 /// `lhs << rhs`. main_token is the `<<`.2596 /// `lhs << rhs`. main_token is the `<<`.
2597 bit_shift_left,2597 shl,
2598 /// `lhs <<| rhs`. main_token is the `<<|`.2598 /// `lhs <<| rhs`. main_token is the `<<|`.
2599 bit_shift_left_sat,2599 shl_sat,
2600 /// `lhs >> rhs`. main_token is the `>>`.2600 /// `lhs >> rhs`. main_token is the `>>`.
2601 bit_shift_right,2601 shr,
2602 /// `lhs & rhs`. main_token is the `&`.2602 /// `lhs & rhs`. main_token is the `&`.
2603 bit_and,2603 bit_and,
2604 /// `lhs ^ rhs`. main_token is the `^`.2604 /// `lhs ^ rhs`. main_token is the `^`.
lib/std/zig/parse.zig+6-6
...@@ -1268,9 +1268,9 @@ const Parser = struct {...@@ -1268,9 +1268,9 @@ const Parser = struct {
1268 .percent_equal => .assign_mod,1268 .percent_equal => .assign_mod,
1269 .plus_equal => .assign_add,1269 .plus_equal => .assign_add,
1270 .minus_equal => .assign_sub,1270 .minus_equal => .assign_sub,
1271 .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,1271 .angle_bracket_angle_bracket_left_equal => .assign_shl,
1272 .angle_bracket_angle_bracket_left_pipe_equal => .assign_bit_shift_left_sat,1272 .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat,
1273 .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,1273 .angle_bracket_angle_bracket_right_equal => .assign_shr,
1274 .ampersand_equal => .assign_bit_and,1274 .ampersand_equal => .assign_bit_and,
1275 .caret_equal => .assign_bit_xor,1275 .caret_equal => .assign_bit_xor,
1276 .pipe_equal => .assign_bit_or,1276 .pipe_equal => .assign_bit_or,
...@@ -1346,9 +1346,9 @@ const Parser = struct {...@@ -1346,9 +1346,9 @@ const Parser = struct {
1346 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },1346 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },
1347 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },1347 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
13481348
1349 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left },1349 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl },
1350 .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .bit_shift_left_sat },1350 .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat },
1351 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right },1351 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr },
13521352
1353 .plus = .{ .prec = 60, .tag = .add },1353 .plus = .{ .prec = 60, .tag = .add },
1354 .minus = .{ .prec = 60, .tag = .sub },1354 .minus = .{ .prec = 60, .tag = .sub },
lib/std/zig/render.zig+10-10
...@@ -339,9 +339,9 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -339,9 +339,9 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
339 .assign,339 .assign,
340 .assign_bit_and,340 .assign_bit_and,
341 .assign_bit_or,341 .assign_bit_or,
342 .assign_bit_shift_left,342 .assign_shl,
343 .assign_bit_shift_left_sat,343 .assign_shl_sat,
344 .assign_bit_shift_right,344 .assign_shr,
345 .assign_bit_xor,345 .assign_bit_xor,
346 .assign_div,346 .assign_div,
347 .assign_sub,347 .assign_sub,
...@@ -357,9 +357,9 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -357,9 +357,9 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
357 .bang_equal,357 .bang_equal,
358 .bit_and,358 .bit_and,
359 .bit_or,359 .bit_or,
360 .bit_shift_left,360 .shl,
361 .bit_shift_left_sat,361 .shl_sat,
362 .bit_shift_right,362 .shr,
363 .bit_xor,363 .bit_xor,
364 .bool_and,364 .bool_and,
365 .bool_or,365 .bool_or,
...@@ -2528,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {...@@ -2528,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
2528 .assign,2528 .assign,
2529 .assign_bit_and,2529 .assign_bit_and,
2530 .assign_bit_or,2530 .assign_bit_or,
2531 .assign_bit_shift_left,2531 .assign_shl,
2532 .assign_bit_shift_right,2532 .assign_shr,
2533 .assign_bit_xor,2533 .assign_bit_xor,
2534 .assign_div,2534 .assign_div,
2535 .assign_sub,2535 .assign_sub,
...@@ -2542,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {...@@ -2542,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
2542 .bang_equal,2542 .bang_equal,
2543 .bit_and,2543 .bit_and,
2544 .bit_or,2544 .bit_or,
2545 .bit_shift_left,2545 .shl,
2546 .bit_shift_right,2546 .shr,
2547 .bit_xor,2547 .bit_xor,
2548 .bool_and,2548 .bool_and,
2549 .bool_or,2549 .bool_or,
src/Air.zig+12-6
...@@ -48,7 +48,7 @@ pub const Inst = struct {...@@ -48,7 +48,7 @@ pub const Inst = struct {
48 /// Both operands are guaranteed to be the same type, and the result type48 /// Both operands are guaranteed to be the same type, and the result type
49 /// is the same as both operands.49 /// is the same as both operands.
50 /// Uses the `bin_op` field.50 /// Uses the `bin_op` field.
51 addsat,51 add_sat,
52 /// Float or integer subtraction. For integers, wrapping is undefined behavior.52 /// Float or integer subtraction. For integers, wrapping is undefined behavior.
53 /// Both operands are guaranteed to be the same type, and the result type53 /// Both operands are guaranteed to be the same type, and the result type
54 /// is the same as both operands.54 /// is the same as both operands.
...@@ -63,7 +63,7 @@ pub const Inst = struct {...@@ -63,7 +63,7 @@ pub const Inst = struct {
63 /// Both operands are guaranteed to be the same type, and the result type63 /// Both operands are guaranteed to be the same type, and the result type
64 /// is the same as both operands.64 /// is the same as both operands.
65 /// Uses the `bin_op` field.65 /// Uses the `bin_op` field.
66 subsat,66 sub_sat,
67 /// Float or integer multiplication. For integers, wrapping is undefined behavior.67 /// Float or integer multiplication. For integers, wrapping is undefined behavior.
68 /// Both operands are guaranteed to be the same type, and the result type68 /// Both operands are guaranteed to be the same type, and the result type
69 /// is the same as both operands.69 /// is the same as both operands.
...@@ -78,7 +78,7 @@ pub const Inst = struct {...@@ -78,7 +78,7 @@ pub const Inst = struct {
78 /// Both operands are guaranteed to be the same type, and the result type78 /// Both operands are guaranteed to be the same type, and the result type
79 /// is the same as both operands.79 /// is the same as both operands.
80 /// Uses the `bin_op` field.80 /// Uses the `bin_op` field.
81 mulsat,81 mul_sat,
82 /// Integer or float division. For integers, wrapping is undefined behavior.82 /// Integer or float division. For integers, wrapping is undefined behavior.
83 /// Both operands are guaranteed to be the same type, and the result type83 /// Both operands are guaranteed to be the same type, and the result type
84 /// is the same as both operands.84 /// is the same as both operands.
...@@ -125,6 +125,11 @@ pub const Inst = struct {...@@ -125,6 +125,11 @@ pub const Inst = struct {
125 /// Shift left. `<<`125 /// Shift left. `<<`
126 /// Uses the `bin_op` field.126 /// Uses the `bin_op` field.
127 shl,127 shl,
128 /// Shift left; For unsigned integers, the shift produces a poison value if it shifts
129 /// out any non-zero bits. For signed integers, the shift produces a poison value if
130 /// it shifts out any bits that disagree with the resultant sign bit.
131 /// Uses the `bin_op` field.
132 shl_exact,
128 /// Shift left saturating. `<<|`133 /// Shift left saturating. `<<|`
129 /// Uses the `bin_op` field.134 /// Uses the `bin_op` field.
130 shl_sat,135 shl_sat,
...@@ -586,13 +591,13 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -586,13 +591,13 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
586591
587 .add,592 .add,
588 .addwrap,593 .addwrap,
589 .addsat,594 .add_sat,
590 .sub,595 .sub,
591 .subwrap,596 .subwrap,
592 .subsat,597 .sub_sat,
593 .mul,598 .mul,
594 .mulwrap,599 .mulwrap,
595 .mulsat,600 .mul_sat,
596 .div,601 .div,
597 .rem,602 .rem,
598 .mod,603 .mod,
...@@ -603,6 +608,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -603,6 +608,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
603 .ptr_sub,608 .ptr_sub,
604 .shr,609 .shr,
605 .shl,610 .shl,
611 .shl_exact,
606 .shl_sat,612 .shl_sat,
607 => return air.typeOf(datas[inst].bin_op.lhs),613 => return air.typeOf(datas[inst].bin_op.lhs),
608614
src/AstGen.zig+54-99
...@@ -317,9 +317,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins...@@ -317,9 +317,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
317 .assign,317 .assign,
318 .assign_bit_and,318 .assign_bit_and,
319 .assign_bit_or,319 .assign_bit_or,
320 .assign_bit_shift_left,320 .assign_shl,
321 .assign_bit_shift_left_sat,321 .assign_shl_sat,
322 .assign_bit_shift_right,322 .assign_shr,
323 .assign_bit_xor,323 .assign_bit_xor,
324 .assign_div,324 .assign_div,
325 .assign_sub,325 .assign_sub,
...@@ -345,9 +345,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins...@@ -345,9 +345,9 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
345 .mod,345 .mod,
346 .bit_and,346 .bit_and,
347 .bit_or,347 .bit_or,
348 .bit_shift_left,348 .shl,
349 .bit_shift_left_sat,349 .shl_sat,
350 .bit_shift_right,350 .shr,
351 .bit_xor,351 .bit_xor,
352 .bang_equal,352 .bang_equal,
353 .equal_equal,353 .equal_equal,
...@@ -530,15 +530,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -530,15 +530,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
530 return rvalue(gz, rl, .void_value, node);530 return rvalue(gz, rl, .void_value, node);
531 },531 },
532532
533 .assign_bit_shift_left => {533 .assign_shl => {
534 try assignShift(gz, scope, node, .shl);534 try assignShift(gz, scope, node, .shl);
535 return rvalue(gz, rl, .void_value, node);535 return rvalue(gz, rl, .void_value, node);
536 },536 },
537 .assign_bit_shift_left_sat => {537 .assign_shl_sat => {
538 try assignOpExt(gz, scope, node, .shl_with_saturation, Zir.Inst.SaturatingArithmetic);538 try assignShiftSat(gz, scope, node);
539 return rvalue(gz, rl, .void_value, node);539 return rvalue(gz, rl, .void_value, node);
540 },540 },
541 .assign_bit_shift_right => {541 .assign_shr => {
542 try assignShift(gz, scope, node, .shr);542 try assignShift(gz, scope, node, .shr);
543 return rvalue(gz, rl, .void_value, node);543 return rvalue(gz, rl, .void_value, node);
544 },544 },
...@@ -568,7 +568,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -568,7 +568,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
568 return rvalue(gz, rl, .void_value, node);568 return rvalue(gz, rl, .void_value, node);
569 },569 },
570 .assign_sub_sat => {570 .assign_sub_sat => {
571 try assignOpExt(gz, scope, node, .sub_with_saturation, Zir.Inst.SaturatingArithmetic);571 try assignOp(gz, scope, node, .sub_sat);
572 return rvalue(gz, rl, .void_value, node);572 return rvalue(gz, rl, .void_value, node);
573 },573 },
574 .assign_mod => {574 .assign_mod => {
...@@ -584,7 +584,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -584,7 +584,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
584 return rvalue(gz, rl, .void_value, node);584 return rvalue(gz, rl, .void_value, node);
585 },585 },
586 .assign_add_sat => {586 .assign_add_sat => {
587 try assignOpExt(gz, scope, node, .add_with_saturation, Zir.Inst.SaturatingArithmetic);587 try assignOp(gz, scope, node, .add_sat);
588 return rvalue(gz, rl, .void_value, node);588 return rvalue(gz, rl, .void_value, node);
589 },589 },
590 .assign_mul => {590 .assign_mul => {
...@@ -596,28 +596,27 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -596,28 +596,27 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
596 return rvalue(gz, rl, .void_value, node);596 return rvalue(gz, rl, .void_value, node);
597 },597 },
598 .assign_mul_sat => {598 .assign_mul_sat => {
599 try assignOpExt(gz, scope, node, .mul_with_saturation, Zir.Inst.SaturatingArithmetic);599 try assignOp(gz, scope, node, .mul_sat);
600 return rvalue(gz, rl, .void_value, node);600 return rvalue(gz, rl, .void_value, node);
601 },601 },
602602
603 // zig fmt: off603 // zig fmt: off
604 .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),604 .shl => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
605 .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),605 .shr => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
606606
607 .add => return simpleBinOp(gz, scope, rl, node, .add),607 .add => return simpleBinOp(gz, scope, rl, node, .add),
608 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),608 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
609 .add_sat => return simpleBinOp(gz, scope, rl, node, .add_sat),
609 .sub => return simpleBinOp(gz, scope, rl, node, .sub),610 .sub => return simpleBinOp(gz, scope, rl, node, .sub),
610 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),611 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
612 .sub_sat => return simpleBinOp(gz, scope, rl, node, .sub_sat),
611 .mul => return simpleBinOp(gz, scope, rl, node, .mul),613 .mul => return simpleBinOp(gz, scope, rl, node, .mul),
612 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),614 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
615 .mul_sat => return simpleBinOp(gz, scope, rl, node, .mul_sat),
613 .div => return simpleBinOp(gz, scope, rl, node, .div),616 .div => return simpleBinOp(gz, scope, rl, node, .div),
614 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),617 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
618 .shl_sat => return simpleBinOp(gz, scope, rl, node, .shl_sat),
615619
616 .add_sat => return simpleBinOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .add_with_saturation, Zir.Inst.SaturatingArithmetic),
617 .sub_sat => return simpleBinOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .sub_with_saturation, Zir.Inst.SaturatingArithmetic),
618 .mul_sat => return simpleBinOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .mul_with_saturation, Zir.Inst.SaturatingArithmetic),
619 .bit_shift_left_sat => return simpleBinOpExt(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl_with_saturation, Zir.Inst.SaturatingArithmetic),
620
621 .bit_and => {620 .bit_and => {
622 const current_ampersand_token = main_tokens[node];621 const current_ampersand_token = main_tokens[node];
623 if (token_tags[current_ampersand_token + 1] == .ampersand) {622 if (token_tags[current_ampersand_token + 1] == .ampersand) {
...@@ -1928,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod...@@ -1928,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
19281927
1929 .assign => try assign(gz, scope, statement),1928 .assign => try assign(gz, scope, statement),
19301929
1931 .assign_bit_shift_left => try assignShift(gz, scope, statement, .shl),1930 .assign_shl => try assignShift(gz, scope, statement, .shl),
1932 .assign_bit_shift_right => try assignShift(gz, scope, statement, .shr),1931 .assign_shr => try assignShift(gz, scope, statement, .shr),
19331932
1934 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),1933 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
1935 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),1934 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),
...@@ -1979,6 +1978,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -1979,6 +1978,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1979 // ZIR instructions that might be a type other than `noreturn` or `void`.1978 // ZIR instructions that might be a type other than `noreturn` or `void`.
1980 .add,1979 .add,
1981 .addwrap,1980 .addwrap,
1981 .add_sat,
1982 .param,1982 .param,
1983 .param_comptime,1983 .param_comptime,
1984 .param_anytype,1984 .param_anytype,
...@@ -2045,12 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2045,12 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2045 .mod_rem,2045 .mod_rem,
2046 .mul,2046 .mul,
2047 .mulwrap,2047 .mulwrap,
2048 .mul_sat,
2048 .ref,2049 .ref,
2049 .shl,2050 .shl,
2051 .shl_sat,
2050 .shr,2052 .shr,
2051 .str,2053 .str,
2052 .sub,2054 .sub,
2053 .subwrap,2055 .subwrap,
2056 .sub_sat,
2054 .negate,2057 .negate,
2055 .negate_wrap,2058 .negate_wrap,
2056 .typeof,2059 .typeof,
...@@ -2715,55 +2718,30 @@ fn assignOp(...@@ -2715,55 +2718,30 @@ fn assignOp(
2715 _ = try gz.addBin(.store, lhs_ptr, result);2718 _ = try gz.addBin(.store, lhs_ptr, result);
2716}2719}
27172720
2718fn simpleBinOpExt(2721fn assignShift(
2719 gz: *GenZir,
2720 scope: *Scope,
2721 rl: ResultLoc,
2722 infix_node: Ast.Node.Index,
2723 lhs_node: Ast.Node.Index,
2724 rhs_node: Ast.Node.Index,
2725 tag: Zir.Inst.Extended,
2726 comptime T: type,
2727) InnerError!Zir.Inst.Ref {
2728 const lhs = try expr(gz, scope, .none, lhs_node);
2729 const rhs = try expr(gz, scope, .none, rhs_node);
2730 const result = try gz.addExtendedPayload(tag, T{
2731 .node = gz.nodeIndexToRelative(infix_node),
2732 .lhs = lhs,
2733 .rhs = rhs,
2734 });
2735 return rvalue(gz, rl, result, infix_node);
2736}
2737
2738fn assignOpExt(
2739 gz: *GenZir,2722 gz: *GenZir,
2740 scope: *Scope,2723 scope: *Scope,
2741 infix_node: Ast.Node.Index,2724 infix_node: Ast.Node.Index,
2742 op_inst_tag: Zir.Inst.Extended,2725 op_inst_tag: Zir.Inst.Tag,
2743 comptime T: type,
2744) InnerError!void {2726) InnerError!void {
2727 try emitDbgNode(gz, infix_node);
2745 const astgen = gz.astgen;2728 const astgen = gz.astgen;
2746 const tree = astgen.tree;2729 const tree = astgen.tree;
2747 const node_datas = tree.nodes.items(.data);2730 const node_datas = tree.nodes.items(.data);
27482731
2749 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);2732 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
2750 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);2733 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
2751 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);2734 const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
2752 const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs);2735 const rhs = try expr(gz, scope, .{ .ty = rhs_type }, node_datas[infix_node].rhs);
2753 const result = try gz.addExtendedPayload(op_inst_tag, T{2736
2754 .node = gz.nodeIndexToRelative(infix_node),2737 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
2755 .lhs = lhs,2738 .lhs = lhs,
2756 .rhs = rhs,2739 .rhs = rhs,
2757 });2740 });
2758 _ = try gz.addBin(.store, lhs_ptr, result);2741 _ = try gz.addBin(.store, lhs_ptr, result);
2759}2742}
27602743
2761fn assignShift(2744fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
2762 gz: *GenZir,
2763 scope: *Scope,
2764 infix_node: Ast.Node.Index,
2765 op_inst_tag: Zir.Inst.Tag,
2766) InnerError!void {
2767 try emitDbgNode(gz, infix_node);2745 try emitDbgNode(gz, infix_node);
2768 const astgen = gz.astgen;2746 const astgen = gz.astgen;
2769 const tree = astgen.tree;2747 const tree = astgen.tree;
...@@ -2771,10 +2749,10 @@ fn assignShift(...@@ -2771,10 +2749,10 @@ fn assignShift(
27712749
2772 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);2750 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
2773 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);2751 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
2774 const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);2752 // Saturating shift-left allows any integer type for both the LHS and RHS.
2775 const rhs = try expr(gz, scope, .{ .ty = rhs_type }, node_datas[infix_node].rhs);2753 const rhs = try expr(gz, scope, .none, node_datas[infix_node].rhs);
27762754
2777 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{2755 const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
2778 .lhs = lhs,2756 .lhs = lhs,
2779 .rhs = rhs,2757 .rhs = rhs,
2780 });2758 });
...@@ -7556,11 +7534,6 @@ fn builtinCall(...@@ -7556,11 +7534,6 @@ fn builtinCall(
7556 return rvalue(gz, rl, result, node);7534 return rvalue(gz, rl, result, node);
7557 },7535 },
75587536
7559 .add_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .add_with_saturation),
7560 .sub_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .sub_with_saturation),
7561 .mul_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .mul_with_saturation),
7562 .shl_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .shl_with_saturation),
7563
7564 .atomic_load => {7537 .atomic_load => {
7565 const int_type = try typeExpr(gz, scope, params[0]);7538 const int_type = try typeExpr(gz, scope, params[0]);
7566 // TODO allow this pointer type to be volatile7539 // TODO allow this pointer type to be volatile
...@@ -7955,24 +7928,6 @@ fn overflowArithmetic(...@@ -7955,24 +7928,6 @@ fn overflowArithmetic(
7955 return rvalue(gz, rl, result, node);7928 return rvalue(gz, rl, result, node);
7956}7929}
79577930
7958fn saturatingArithmetic(
7959 gz: *GenZir,
7960 scope: *Scope,
7961 rl: ResultLoc,
7962 node: Ast.Node.Index,
7963 params: []const Ast.Node.Index,
7964 tag: Zir.Inst.Extended,
7965) InnerError!Zir.Inst.Ref {
7966 const lhs = try expr(gz, scope, .none, params[0]);
7967 const rhs = try expr(gz, scope, .none, params[1]);
7968 const result = try gz.addExtendedPayload(tag, Zir.Inst.SaturatingArithmetic{
7969 .node = gz.nodeIndexToRelative(node),
7970 .lhs = lhs,
7971 .rhs = rhs,
7972 });
7973 return rvalue(gz, rl, result, node);
7974}
7975
7976fn callExpr(7931fn callExpr(
7977 gz: *GenZir,7932 gz: *GenZir,
7978 scope: *Scope,7933 scope: *Scope,
...@@ -8198,9 +8153,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool...@@ -8198,9 +8153,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
8198 .assign,8153 .assign,
8199 .assign_bit_and,8154 .assign_bit_and,
8200 .assign_bit_or,8155 .assign_bit_or,
8201 .assign_bit_shift_left,8156 .assign_shl,
8202 .assign_bit_shift_left_sat,8157 .assign_shl_sat,
8203 .assign_bit_shift_right,8158 .assign_shr,
8204 .assign_bit_xor,8159 .assign_bit_xor,
8205 .assign_div,8160 .assign_div,
8206 .assign_sub,8161 .assign_sub,
...@@ -8216,9 +8171,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool...@@ -8216,9 +8171,9 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
8216 .bang_equal,8171 .bang_equal,
8217 .bit_and,8172 .bit_and,
8218 .bit_or,8173 .bit_or,
8219 .bit_shift_left,8174 .shl,
8220 .bit_shift_left_sat,8175 .shl_sat,
8221 .bit_shift_right,8176 .shr,
8222 .bit_xor,8177 .bit_xor,
8223 .bool_and,8178 .bool_and,
8224 .bool_or,8179 .bool_or,
...@@ -8439,9 +8394,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never...@@ -8439,9 +8394,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
8439 .assign,8394 .assign,
8440 .assign_bit_and,8395 .assign_bit_and,
8441 .assign_bit_or,8396 .assign_bit_or,
8442 .assign_bit_shift_left,8397 .assign_shl,
8443 .assign_bit_shift_left_sat,8398 .assign_shl_sat,
8444 .assign_bit_shift_right,8399 .assign_shr,
8445 .assign_bit_xor,8400 .assign_bit_xor,
8446 .assign_div,8401 .assign_div,
8447 .assign_sub,8402 .assign_sub,
...@@ -8457,9 +8412,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never...@@ -8457,9 +8412,9 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
8457 .bang_equal,8412 .bang_equal,
8458 .bit_and,8413 .bit_and,
8459 .bit_or,8414 .bit_or,
8460 .bit_shift_left,8415 .shl,
8461 .bit_shift_left_sat,8416 .shl_sat,
8462 .bit_shift_right,8417 .shr,
8463 .bit_xor,8418 .bit_xor,
8464 .bool_and,8419 .bool_and,
8465 .bool_or,8420 .bool_or,
...@@ -8619,9 +8574,9 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -8619,9 +8574,9 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
8619 .assign,8574 .assign,
8620 .assign_bit_and,8575 .assign_bit_and,
8621 .assign_bit_or,8576 .assign_bit_or,
8622 .assign_bit_shift_left,8577 .assign_shl,
8623 .assign_bit_shift_left_sat,8578 .assign_shl_sat,
8624 .assign_bit_shift_right,8579 .assign_shr,
8625 .assign_bit_xor,8580 .assign_bit_xor,
8626 .assign_div,8581 .assign_div,
8627 .assign_sub,8582 .assign_sub,
...@@ -8637,9 +8592,9 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -8637,9 +8592,9 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
8637 .bang_equal,8592 .bang_equal,
8638 .bit_and,8593 .bit_and,
8639 .bit_or,8594 .bit_or,
8640 .bit_shift_left,8595 .shl,
8641 .bit_shift_left_sat,8596 .shl_sat,
8642 .bit_shift_right,8597 .shr,
8643 .bit_xor,8598 .bit_xor,
8644 .bool_and,8599 .bool_and,
8645 .bool_or,8600 .bool_or,
src/BuiltinFn.zig-32
...@@ -2,7 +2,6 @@ const std = @import("std");...@@ -2,7 +2,6 @@ const std = @import("std");
22
3pub const Tag = enum {3pub const Tag = enum {
4 add_with_overflow,4 add_with_overflow,
5 add_with_saturation,
6 align_cast,5 align_cast,
7 align_of,6 align_of,
8 as,7 as,
...@@ -66,7 +65,6 @@ pub const Tag = enum {...@@ -66,7 +65,6 @@ pub const Tag = enum {
66 wasm_memory_grow,65 wasm_memory_grow,
67 mod,66 mod,
68 mul_with_overflow,67 mul_with_overflow,
69 mul_with_saturation,
70 panic,68 panic,
71 pop_count,69 pop_count,
72 ptr_cast,70 ptr_cast,
...@@ -81,12 +79,10 @@ pub const Tag = enum {...@@ -81,12 +79,10 @@ pub const Tag = enum {
81 set_runtime_safety,79 set_runtime_safety,
82 shl_exact,80 shl_exact,
83 shl_with_overflow,81 shl_with_overflow,
84 shl_with_saturation,
85 shr_exact,82 shr_exact,
86 shuffle,83 shuffle,
87 size_of,84 size_of,
88 splat,85 splat,
89 sub_with_saturation,
90 reduce,86 reduce,
91 src,87 src,
92 sqrt,88 sqrt,
...@@ -531,34 +527,6 @@ pub const list = list: {...@@ -531,34 +527,6 @@ pub const list = list: {
531 .param_count = 2,527 .param_count = 2,
532 },528 },
533 },529 },
534 .{
535 "@addWithSaturation",
536 .{
537 .tag = .add_with_saturation,
538 .param_count = 2,
539 },
540 },
541 .{
542 "@subWithSaturation",
543 .{
544 .tag = .sub_with_saturation,
545 .param_count = 2,
546 },
547 },
548 .{
549 "@mulWithSaturation",
550 .{
551 .tag = .mul_with_saturation,
552 .param_count = 2,
553 },
554 },
555 .{
556 "@shlWithSaturation",
557 .{
558 .tag = .shl_with_saturation,
559 .param_count = 2,
560 },
561 },
562 .{530 .{
563 "@memcpy",531 "@memcpy",
564 .{532 .{
src/Liveness.zig+4-3
...@@ -226,13 +226,13 @@ fn analyzeInst(...@@ -226,13 +226,13 @@ fn analyzeInst(
226 switch (inst_tags[inst]) {226 switch (inst_tags[inst]) {
227 .add,227 .add,
228 .addwrap,228 .addwrap,
229 .addsat,229 .add_sat,
230 .sub,230 .sub,
231 .subwrap,231 .subwrap,
232 .subsat,232 .sub_sat,
233 .mul,233 .mul,
234 .mulwrap,234 .mulwrap,
235 .mulsat,235 .mul_sat,
236 .div,236 .div,
237 .rem,237 .rem,
238 .mod,238 .mod,
...@@ -255,6 +255,7 @@ fn analyzeInst(...@@ -255,6 +255,7 @@ fn analyzeInst(
255 .ptr_elem_val,255 .ptr_elem_val,
256 .ptr_ptr_elem_val,256 .ptr_ptr_elem_val,
257 .shl,257 .shl,
258 .shl_exact,
258 .shl_sat,259 .shl_sat,
259 .shr,260 .shr,
260 .atomic_store_unordered,261 .atomic_store_unordered,
src/Sema.zig+113-121
...@@ -246,7 +246,6 @@ pub fn analyzeBody(...@@ -246,7 +246,6 @@ pub fn analyzeBody(
246 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),246 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
247 .ref => try sema.zirRef(block, inst),247 .ref => try sema.zirRef(block, inst),
248 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),248 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),
249 .shl => try sema.zirShl(block, inst),
250 .shr => try sema.zirShr(block, inst),249 .shr => try sema.zirShr(block, inst),
251 .slice_end => try sema.zirSliceEnd(block, inst),250 .slice_end => try sema.zirSliceEnd(block, inst),
252 .slice_sentinel => try sema.zirSliceSentinel(block, inst),251 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
...@@ -319,7 +318,6 @@ pub fn analyzeBody(...@@ -319,7 +318,6 @@ pub fn analyzeBody(
319 .div_exact => try sema.zirDivExact(block, inst),318 .div_exact => try sema.zirDivExact(block, inst),
320 .div_floor => try sema.zirDivFloor(block, inst),319 .div_floor => try sema.zirDivFloor(block, inst),
321 .div_trunc => try sema.zirDivTrunc(block, inst),320 .div_trunc => try sema.zirDivTrunc(block, inst),
322 .shl_exact => try sema.zirShlExact(block, inst),
323 .shr_exact => try sema.zirShrExact(block, inst),321 .shr_exact => try sema.zirShrExact(block, inst),
324 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),322 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),
325 .offset_of => try sema.zirOffsetOf(block, inst),323 .offset_of => try sema.zirOffsetOf(block, inst),
...@@ -363,14 +361,21 @@ pub fn analyzeBody(...@@ -363,14 +361,21 @@ pub fn analyzeBody(
363361
364 .add => try sema.zirArithmetic(block, inst, .add),362 .add => try sema.zirArithmetic(block, inst, .add),
365 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),363 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
364 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
366 .div => try sema.zirArithmetic(block, inst, .div),365 .div => try sema.zirArithmetic(block, inst, .div),
367 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),366 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
368 .mod => try sema.zirArithmetic(block, inst, .mod),367 .mod => try sema.zirArithmetic(block, inst, .mod),
369 .rem => try sema.zirArithmetic(block, inst, .rem),368 .rem => try sema.zirArithmetic(block, inst, .rem),
370 .mul => try sema.zirArithmetic(block, inst, .mul),369 .mul => try sema.zirArithmetic(block, inst, .mul),
371 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),370 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
371 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
372 .sub => try sema.zirArithmetic(block, inst, .sub),372 .sub => try sema.zirArithmetic(block, inst, .sub),
373 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),373 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),
374 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
375
376 .shl => try sema.zirShl(block, inst, .shl),
377 .shl_exact => try sema.zirShl(block, inst, .shl_exact),
378 .shl_sat => try sema.zirShl(block, inst, .shl_sat),
374379
375 // Instructions that we know to *always* be noreturn based solely on their tag.380 // Instructions that we know to *always* be noreturn based solely on their tag.
376 // These functions match the return type of analyzeBody so that we can381 // These functions match the return type of analyzeBody so that we can
...@@ -694,11 +699,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -694,11 +699,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
694 .c_define => return sema.zirCDefine( block, extended),699 .c_define => return sema.zirCDefine( block, extended),
695 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),700 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
696 .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),701 .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),
697 .add_with_saturation,
698 .sub_with_saturation,
699 .mul_with_saturation,
700 .shl_with_saturation,
701 => return sema.zirSatArithmetic( block, extended),
702 // zig fmt: on702 // zig fmt: on
703 }703 }
704}704}
...@@ -5875,7 +5875,12 @@ fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co...@@ -5875,7 +5875,12 @@ fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
5875 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});5875 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});
5876}5876}
58775877
5878fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5878fn zirShl(
5879 sema: *Sema,
5880 block: *Scope.Block,
5881 inst: Zir.Inst.Index,
5882 air_tag: Air.Inst.Tag,
5883) CompileError!Air.Inst.Ref {
5879 const tracy = trace(@src());5884 const tracy = trace(@src());
5880 defer tracy.end();5885 defer tracy.end();
58815886
...@@ -5886,6 +5891,8 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A...@@ -5886,6 +5891,8 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
5886 const lhs = sema.resolveInst(extra.lhs);5891 const lhs = sema.resolveInst(extra.lhs);
5887 const rhs = sema.resolveInst(extra.rhs);5892 const rhs = sema.resolveInst(extra.rhs);
58885893
5894 // TODO coerce rhs if air_tag is not shl_sat
5895
5889 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);5896 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
5890 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);5897 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
58915898
...@@ -5901,6 +5908,12 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A...@@ -5901,6 +5908,12 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
5901 return sema.addConstant(lhs_ty, lhs_val);5908 return sema.addConstant(lhs_ty, lhs_val);
5902 }5909 }
5903 const val = try lhs_val.shl(rhs_val, sema.arena);5910 const val = try lhs_val.shl(rhs_val, sema.arena);
5911 switch (air_tag) {
5912 .shl_exact => return sema.mod.fail(&block.base, lhs_src, "TODO implement Sema for comptime shl_exact", .{}),
5913 .shl_sat => return sema.mod.fail(&block.base, lhs_src, "TODO implement Sema for comptime shl_sat", .{}),
5914 .shl => {},
5915 else => unreachable,
5916 }
5904 return sema.addConstant(lhs_ty, val);5917 return sema.addConstant(lhs_ty, val);
5905 } else rs: {5918 } else rs: {
5906 if (maybe_rhs_val) |rhs_val| {5919 if (maybe_rhs_val) |rhs_val| {
...@@ -5909,8 +5922,10 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A...@@ -5909,8 +5922,10 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
5909 break :rs lhs_src;5922 break :rs lhs_src;
5910 };5923 };
59115924
5925 // TODO: insert runtime safety check for shl_exact
5926
5912 try sema.requireRuntimeBlock(block, runtime_src);5927 try sema.requireRuntimeBlock(block, runtime_src);
5913 return block.addBinOp(.shl, lhs, rhs);5928 return block.addBinOp(air_tag, lhs, rhs);
5914}5929}
59155930
5916fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5931fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -6201,105 +6216,6 @@ fn zirOverflowArithmetic(...@@ -6201,105 +6216,6 @@ fn zirOverflowArithmetic(
6201 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});6216 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});
6202}6217}
62036218
6204fn zirSatArithmetic(
6205 sema: *Sema,
6206 block: *Scope.Block,
6207 extended: Zir.Inst.Extended.InstData,
6208) CompileError!Air.Inst.Ref {
6209 const tracy = trace(@src());
6210 defer tracy.end();
6211
6212 const extra = sema.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data;
6213 sema.src = .{ .node_offset_bin_op = extra.node };
6214 const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = extra.node };
6215 const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = extra.node };
6216 const lhs = sema.resolveInst(extra.lhs);
6217 const rhs = sema.resolveInst(extra.rhs);
6218
6219 return sema.analyzeSatArithmetic(block, lhs, rhs, sema.src, lhs_src, rhs_src, extended);
6220}
6221
6222fn analyzeSatArithmetic(
6223 sema: *Sema,
6224 block: *Scope.Block,
6225 lhs: Air.Inst.Ref,
6226 rhs: Air.Inst.Ref,
6227 src: LazySrcLoc,
6228 lhs_src: LazySrcLoc,
6229 rhs_src: LazySrcLoc,
6230 extended: Zir.Inst.Extended.InstData,
6231) CompileError!Air.Inst.Ref {
6232 const lhs_ty = sema.typeOf(lhs);
6233 const rhs_ty = sema.typeOf(rhs);
6234 const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
6235 const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
6236 if (lhs_zig_ty_tag == .Vector and rhs_zig_ty_tag == .Vector) {
6237 if (lhs_ty.arrayLen() != rhs_ty.arrayLen()) {
6238 return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{
6239 lhs_ty.arrayLen(), rhs_ty.arrayLen(),
6240 });
6241 }
6242 return sema.mod.fail(&block.base, src, "TODO implement support for vectors in zirBinOp", .{});
6243 } else if (lhs_zig_ty_tag == .Vector or rhs_zig_ty_tag == .Vector) {
6244 return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{
6245 lhs_ty, rhs_ty,
6246 });
6247 }
6248
6249 if (lhs_zig_ty_tag == .Pointer or rhs_zig_ty_tag == .Pointer)
6250 return sema.mod.fail(&block.base, src, "TODO implement support for pointers in zirSatArithmetic", .{});
6251
6252 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
6253 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]LazySrcLoc{ lhs_src, rhs_src } });
6254 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
6255 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
6256
6257 const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
6258 resolved_type.elemType()
6259 else
6260 resolved_type;
6261
6262 const scalar_tag = scalar_type.zigTypeTag();
6263
6264 const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
6265
6266 if (!is_int)
6267 return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
6268 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
6269 });
6270
6271 if (try sema.resolveMaybeUndefVal(block, lhs_src, casted_lhs)) |lhs_val| {
6272 if (try sema.resolveMaybeUndefVal(block, rhs_src, casted_rhs)) |rhs_val| {
6273 if (lhs_val.isUndef() or rhs_val.isUndef()) {
6274 return sema.addConstUndef(resolved_type);
6275 }
6276 // incase rhs is 0, simply return lhs without doing any calculations
6277 if (rhs_val.compareWithZero(.eq)) {
6278 switch (extended.opcode) {
6279 .add_with_saturation, .sub_with_saturation => return sema.addConstant(scalar_type, lhs_val),
6280 else => {},
6281 }
6282 }
6283
6284 return sema.mod.fail(&block.base, src, "TODO implement comptime saturating arithmetic for operand '{s}'", .{@tagName(extended.opcode)});
6285 } else {
6286 try sema.requireRuntimeBlock(block, rhs_src);
6287 }
6288 } else {
6289 try sema.requireRuntimeBlock(block, lhs_src);
6290 }
6291
6292 const air_tag: Air.Inst.Tag = switch (extended.opcode) {
6293 .add_with_saturation => .addsat,
6294 .sub_with_saturation => .subsat,
6295 .mul_with_saturation => .mulsat,
6296 .shl_with_saturation => .shl_sat,
6297 else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for extended opcode '{s}'", .{@tagName(extended.opcode)}),
6298 };
6299
6300 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
6301}
6302
6303fn analyzeArithmetic(6219fn analyzeArithmetic(
6304 sema: *Sema,6220 sema: *Sema,
6305 block: *Scope.Block,6221 block: *Scope.Block,
...@@ -6441,8 +6357,7 @@ fn analyzeArithmetic(...@@ -6441,8 +6357,7 @@ fn analyzeArithmetic(
6441 },6357 },
6442 .addwrap => {6358 .addwrap => {
6443 // Integers only; floats are checked above.6359 // Integers only; floats are checked above.
6444 // If either of the operands are zero, then the other operand is6360 // If either of the operands are zero, the other operand is returned.
6445 // returned, even if it is undefined.
6446 // If either of the operands are undefined, the result is undefined.6361 // If either of the operands are undefined, the result is undefined.
6447 if (maybe_lhs_val) |lhs_val| {6362 if (maybe_lhs_val) |lhs_val| {
6448 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {6363 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
...@@ -6464,6 +6379,30 @@ fn analyzeArithmetic(...@@ -6464,6 +6379,30 @@ fn analyzeArithmetic(
6464 } else break :rs .{ .src = lhs_src, .air_tag = .addwrap };6379 } else break :rs .{ .src = lhs_src, .air_tag = .addwrap };
6465 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };6380 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
6466 },6381 },
6382 .add_sat => {
6383 // For both integers and floats:
6384 // If either of the operands are zero, then the other operand is returned.
6385 // If either of the operands are undefined, the result is undefined.
6386 if (maybe_lhs_val) |lhs_val| {
6387 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
6388 return casted_rhs;
6389 }
6390 }
6391 if (maybe_rhs_val) |rhs_val| {
6392 if (rhs_val.isUndef()) {
6393 return sema.addConstUndef(scalar_type);
6394 }
6395 if (rhs_val.compareWithZero(.eq)) {
6396 return casted_lhs;
6397 }
6398 if (maybe_lhs_val) |lhs_val| {
6399 return sema.addConstant(
6400 scalar_type,
6401 try lhs_val.numberAddSat(rhs_val, scalar_type, sema.arena, target),
6402 );
6403 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
6404 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
6405 },
6467 .sub => {6406 .sub => {
6468 // For integers:6407 // For integers:
6469 // If the rhs is zero, then the other operand is6408 // If the rhs is zero, then the other operand is
...@@ -6531,6 +6470,30 @@ fn analyzeArithmetic(...@@ -6531,6 +6470,30 @@ fn analyzeArithmetic(
6531 } else break :rs .{ .src = rhs_src, .air_tag = .subwrap };6470 } else break :rs .{ .src = rhs_src, .air_tag = .subwrap };
6532 } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };6471 } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };
6533 },6472 },
6473 .sub_sat => {
6474 // For both integers and floats:
6475 // If the RHS is zero, result is LHS.
6476 // If either of the operands are undefined, result is undefined.
6477 if (maybe_rhs_val) |rhs_val| {
6478 if (rhs_val.isUndef()) {
6479 return sema.addConstUndef(scalar_type);
6480 }
6481 if (rhs_val.compareWithZero(.eq)) {
6482 return casted_lhs;
6483 }
6484 }
6485 if (maybe_lhs_val) |lhs_val| {
6486 if (lhs_val.isUndef()) {
6487 return sema.addConstUndef(scalar_type);
6488 }
6489 if (maybe_rhs_val) |rhs_val| {
6490 return sema.addConstant(
6491 scalar_type,
6492 try lhs_val.numberSubSat(rhs_val, scalar_type, sema.arena, target),
6493 );
6494 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
6495 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
6496 },
6534 .div => {6497 .div => {
6535 // For integers:6498 // For integers:
6536 // If the lhs is zero, then zero is returned regardless of rhs.6499 // If the lhs is zero, then zero is returned regardless of rhs.
...@@ -6649,10 +6612,9 @@ fn analyzeArithmetic(...@@ -6649,10 +6612,9 @@ fn analyzeArithmetic(
6649 },6612 },
6650 .mulwrap => {6613 .mulwrap => {
6651 // Integers only; floats are handled above.6614 // Integers only; floats are handled above.
6652 // If either of the operands are zero, the result is zero.6615 // If either of the operands are zero, result is zero.
6653 // If either of the operands are one, the result is the other6616 // If either of the operands are one, result is the other operand.
6654 // operand, even if it is undefined.6617 // If either of the operands are undefined, result is undefined.
6655 // If either of the operands are undefined, the result is undefined.
6656 if (maybe_lhs_val) |lhs_val| {6618 if (maybe_lhs_val) |lhs_val| {
6657 if (!lhs_val.isUndef()) {6619 if (!lhs_val.isUndef()) {
6658 if (lhs_val.compareWithZero(.eq)) {6620 if (lhs_val.compareWithZero(.eq)) {
...@@ -6684,6 +6646,42 @@ fn analyzeArithmetic(...@@ -6684,6 +6646,42 @@ fn analyzeArithmetic(
6684 } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap };6646 } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap };
6685 } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };6647 } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };
6686 },6648 },
6649 .mul_sat => {
6650 // For both integers and floats:
6651 // If either of the operands are zero, result is zero.
6652 // If either of the operands are one, result is the other operand.
6653 // If either of the operands are undefined, result is undefined.
6654 if (maybe_lhs_val) |lhs_val| {
6655 if (!lhs_val.isUndef()) {
6656 if (lhs_val.compareWithZero(.eq)) {
6657 return sema.addConstant(scalar_type, Value.zero);
6658 }
6659 if (lhs_val.compare(.eq, Value.one, scalar_type)) {
6660 return casted_rhs;
6661 }
6662 }
6663 }
6664 if (maybe_rhs_val) |rhs_val| {
6665 if (rhs_val.isUndef()) {
6666 return sema.addConstUndef(scalar_type);
6667 }
6668 if (rhs_val.compareWithZero(.eq)) {
6669 return sema.addConstant(scalar_type, Value.zero);
6670 }
6671 if (rhs_val.compare(.eq, Value.one, scalar_type)) {
6672 return casted_lhs;
6673 }
6674 if (maybe_lhs_val) |lhs_val| {
6675 if (lhs_val.isUndef()) {
6676 return sema.addConstUndef(scalar_type);
6677 }
6678 return sema.addConstant(
6679 scalar_type,
6680 try lhs_val.numberMulSat(rhs_val, scalar_type, sema.arena, target),
6681 );
6682 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
6683 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };
6684 },
6687 .mod_rem => {6685 .mod_rem => {
6688 // For integers:6686 // For integers:
6689 // Either operand being undef is a compile error because there exists6687 // Either operand being undef is a compile error because there exists
...@@ -7933,7 +7931,7 @@ fn analyzeRet(...@@ -7933,7 +7931,7 @@ fn analyzeRet(
7933fn floatOpAllowed(tag: Zir.Inst.Tag) bool {7931fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
7934 // extend this swich as additional operators are implemented7932 // extend this swich as additional operators are implemented
7935 return switch (tag) {7933 return switch (tag) {
7936 .add, .sub, .mul, .div, .mod, .rem, .mod_rem => true,7934 .add, .add_sat, .sub, .sub_sat, .mul, .mul_sat, .div, .mod, .rem, .mod_rem => true,
7937 else => false,7935 else => false,
7938 };7936 };
7939}7937}
...@@ -8600,12 +8598,6 @@ fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -8600,12 +8598,6 @@ fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
8600 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});8598 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});
8601}8599}
86028600
8603fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8604 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
8605 const src = inst_data.src();
8606 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShlExact", .{});
8607}
8608
8609fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8601fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8610 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;8602 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
8611 const src = inst_data.src();8603 const src = inst_data.src();
src/Zir.zig+71-72
...@@ -126,6 +126,64 @@ pub const Inst = struct {...@@ -126,6 +126,64 @@ pub const Inst = struct {
126 /// Twos complement wrapping integer addition.126 /// Twos complement wrapping integer addition.
127 /// Uses the `pl_node` union field. Payload is `Bin`.127 /// Uses the `pl_node` union field. Payload is `Bin`.
128 addwrap,128 addwrap,
129 /// Saturating addition.
130 /// Uses the `pl_node` union field. Payload is `Bin`.
131 add_sat,
132 /// Arithmetic subtraction. Asserts no integer overflow.
133 /// Uses the `pl_node` union field. Payload is `Bin`.
134 sub,
135 /// Twos complement wrapping integer subtraction.
136 /// Uses the `pl_node` union field. Payload is `Bin`.
137 subwrap,
138 /// Saturating subtraction.
139 /// Uses the `pl_node` union field. Payload is `Bin`.
140 sub_sat,
141 /// Arithmetic multiplication. Asserts no integer overflow.
142 /// Uses the `pl_node` union field. Payload is `Bin`.
143 mul,
144 /// Twos complement wrapping integer multiplication.
145 /// Uses the `pl_node` union field. Payload is `Bin`.
146 mulwrap,
147 /// Saturating multiplication.
148 /// Uses the `pl_node` union field. Payload is `Bin`.
149 mul_sat,
150 /// Implements the `@divExact` builtin.
151 /// Uses the `pl_node` union field with payload `Bin`.
152 div_exact,
153 /// Implements the `@divFloor` builtin.
154 /// Uses the `pl_node` union field with payload `Bin`.
155 div_floor,
156 /// Implements the `@divTrunc` builtin.
157 /// Uses the `pl_node` union field with payload `Bin`.
158 div_trunc,
159 /// Implements the `@mod` builtin.
160 /// Uses the `pl_node` union field with payload `Bin`.
161 mod,
162 /// Implements the `@rem` builtin.
163 /// Uses the `pl_node` union field with payload `Bin`.
164 rem,
165 /// Ambiguously remainder division or modulus. If the computation would possibly have
166 /// a different value depending on whether the operation is remainder division or modulus,
167 /// a compile error is emitted. Otherwise the computation is performed.
168 /// Uses the `pl_node` union field. Payload is `Bin`.
169 mod_rem,
170 /// Integer shift-left. Zeroes are shifted in from the right hand side.
171 /// Uses the `pl_node` union field. Payload is `Bin`.
172 shl,
173 /// Implements the `@shlExact` builtin.
174 /// Uses the `pl_node` union field with payload `Bin`.
175 shl_exact,
176 /// Saturating shift-left.
177 /// Uses the `pl_node` union field. Payload is `Bin`.
178 shl_sat,
179 /// Integer shift-right. Arithmetic or logical depending on the signedness of
180 /// the integer type.
181 /// Uses the `pl_node` union field. Payload is `Bin`.
182 shr,
183 /// Implements the `@shrExact` builtin.
184 /// Uses the `pl_node` union field with payload `Bin`.
185 shr_exact,
186
129 /// Declares a parameter of the current function. Used for:187 /// Declares a parameter of the current function. Used for:
130 /// * debug info188 /// * debug info
131 /// * checking shadowing against declarations in the current namespace189 /// * checking shadowing against declarations in the current namespace
...@@ -471,12 +529,6 @@ pub const Inst = struct {...@@ -471,12 +529,6 @@ pub const Inst = struct {
471 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.529 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
472 /// Uses the `str` union field.530 /// Uses the `str` union field.
473 str,531 str,
474 /// Arithmetic subtraction. Asserts no integer overflow.
475 /// Uses the `pl_node` union field. Payload is `Bin`.
476 sub,
477 /// Twos complement wrapping integer subtraction.
478 /// Uses the `pl_node` union field. Payload is `Bin`.
479 subwrap,
480 /// Arithmetic negation. Asserts no integer overflow.532 /// Arithmetic negation. Asserts no integer overflow.
481 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.533 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
482 /// Uses `un_node`.534 /// Uses `un_node`.
...@@ -802,46 +854,6 @@ pub const Inst = struct {...@@ -802,46 +854,6 @@ pub const Inst = struct {
802 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.854 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.
803 bit_reverse,855 bit_reverse,
804856
805 /// Implements the `@divExact` builtin.
806 /// Uses the `pl_node` union field with payload `Bin`.
807 div_exact,
808 /// Implements the `@divFloor` builtin.
809 /// Uses the `pl_node` union field with payload `Bin`.
810 div_floor,
811 /// Implements the `@divTrunc` builtin.
812 /// Uses the `pl_node` union field with payload `Bin`.
813 div_trunc,
814 /// Implements the `@mod` builtin.
815 /// Uses the `pl_node` union field with payload `Bin`.
816 mod,
817 /// Implements the `@rem` builtin.
818 /// Uses the `pl_node` union field with payload `Bin`.
819 rem,
820 /// Ambiguously remainder division or modulus. If the computation would possibly have
821 /// a different value depending on whether the operation is remainder division or modulus,
822 /// a compile error is emitted. Otherwise the computation is performed.
823 /// Uses the `pl_node` union field. Payload is `Bin`.
824 mod_rem,
825 /// Arithmetic multiplication. Asserts no integer overflow.
826 /// Uses the `pl_node` union field. Payload is `Bin`.
827 mul,
828 /// Twos complement wrapping integer multiplication.
829 /// Uses the `pl_node` union field. Payload is `Bin`.
830 mulwrap,
831
832 /// Integer shift-left. Zeroes are shifted in from the right hand side.
833 /// Uses the `pl_node` union field. Payload is `Bin`.
834 shl,
835 /// Implements the `@shlExact` builtin.
836 /// Uses the `pl_node` union field with payload `Bin`.
837 shl_exact,
838 /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
839 /// Uses the `pl_node` union field. Payload is `Bin`.
840 shr,
841 /// Implements the `@shrExact` builtin.
842 /// Uses the `pl_node` union field with payload `Bin`.
843 shr_exact,
844
845 /// Implements the `@bitOffsetOf` builtin.857 /// Implements the `@bitOffsetOf` builtin.
846 /// Uses the `pl_node` union field with payload `Bin`.858 /// Uses the `pl_node` union field with payload `Bin`.
847 bit_offset_of,859 bit_offset_of,
...@@ -961,6 +973,7 @@ pub const Inst = struct {...@@ -961,6 +973,7 @@ pub const Inst = struct {
961 .param_anytype_comptime,973 .param_anytype_comptime,
962 .add,974 .add,
963 .addwrap,975 .addwrap,
976 .add_sat,
964 .alloc,977 .alloc,
965 .alloc_mut,978 .alloc_mut,
966 .alloc_comptime,979 .alloc_comptime,
...@@ -1035,8 +1048,10 @@ pub const Inst = struct {...@@ -1035,8 +1048,10 @@ pub const Inst = struct {
1035 .mod_rem,1048 .mod_rem,
1036 .mul,1049 .mul,
1037 .mulwrap,1050 .mulwrap,
1051 .mul_sat,
1038 .ref,1052 .ref,
1039 .shl,1053 .shl,
1054 .shl_sat,
1040 .shr,1055 .shr,
1041 .store,1056 .store,
1042 .store_node,1057 .store_node,
...@@ -1045,6 +1060,7 @@ pub const Inst = struct {...@@ -1045,6 +1060,7 @@ pub const Inst = struct {
1045 .str,1060 .str,
1046 .sub,1061 .sub,
1047 .subwrap,1062 .subwrap,
1063 .sub_sat,
1048 .negate,1064 .negate,
1049 .negate_wrap,1065 .negate_wrap,
1050 .typeof,1066 .typeof,
...@@ -1218,6 +1234,14 @@ pub const Inst = struct {...@@ -1218,6 +1234,14 @@ pub const Inst = struct {
1218 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{1234 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
1219 .add = .pl_node,1235 .add = .pl_node,
1220 .addwrap = .pl_node,1236 .addwrap = .pl_node,
1237 .add_sat = .pl_node,
1238 .sub = .pl_node,
1239 .subwrap = .pl_node,
1240 .sub_sat = .pl_node,
1241 .mul = .pl_node,
1242 .mulwrap = .pl_node,
1243 .mul_sat = .pl_node,
1244
1221 .param = .pl_tok,1245 .param = .pl_tok,
1222 .param_comptime = .pl_tok,1246 .param_comptime = .pl_tok,
1223 .param_anytype = .str_tok,1247 .param_anytype = .str_tok,
...@@ -1297,8 +1321,6 @@ pub const Inst = struct {...@@ -1297,8 +1321,6 @@ pub const Inst = struct {
1297 .repeat_inline = .node,1321 .repeat_inline = .node,
1298 .merge_error_sets = .pl_node,1322 .merge_error_sets = .pl_node,
1299 .mod_rem = .pl_node,1323 .mod_rem = .pl_node,
1300 .mul = .pl_node,
1301 .mulwrap = .pl_node,
1302 .ref = .un_tok,1324 .ref = .un_tok,
1303 .ret_node = .un_node,1325 .ret_node = .un_node,
1304 .ret_load = .un_node,1326 .ret_load = .un_node,
...@@ -1315,8 +1337,6 @@ pub const Inst = struct {...@@ -1315,8 +1337,6 @@ pub const Inst = struct {
1315 .store_to_block_ptr = .bin,1337 .store_to_block_ptr = .bin,
1316 .store_to_inferred_ptr = .bin,1338 .store_to_inferred_ptr = .bin,
1317 .str = .str,1339 .str = .str,
1318 .sub = .pl_node,
1319 .subwrap = .pl_node,
1320 .negate = .un_node,1340 .negate = .un_node,
1321 .negate_wrap = .un_node,1341 .negate_wrap = .un_node,
1322 .typeof = .un_node,1342 .typeof = .un_node,
...@@ -1437,6 +1457,7 @@ pub const Inst = struct {...@@ -1437,6 +1457,7 @@ pub const Inst = struct {
14371457
1438 .shl = .pl_node,1458 .shl = .pl_node,
1439 .shl_exact = .pl_node,1459 .shl_exact = .pl_node,
1460 .shl_sat = .pl_node,
1440 .shr = .pl_node,1461 .shr = .pl_node,
1441 .shr_exact = .pl_node,1462 .shr_exact = .pl_node,
14421463
...@@ -1593,22 +1614,6 @@ pub const Inst = struct {...@@ -1593,22 +1614,6 @@ pub const Inst = struct {
1593 wasm_memory_size,1614 wasm_memory_size,
1594 /// `operand` is payload index to `BinNode`.1615 /// `operand` is payload index to `BinNode`.
1595 wasm_memory_grow,1616 wasm_memory_grow,
1596 /// Implements the `@addWithSaturation` builtin.
1597 /// `operand` is payload index to `SaturatingArithmetic`.
1598 /// `small` is unused.
1599 add_with_saturation,
1600 /// Implements the `@subWithSaturation` builtin.
1601 /// `operand` is payload index to `SaturatingArithmetic`.
1602 /// `small` is unused.
1603 sub_with_saturation,
1604 /// Implements the `@mulWithSaturation` builtin.
1605 /// `operand` is payload index to `SaturatingArithmetic`.
1606 /// `small` is unused.
1607 mul_with_saturation,
1608 /// Implements the `@shlWithSaturation` builtin.
1609 /// `operand` is payload index to `SaturatingArithmetic`.
1610 /// `small` is unused.
1611 shl_with_saturation,
16121617
1613 pub const InstData = struct {1618 pub const InstData = struct {
1614 opcode: Extended,1619 opcode: Extended,
...@@ -2788,12 +2793,6 @@ pub const Inst = struct {...@@ -2788,12 +2793,6 @@ pub const Inst = struct {
2788 ptr: Ref,2793 ptr: Ref,
2789 };2794 };
27902795
2791 pub const SaturatingArithmetic = struct {
2792 node: i32,
2793 lhs: Ref,
2794 rhs: Ref,
2795 };
2796
2797 pub const Cmpxchg = struct {2796 pub const Cmpxchg = struct {
2798 ptr: Ref,2797 ptr: Ref,
2799 expected_value: Ref,2798 expected_value: Ref,
src/codegen.zig+40-16
...@@ -824,18 +824,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -824,18 +824,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
824824
825 switch (air_tags[inst]) {825 switch (air_tags[inst]) {
826 // zig fmt: off826 // zig fmt: off
827 .add, .ptr_add => try self.airAdd(inst),827 .add, .ptr_add => try self.airAdd(inst),
828 .addwrap => try self.airAddWrap(inst),828 .addwrap => try self.airAddWrap(inst),
829 .addsat => try self.airArithmeticOpSat(inst, "addsat"),829 .add_sat => try self.airAddSat(inst),
830 .sub, .ptr_sub => try self.airSub(inst),830 .sub, .ptr_sub => try self.airSub(inst),
831 .subwrap => try self.airSubWrap(inst),831 .subwrap => try self.airSubWrap(inst),
832 .subsat => try self.airArithmeticOpSat(inst, "subsat"),832 .sub_sat => try self.airSubSat(inst),
833 .mul => try self.airMul(inst),833 .mul => try self.airMul(inst),
834 .mulwrap => try self.airMulWrap(inst),834 .mulwrap => try self.airMulWrap(inst),
835 .mulsat => try self.airArithmeticOpSat(inst, "mulsat"),835 .mul_sat => try self.airMulSat(inst),
836 .div => try self.airDiv(inst),836 .div => try self.airDiv(inst),
837 .rem => try self.airRem(inst),837 .rem => try self.airRem(inst),
838 .mod => try self.airMod(inst),838 .mod => try self.airMod(inst),
839 .shl, .shl_exact => try self.airShl(inst),
840 .shl_sat => try self.airShlSat(inst),
839841
840 .cmp_lt => try self.airCmp(inst, .lt),842 .cmp_lt => try self.airCmp(inst, .lt),
841 .cmp_lte => try self.airCmp(inst, .lte),843 .cmp_lte => try self.airCmp(inst, .lte),
...@@ -850,8 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -850,8 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
850 .bit_or => try self.airBitOr(inst),852 .bit_or => try self.airBitOr(inst),
851 .xor => try self.airXor(inst),853 .xor => try self.airXor(inst),
852 .shr => try self.airShr(inst),854 .shr => try self.airShr(inst),
853 .shl => try self.airShl(inst),
854 .shl_sat => try self.airArithmeticOpSat(inst, "shl_sat"),
855855
856 .alloc => try self.airAlloc(inst),856 .alloc => try self.airAlloc(inst),
857 .arg => try self.airArg(inst),857 .arg => try self.airArg(inst),
...@@ -1306,6 +1306,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1306,6 +1306,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1306 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1306 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1307 }1307 }
13081308
1309 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
1310 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1311 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1312 else => return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}),
1313 };
1314 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1315 }
1316
1309 fn airSub(self: *Self, inst: Air.Inst.Index) !void {1317 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
1310 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1318 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1311 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1319 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
...@@ -1324,10 +1332,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1324,10 +1332,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1324 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1332 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1325 }1333 }
13261334
1327 fn airArithmeticOpSat(self: *Self, inst: Air.Inst.Index, comptime name: []const u8) !void {1335 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
1328 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1336 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1329 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1337 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1330 else => return self.fail("TODO implement " ++ name ++ " for {}", .{self.target.cpu.arch}),1338 else => return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}),
1331 };1339 };
1332 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1340 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1333 }1341 }
...@@ -1350,6 +1358,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1350,6 +1358,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1350 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1358 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1351 }1359 }
13521360
1361 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
1362 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1363 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1364 else => return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}),
1365 };
1366 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1367 }
1368
1353 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1369 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1354 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1370 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1355 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1371 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
...@@ -1412,6 +1428,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1412,6 +1428,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1412 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1428 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1413 }1429 }
14141430
1431 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
1432 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1433 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1434 else => return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}),
1435 };
1436 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1437 }
1438
1415 fn airShr(self: *Self, inst: Air.Inst.Index) !void {1439 fn airShr(self: *Self, inst: Air.Inst.Index) !void {
1416 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1440 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1417 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1441 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
src/codegen/c.zig+35-41
...@@ -883,25 +883,27 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -883,25 +883,27 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
883883
884 // TODO use a different strategy for add that communicates to the optimizer884 // TODO use a different strategy for add that communicates to the optimizer
885 // that wrapping is UB.885 // that wrapping is UB.
886 .add, .ptr_add => try airBinOp( f, inst, " + "),886 .add, .ptr_add => try airBinOp (f, inst, " + "),
887 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
888 .addsat => return f.fail("TODO: C backend: implement codegen for addsat", .{}),
889 // TODO use a different strategy for sub that communicates to the optimizer887 // TODO use a different strategy for sub that communicates to the optimizer
890 // that wrapping is UB.888 // that wrapping is UB.
891 .sub, .ptr_sub => try airBinOp( f, inst, " - "),889 .sub, .ptr_sub => try airBinOp (f, inst, " - "),
892 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
893 .subsat => return f.fail("TODO: C backend: implement codegen for subsat", .{}),
894 // TODO use a different strategy for mul that communicates to the optimizer890 // TODO use a different strategy for mul that communicates to the optimizer
895 // that wrapping is UB.891 // that wrapping is UB.
896 .mul => try airBinOp( f, inst, " * "),892 .mul => try airBinOp (f, inst, " * "),
897 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
898 .mulsat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}),
899 // TODO use a different strategy for div that communicates to the optimizer893 // TODO use a different strategy for div that communicates to the optimizer
900 // that wrapping is UB.894 // that wrapping is UB.
901 .div => try airBinOp( f, inst, " / "),895 .div => try airBinOp( f, inst, " / "),
902 .rem => try airBinOp( f, inst, " % "),896 .rem => try airBinOp( f, inst, " % "),
903 // TODO implement modulus division897 .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
904 .mod => try airBinOp( f, inst, " mod "),898
899 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
900 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
901 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
902
903 .add_sat => try airSatOp(f, inst, "adds_"),
904 .sub_sat => try airSatOp(f, inst, "subs_"),
905 .mul_sat => try airSatOp(f, inst, "muls_"),
906 .shl_sat => try airSatOp(f, inst, "shls_"),
905907
906 .cmp_eq => try airBinOp(f, inst, " == "),908 .cmp_eq => try airBinOp(f, inst, " == "),
907 .cmp_gt => try airBinOp(f, inst, " > "),909 .cmp_gt => try airBinOp(f, inst, " > "),
...@@ -911,18 +913,14 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -911,18 +913,14 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
911 .cmp_neq => try airBinOp(f, inst, " != "),913 .cmp_neq => try airBinOp(f, inst, " != "),
912914
913 // bool_and and bool_or are non-short-circuit operations915 // bool_and and bool_or are non-short-circuit operations
914 .bool_and => try airBinOp(f, inst, " & "),916 .bool_and => try airBinOp(f, inst, " & "),
915 .bool_or => try airBinOp(f, inst, " | "),917 .bool_or => try airBinOp(f, inst, " | "),
916 .bit_and => try airBinOp(f, inst, " & "),918 .bit_and => try airBinOp(f, inst, " & "),
917 .bit_or => try airBinOp(f, inst, " | "),919 .bit_or => try airBinOp(f, inst, " | "),
918 .xor => try airBinOp(f, inst, " ^ "),920 .xor => try airBinOp(f, inst, " ^ "),
919921 .shr => try airBinOp(f, inst, " >> "),
920 .shr => try airBinOp(f, inst, " >> "),922 .shl, .shl_exact => try airBinOp(f, inst, " << "),
921 .shl => try airBinOp(f, inst, " << "),923 .not => try airNot (f, inst),
922 .shl_sat => return f.fail("TODO: C backend: implement codegen for mulsat", .{}),
923
924
925 .not => try airNot( f, inst),
926924
927 .optional_payload => try airOptionalPayload(f, inst),925 .optional_payload => try airOptionalPayload(f, inst),
928 .optional_payload_ptr => try airOptionalPayload(f, inst),926 .optional_payload_ptr => try airOptionalPayload(f, inst),
...@@ -1314,27 +1312,23 @@ fn airWrapOp(...@@ -1314,27 +1312,23 @@ fn airWrapOp(
1314 return ret;1312 return ret;
1315}1313}
13161314
1317fn airSatOp(1315fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
1318 o: *Object,1316 if (f.liveness.isUnused(inst))
1319 inst: Air.Inst.Index,
1320 fn_op: [*:0]const u8,
1321) !CValue {
1322 if (o.liveness.isUnused(inst))
1323 return CValue.none;1317 return CValue.none;
13241318
1325 const bin_op = o.air.instructions.items(.data)[inst].bin_op;1319 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1326 const inst_ty = o.air.typeOfIndex(inst);1320 const inst_ty = f.air.typeOfIndex(inst);
1327 const int_info = inst_ty.intInfo(o.dg.module.getTarget());1321 const int_info = inst_ty.intInfo(f.object.dg.module.getTarget());
1328 const bits = int_info.bits;1322 const bits = int_info.bits;
13291323
1330 switch (bits) {1324 switch (bits) {
1331 8, 16, 32, 64, 128 => {},1325 8, 16, 32, 64, 128 => {},
1332 else => return o.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}),1326 else => return f.object.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}),
1333 }1327 }
13341328
1335 // if it's an unsigned int with non-arbitrary bit size then we can just add1329 // if it's an unsigned int with non-arbitrary bit size then we can just add
1336 if (bits > 64) {1330 if (bits > 64) {
1337 return o.dg.fail("TODO: C backend: airSatOp for large integers", .{});1331 return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{});
1338 }1332 }
13391333
1340 var min_buf: [80]u8 = undefined;1334 var min_buf: [80]u8 = undefined;
...@@ -1382,11 +1376,11 @@ fn airSatOp(...@@ -1382,11 +1376,11 @@ fn airSatOp(
1382 },1376 },
1383 };1377 };
13841378
1385 const lhs = try o.resolveInst(bin_op.lhs);1379 const lhs = try f.resolveInst(bin_op.lhs);
1386 const rhs = try o.resolveInst(bin_op.rhs);1380 const rhs = try f.resolveInst(bin_op.rhs);
1387 const w = o.writer();1381 const w = f.object.writer();
13881382
1389 const ret = try o.allocLocal(inst_ty, .Mut);1383 const ret = try f.allocLocal(inst_ty, .Mut);
1390 try w.print(" = zig_{s}", .{fn_op});1384 try w.print(" = zig_{s}", .{fn_op});
13911385
1392 switch (inst_ty.tag()) {1386 switch (inst_ty.tag()) {
...@@ -1412,16 +1406,16 @@ fn airSatOp(...@@ -1412,16 +1406,16 @@ fn airSatOp(
1412 }1406 }
14131407
1414 try w.writeByte('(');1408 try w.writeByte('(');
1415 try o.writeCValue(w, lhs);1409 try f.writeCValue(w, lhs);
1416 try w.writeAll(", ");1410 try w.writeAll(", ");
1417 try o.writeCValue(w, rhs);1411 try f.writeCValue(w, rhs);
14181412
1419 if (int_info.signedness == .signed) {1413 if (int_info.signedness == .signed) {
1420 try w.print(", {s}", .{min});1414 try w.print(", {s}", .{min});
1421 }1415 }
14221416
1423 try w.print(", {s});", .{max});1417 try w.print(", {s});", .{max});
1424 try o.indent_writer.insertNewline();1418 try f.object.indent_writer.insertNewline();
14251419
1426 return ret;1420 return ret;
1427}1421}
src/codegen/llvm.zig+128-60
...@@ -1236,27 +1236,27 @@ pub const FuncGen = struct {...@@ -1236,27 +1236,27 @@ pub const FuncGen = struct {
1236 for (body) |inst| {1236 for (body) |inst| {
1237 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {1237 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
1238 // zig fmt: off1238 // zig fmt: off
1239 .add => try self.airAdd(inst, .standard),1239 .add => try self.airAdd(inst),
1240 .addwrap => try self.airAdd(inst, .wrapping),1240 .addwrap => try self.airAddWrap(inst),
1241 .addsat => try self.airAdd(inst, .saturated),1241 .add_sat => try self.airAddSat(inst),
1242 .sub => try self.airSub(inst, .standard),1242 .sub => try self.airSub(inst),
1243 .subwrap => try self.airSub(inst, .wrapping),1243 .subwrap => try self.airSubWrap(inst),
1244 .subsat => try self.airSub(inst, .saturated),1244 .sub_sat => try self.airSubSat(inst),
1245 .mul => try self.airMul(inst, .standard),1245 .mul => try self.airMul(inst),
1246 .mulwrap => try self.airMul(inst, .wrapping),1246 .mulwrap => try self.airMulWrap(inst),
1247 .mulsat => try self.airMul(inst, .saturated),1247 .mul_sat => try self.airMulSat(inst),
1248 .div => try self.airDiv(inst),1248 .div => try self.airDiv(inst),
1249 .rem => try self.airRem(inst),1249 .rem => try self.airRem(inst),
1250 .mod => try self.airMod(inst),1250 .mod => try self.airMod(inst),
1251 .ptr_add => try self.airPtrAdd(inst),1251 .ptr_add => try self.airPtrAdd(inst),
1252 .ptr_sub => try self.airPtrSub(inst),1252 .ptr_sub => try self.airPtrSub(inst),
1253 .shl => try self.airShl(inst),
1254 .shl_sat => try self.airShlSat(inst),
1255 .shl_exact => try self.airShlExact(inst),
12531256
1254 .bit_and, .bool_and => try self.airAnd(inst),1257 .bit_and, .bool_and => try self.airAnd(inst),
1255 .bit_or, .bool_or => try self.airOr(inst),1258 .bit_or, .bool_or => try self.airOr(inst),
1256 .xor => try self.airXor(inst),1259 .xor => try self.airXor(inst),
1257
1258 .shl => try self.airShl(inst, false),
1259 .shl_sat => try self.airShl(inst, true),
1260 .shr => try self.airShr(inst),1260 .shr => try self.airShr(inst),
12611261
1262 .cmp_eq => try self.airCmp(inst, .eq),1262 .cmp_eq => try self.airCmp(inst, .eq),
...@@ -2028,10 +2028,8 @@ pub const FuncGen = struct {...@@ -2028,10 +2028,8 @@ pub const FuncGen = struct {
2028 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});2028 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
2029 }2029 }
20302030
2031 const ArithmeticType = enum { standard, wrapping, saturated };2031 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2032 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {2032 if (self.liveness.isUnused(inst)) return null;
2033 if (self.liveness.isUnused(inst))
2034 return null;
20352033
2036 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2034 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2037 const lhs = try self.resolveInst(bin_op.lhs);2035 const lhs = try self.resolveInst(bin_op.lhs);
...@@ -2039,21 +2037,36 @@ pub const FuncGen = struct {...@@ -2039,21 +2037,36 @@ pub const FuncGen = struct {
2039 const inst_ty = self.air.typeOfIndex(inst);2037 const inst_ty = self.air.typeOfIndex(inst);
20402038
2041 if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");2039 if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
2042 if (ty == .wrapping)
2043 return self.builder.buildAdd(lhs, rhs, "")
2044 else if (ty == .saturated) {
2045 if (inst_ty.isSignedInt())
2046 return self.builder.buildSAddSat(lhs, rhs, "")
2047 else
2048 return self.builder.buildUAddSat(lhs, rhs, "");
2049 }
2050 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");2040 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
2051 return self.builder.buildNUWAdd(lhs, rhs, "");2041 return self.builder.buildNUWAdd(lhs, rhs, "");
2052 }2042 }
20532043
2054 fn airSub(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {2044 fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2055 if (self.liveness.isUnused(inst))2045 if (self.liveness.isUnused(inst)) return null;
2056 return null;2046
2047 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2048 const lhs = try self.resolveInst(bin_op.lhs);
2049 const rhs = try self.resolveInst(bin_op.rhs);
2050
2051 return self.builder.buildAdd(lhs, rhs, "");
2052 }
2053
2054 fn airAddSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2055 if (self.liveness.isUnused(inst)) return null;
2056
2057 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2058 const lhs = try self.resolveInst(bin_op.lhs);
2059 const rhs = try self.resolveInst(bin_op.rhs);
2060 const inst_ty = self.air.typeOfIndex(inst);
2061
2062 if (inst_ty.isAnyFloat()) return self.todo("saturating float add", .{});
2063 if (inst_ty.isSignedInt()) return self.builder.buildSAddSat(lhs, rhs, "");
2064
2065 return self.builder.buildUAddSat(lhs, rhs, "");
2066 }
2067
2068 fn airSub(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2069 if (self.liveness.isUnused(inst)) return null;
20572070
2058 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2071 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2059 const lhs = try self.resolveInst(bin_op.lhs);2072 const lhs = try self.resolveInst(bin_op.lhs);
...@@ -2061,21 +2074,35 @@ pub const FuncGen = struct {...@@ -2061,21 +2074,35 @@ pub const FuncGen = struct {
2061 const inst_ty = self.air.typeOfIndex(inst);2074 const inst_ty = self.air.typeOfIndex(inst);
20622075
2063 if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");2076 if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
2064 if (ty == .wrapping)
2065 return self.builder.buildSub(lhs, rhs, "")
2066 else if (ty == .saturated) {
2067 if (inst_ty.isSignedInt())
2068 return self.builder.buildSSubSat(lhs, rhs, "")
2069 else
2070 return self.builder.buildUSubSat(lhs, rhs, "");
2071 }
2072 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");2077 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
2073 return self.builder.buildNUWSub(lhs, rhs, "");2078 return self.builder.buildNUWSub(lhs, rhs, "");
2074 }2079 }
20752080
2076 fn airMul(self: *FuncGen, inst: Air.Inst.Index, ty: ArithmeticType) !?*const llvm.Value {2081 fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2077 if (self.liveness.isUnused(inst))2082 if (self.liveness.isUnused(inst)) return null;
2078 return null;2083
2084 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2085 const lhs = try self.resolveInst(bin_op.lhs);
2086 const rhs = try self.resolveInst(bin_op.rhs);
2087
2088 return self.builder.buildSub(lhs, rhs, "");
2089 }
2090
2091 fn airSubSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2092 if (self.liveness.isUnused(inst)) return null;
2093
2094 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2095 const lhs = try self.resolveInst(bin_op.lhs);
2096 const rhs = try self.resolveInst(bin_op.rhs);
2097 const inst_ty = self.air.typeOfIndex(inst);
2098
2099 if (inst_ty.isAnyFloat()) return self.todo("saturating float sub", .{});
2100 if (inst_ty.isSignedInt()) return self.builder.buildSSubSat(lhs, rhs, "");
2101 return self.builder.buildUSubSat(lhs, rhs, "");
2102 }
2103
2104 fn airMul(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2105 if (self.liveness.isUnused(inst)) return null;
20792106
2080 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2107 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2081 const lhs = try self.resolveInst(bin_op.lhs);2108 const lhs = try self.resolveInst(bin_op.lhs);
...@@ -2083,18 +2110,33 @@ pub const FuncGen = struct {...@@ -2083,18 +2110,33 @@ pub const FuncGen = struct {
2083 const inst_ty = self.air.typeOfIndex(inst);2110 const inst_ty = self.air.typeOfIndex(inst);
20842111
2085 if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, "");2112 if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, "");
2086 if (ty == .wrapping)
2087 return self.builder.buildMul(lhs, rhs, "")
2088 else if (ty == .saturated) {
2089 if (inst_ty.isSignedInt())
2090 return self.builder.buildSMulFixSat(lhs, rhs, "")
2091 else
2092 return self.builder.buildUMulFixSat(lhs, rhs, "");
2093 }
2094 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");2113 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
2095 return self.builder.buildNUWMul(lhs, rhs, "");2114 return self.builder.buildNUWMul(lhs, rhs, "");
2096 }2115 }
20972116
2117 fn airMulWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2118 if (self.liveness.isUnused(inst)) return null;
2119
2120 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2121 const lhs = try self.resolveInst(bin_op.lhs);
2122 const rhs = try self.resolveInst(bin_op.rhs);
2123
2124 return self.builder.buildMul(lhs, rhs, "");
2125 }
2126
2127 fn airMulSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2128 if (self.liveness.isUnused(inst)) return null;
2129
2130 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2131 const lhs = try self.resolveInst(bin_op.lhs);
2132 const rhs = try self.resolveInst(bin_op.rhs);
2133 const inst_ty = self.air.typeOfIndex(inst);
2134
2135 if (inst_ty.isAnyFloat()) return self.todo("saturating float mul", .{});
2136 if (inst_ty.isSignedInt()) return self.builder.buildSMulFixSat(lhs, rhs, "");
2137 return self.builder.buildUMulFixSat(lhs, rhs, "");
2138 }
2139
2098 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2140 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2099 if (self.liveness.isUnused(inst))2141 if (self.liveness.isUnused(inst))
2100 return null;2142 return null;
...@@ -2200,9 +2242,25 @@ pub const FuncGen = struct {...@@ -2200,9 +2242,25 @@ pub const FuncGen = struct {
2200 return self.builder.buildXor(lhs, rhs, "");2242 return self.builder.buildXor(lhs, rhs, "");
2201 }2243 }
22022244
2203 fn airShl(self: *FuncGen, inst: Air.Inst.Index, sat: bool) !?*const llvm.Value {2245 fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2204 if (self.liveness.isUnused(inst))2246 if (self.liveness.isUnused(inst)) return null;
2205 return null;2247
2248 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2249 const lhs = try self.resolveInst(bin_op.lhs);
2250 const rhs = try self.resolveInst(bin_op.rhs);
2251 const lhs_type = self.air.typeOf(bin_op.lhs);
2252 const tg = self.dg.module.getTarget();
2253 const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg))
2254 self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
2255 else
2256 rhs;
2257 if (lhs_type.isSignedInt()) return self.builder.buildNSWShl(lhs, casted_rhs, "");
2258 return self.builder.buildNUWShl(lhs, casted_rhs, "");
2259 }
2260
2261 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2262 if (self.liveness.isUnused(inst)) return null;
2263
2206 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2264 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2207 const lhs = try self.resolveInst(bin_op.lhs);2265 const lhs = try self.resolveInst(bin_op.lhs);
2208 const rhs = try self.resolveInst(bin_op.rhs);2266 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -2212,15 +2270,25 @@ pub const FuncGen = struct {...@@ -2212,15 +2270,25 @@ pub const FuncGen = struct {
2212 self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")2270 self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
2213 else2271 else
2214 rhs;2272 rhs;
2215 if (sat) {
2216 return if (lhs_type.isSignedInt())
2217 self.builder.buildSShlSat(lhs, casted_rhs, "")
2218 else
2219 self.builder.buildUShlSat(lhs, casted_rhs, "");
2220 }
2221 return self.builder.buildShl(lhs, casted_rhs, "");2273 return self.builder.buildShl(lhs, casted_rhs, "");
2222 }2274 }
22232275
2276 fn airShlSat(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2277 if (self.liveness.isUnused(inst)) return null;
2278
2279 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2280 const lhs = try self.resolveInst(bin_op.lhs);
2281 const rhs = try self.resolveInst(bin_op.rhs);
2282 const lhs_type = self.air.typeOf(bin_op.lhs);
2283 const tg = self.dg.module.getTarget();
2284 const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg))
2285 self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
2286 else
2287 rhs;
2288 if (lhs_type.isSignedInt()) return self.builder.buildSShlSat(lhs, casted_rhs, "");
2289 return self.builder.buildUShlSat(lhs, casted_rhs, "");
2290 }
2291
2224 fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2292 fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2225 if (self.liveness.isUnused(inst))2293 if (self.liveness.isUnused(inst))
2226 return null;2294 return null;
src/codegen/llvm/bindings.zig+6
...@@ -469,6 +469,12 @@ pub const Builder = opaque {...@@ -469,6 +469,12 @@ pub const Builder = opaque {
469 pub const buildShl = LLVMBuildShl;469 pub const buildShl = LLVMBuildShl;
470 extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;470 extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
471471
472 pub const buildNUWShl = ZigLLVMBuildNUWShl;
473 extern fn ZigLLVMBuildNUWShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
474
475 pub const buildNSWShl = ZigLLVMBuildNSWShl;
476 extern fn ZigLLVMBuildNSWShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
477
472 pub const buildSShlSat = ZigLLVMBuildSShlSat;478 pub const buildSShlSat = ZigLLVMBuildSShlSat;
473 extern fn ZigLLVMBuildSShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;479 extern fn ZigLLVMBuildSShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
474480
src/link/C/zig.h+4-7
...@@ -356,9 +356,6 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon...@@ -356,9 +356,6 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon
356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
357}357}
358358
359/*
360 * Saturating aritmetic operations: add, sub, mul, shl
361 */
362#define zig_add_sat_u(ZT, T) static inline T zig_adds_##ZT(T x, T y, T max) { \359#define zig_add_sat_u(ZT, T) static inline T zig_adds_##ZT(T x, T y, T max) { \
363 return (x > max - y) ? max : x + y; \360 return (x > max - y) ? max : x + y; \
364}361}
...@@ -449,7 +446,7 @@ zig_shl_sat_u(u32, uint32_t, 32)...@@ -449,7 +446,7 @@ zig_shl_sat_u(u32, uint32_t, 32)
449zig_shl_sat_s(i32, int32_t, 31)446zig_shl_sat_s(i32, int32_t, 31)
450zig_shl_sat_u(u64, uint64_t, 64)447zig_shl_sat_u(u64, uint64_t, 64)
451zig_shl_sat_s(i64, int64_t, 63)448zig_shl_sat_s(i64, int64_t, 63)
452zig_shl_sat_s(isize, intptr_t, 63)449zig_shl_sat_s(isize, intptr_t, ((sizeof(intptr_t)) * CHAR_BIT - 1))
453zig_shl_sat_s(short, short, 15)450zig_shl_sat_s(short, short, ((sizeof(short )) * CHAR_BIT - 1))
454zig_shl_sat_s(int, int, 31)451zig_shl_sat_s(int, int, ((sizeof(int )) * CHAR_BIT - 1))
455zig_shl_sat_s(long, long, 63)452zig_shl_sat_s(long, long, ((sizeof(long )) * CHAR_BIT - 1))
src/print_air.zig+4-3
...@@ -104,13 +104,13 @@ const Writer = struct {...@@ -104,13 +104,13 @@ const Writer = struct {
104104
105 .add,105 .add,
106 .addwrap,106 .addwrap,
107 .addsat,107 .add_sat,
108 .sub,108 .sub,
109 .subwrap,109 .subwrap,
110 .subsat,110 .sub_sat,
111 .mul,111 .mul,
112 .mulwrap,112 .mulwrap,
113 .mulsat,113 .mul_sat,
114 .div,114 .div,
115 .rem,115 .rem,
116 .mod,116 .mod,
...@@ -133,6 +133,7 @@ const Writer = struct {...@@ -133,6 +133,7 @@ const Writer = struct {
133 .ptr_elem_val,133 .ptr_elem_val,
134 .ptr_ptr_elem_val,134 .ptr_ptr_elem_val,
135 .shl,135 .shl,
136 .shl_exact,
136 .shl_sat,137 .shl_sat,
137 .shr,138 .shr,
138 .set_union_tag,139 .set_union_tag,
src/print_zir.zig+4-18
...@@ -229,12 +229,15 @@ const Writer = struct {...@@ -229,12 +229,15 @@ const Writer = struct {
229229
230 .add,230 .add,
231 .addwrap,231 .addwrap,
232 .add_sat,
232 .array_cat,233 .array_cat,
233 .array_mul,234 .array_mul,
234 .mul,235 .mul,
235 .mulwrap,236 .mulwrap,
237 .mul_sat,
236 .sub,238 .sub,
237 .subwrap,239 .subwrap,
240 .sub_sat,
238 .cmp_lt,241 .cmp_lt,
239 .cmp_lte,242 .cmp_lte,
240 .cmp_eq,243 .cmp_eq,
...@@ -247,6 +250,7 @@ const Writer = struct {...@@ -247,6 +250,7 @@ const Writer = struct {
247 .mod_rem,250 .mod_rem,
248 .shl,251 .shl,
249 .shl_exact,252 .shl_exact,
253 .shl_sat,
250 .shr,254 .shr,
251 .shr_exact,255 .shr_exact,
252 .xor,256 .xor,
...@@ -400,12 +404,6 @@ const Writer = struct {...@@ -400,12 +404,6 @@ const Writer = struct {
400 .shl_with_overflow,404 .shl_with_overflow,
401 => try self.writeOverflowArithmetic(stream, extended),405 => try self.writeOverflowArithmetic(stream, extended),
402406
403 .add_with_saturation,
404 .sub_with_saturation,
405 .mul_with_saturation,
406 .shl_with_saturation,
407 => try self.writeSaturatingArithmetic(stream, extended),
408
409 .struct_decl => try self.writeStructDecl(stream, extended),407 .struct_decl => try self.writeStructDecl(stream, extended),
410 .union_decl => try self.writeUnionDecl(stream, extended),408 .union_decl => try self.writeUnionDecl(stream, extended),
411 .enum_decl => try self.writeEnumDecl(stream, extended),409 .enum_decl => try self.writeEnumDecl(stream, extended),
...@@ -854,18 +852,6 @@ const Writer = struct {...@@ -854,18 +852,6 @@ const Writer = struct {
854 try self.writeSrc(stream, src);852 try self.writeSrc(stream, src);
855 }853 }
856854
857 fn writeSaturatingArithmetic(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
858 const extra = self.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data;
859 const src: LazySrcLoc = .{ .node_offset = extra.node };
860
861 try self.writeInstRef(stream, extra.lhs);
862 try stream.writeAll(", ");
863 try self.writeInstRef(stream, extra.rhs);
864 try stream.writeAll(", ");
865 try stream.writeAll(") ");
866 try self.writeSrc(stream, src);
867 }
868
869 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {855 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
870 const inst_data = self.code.instructions.items(.data)[inst].pl_node;856 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
871 const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index);857 const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index);
src/stage1/all_types.hpp-4
...@@ -1818,10 +1818,6 @@ enum BuiltinFnId {...@@ -1818,10 +1818,6 @@ enum BuiltinFnId {
1818 BuiltinFnIdReduce,1818 BuiltinFnIdReduce,
1819 BuiltinFnIdMaximum,1819 BuiltinFnIdMaximum,
1820 BuiltinFnIdMinimum,1820 BuiltinFnIdMinimum,
1821 BuiltinFnIdSatAdd,
1822 BuiltinFnIdSatSub,
1823 BuiltinFnIdSatMul,
1824 BuiltinFnIdSatShl,
1825};1821};
18261822
1827struct BuiltinFnEntry {1823struct BuiltinFnEntry {
src/stage1/astgen.cpp-60
...@@ -4720,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast...@@ -4720,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
4720 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);4720 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);
4721 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);4721 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4722 }4722 }
4723 case BuiltinFnIdSatAdd:
4724 {
4725 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4726 Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4727 if (arg0_value == ag->codegen->invalid_inst_src)
4728 return arg0_value;
4729
4730 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4731 Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4732 if (arg1_value == ag->codegen->invalid_inst_src)
4733 return arg1_value;
4734
4735 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpAddSat, arg0_value, arg1_value, true);
4736 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4737 }
4738 case BuiltinFnIdSatSub:
4739 {
4740 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4741 Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4742 if (arg0_value == ag->codegen->invalid_inst_src)
4743 return arg0_value;
4744
4745 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4746 Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4747 if (arg1_value == ag->codegen->invalid_inst_src)
4748 return arg1_value;
4749
4750 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSubSat, arg0_value, arg1_value, true);
4751 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4752 }
4753 case BuiltinFnIdSatMul:
4754 {
4755 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4756 Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4757 if (arg0_value == ag->codegen->invalid_inst_src)
4758 return arg0_value;
4759
4760 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4761 Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4762 if (arg1_value == ag->codegen->invalid_inst_src)
4763 return arg1_value;
4764
4765 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMultSat, arg0_value, arg1_value, true);
4766 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4767 }
4768 case BuiltinFnIdSatShl:
4769 {
4770 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4771 Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4772 if (arg0_value == ag->codegen->invalid_inst_src)
4773 return arg0_value;
4774
4775 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4776 Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4777 if (arg1_value == ag->codegen->invalid_inst_src)
4778 return arg1_value;
4779
4780 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpShlSat, arg0_value, arg1_value, true);
4781 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4782 }
4783 case BuiltinFnIdMemcpy:4723 case BuiltinFnIdMemcpy:
4784 {4724 {
4785 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4725 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
src/stage1/codegen.cpp-4
...@@ -9134,10 +9134,6 @@ static void define_builtin_fns(CodeGen *g) {...@@ -9134,10 +9134,6 @@ static void define_builtin_fns(CodeGen *g) {
9134 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);9134 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
9135 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);9135 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
9136 create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);9136 create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);
9137 create_builtin_fn(g, BuiltinFnIdSatAdd, "addWithSaturation", 2);
9138 create_builtin_fn(g, BuiltinFnIdSatSub, "subWithSaturation", 2);
9139 create_builtin_fn(g, BuiltinFnIdSatMul, "mulWithSaturation", 2);
9140 create_builtin_fn(g, BuiltinFnIdSatShl, "shlWithSaturation", 2);
9141}9137}
91429138
9143static const char *bool_to_str(bool b) {9139static const char *bool_to_str(bool b) {
src/translate_c/ast.zig+4-4
...@@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1462 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),1462 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
1463 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),1463 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
1464 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),1464 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
1465 .shl => return renderBinOpGrouped(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"),1465 .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
1466 .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="),1466 .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
1467 .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"),1467 .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
1468 .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="),1468 .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
1469 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),1469 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
1470 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),1470 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
1471 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),1471 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
src/value.zig+87
...@@ -1588,6 +1588,35 @@ pub const Value = extern union {...@@ -1588,6 +1588,35 @@ pub const Value = extern union {
1588 return result;1588 return result;
1589 }1589 }
15901590
1591 /// Supports both floats and ints; handles undefined.
1592 pub fn numberAddSat(
1593 lhs: Value,
1594 rhs: Value,
1595 ty: Type,
1596 arena: *Allocator,
1597 target: Target,
1598 ) !Value {
1599 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1600
1601 if (ty.isAnyFloat()) {
1602 // TODO: handle outside float range
1603 return floatAdd(lhs, rhs, ty, arena);
1604 }
1605 const result = try intAdd(lhs, rhs, arena);
1606
1607 const max = try ty.maxInt(arena, target);
1608 if (compare(result, .gt, max, ty)) {
1609 return max;
1610 }
1611
1612 const min = try ty.minInt(arena, target);
1613 if (compare(result, .lt, min, ty)) {
1614 return min;
1615 }
1616
1617 return result;
1618 }
1619
1591 /// Supports both floats and ints; handles undefined.1620 /// Supports both floats and ints; handles undefined.
1592 pub fn numberSubWrap(1621 pub fn numberSubWrap(
1593 lhs: Value,1622 lhs: Value,
...@@ -1616,6 +1645,35 @@ pub const Value = extern union {...@@ -1616,6 +1645,35 @@ pub const Value = extern union {
1616 return result;1645 return result;
1617 }1646 }
16181647
1648 /// Supports both floats and ints; handles undefined.
1649 pub fn numberSubSat(
1650 lhs: Value,
1651 rhs: Value,
1652 ty: Type,
1653 arena: *Allocator,
1654 target: Target,
1655 ) !Value {
1656 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1657
1658 if (ty.isAnyFloat()) {
1659 // TODO: handle outside float range
1660 return floatSub(lhs, rhs, ty, arena);
1661 }
1662 const result = try intSub(lhs, rhs, arena);
1663
1664 const max = try ty.maxInt(arena, target);
1665 if (compare(result, .gt, max, ty)) {
1666 return max;
1667 }
1668
1669 const min = try ty.minInt(arena, target);
1670 if (compare(result, .lt, min, ty)) {
1671 return min;
1672 }
1673
1674 return result;
1675 }
1676
1619 /// Supports both floats and ints; handles undefined.1677 /// Supports both floats and ints; handles undefined.
1620 pub fn numberMulWrap(1678 pub fn numberMulWrap(
1621 lhs: Value,1679 lhs: Value,
...@@ -1644,6 +1702,35 @@ pub const Value = extern union {...@@ -1644,6 +1702,35 @@ pub const Value = extern union {
1644 return result;1702 return result;
1645 }1703 }
16461704
1705 /// Supports both floats and ints; handles undefined.
1706 pub fn numberMulSat(
1707 lhs: Value,
1708 rhs: Value,
1709 ty: Type,
1710 arena: *Allocator,
1711 target: Target,
1712 ) !Value {
1713 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1714
1715 if (ty.isAnyFloat()) {
1716 // TODO: handle outside float range
1717 return floatMul(lhs, rhs, ty, arena);
1718 }
1719 const result = try intMul(lhs, rhs, arena);
1720
1721 const max = try ty.maxInt(arena, target);
1722 if (compare(result, .gt, max, ty)) {
1723 return max;
1724 }
1725
1726 const min = try ty.minInt(arena, target);
1727 if (compare(result, .lt, min, ty)) {
1728 return min;
1729 }
1730
1731 return result;
1732 }
1733
1647 /// Supports both floats and ints; handles undefined.1734 /// Supports both floats and ints; handles undefined.
1648 pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {1735 pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1649 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);1736 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
test/behavior/saturating_arithmetic.zig+9-24
...@@ -32,7 +32,7 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {...@@ -32,7 +32,7 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {
32 }32 }
33}33}
3434
35test "@addWithSaturation" {35test "saturating add" {
36 const S = struct {36 const S = struct {
37 fn doTheTest() !void {37 fn doTheTest() !void {
38 // .{a, b, expected a+b}38 // .{a, b, expected a+b}
...@@ -50,22 +50,16 @@ test "@addWithSaturation" {...@@ -50,22 +50,16 @@ test "@addWithSaturation" {
50 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });50 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });
5151
52 const u8x3 = std.meta.Vector(3, u8);52 const u8x3 = std.meta.Vector(3, u8);
53 try expectEqual(u8x3{ 255, 255, 255 }, @addWithSaturation(53 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }));
54 u8x3{ 255, 254, 1 },
55 u8x3{ 1, 2, 255 },
56 ));
57 const i8x3 = std.meta.Vector(3, i8);54 const i8x3 = std.meta.Vector(3, i8);
58 try expectEqual(i8x3{ 127, 127, 127 }, @addWithSaturation(55 try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }));
59 i8x3{ 127, 126, 1 },
60 i8x3{ 1, 2, 127 },
61 ));
62 }56 }
63 };57 };
64 try S.doTheTest();58 try S.doTheTest();
65 comptime try S.doTheTest();59 comptime try S.doTheTest();
66}60}
6761
68test "@subWithSaturation" {62test "saturating subtraction" {
69 const S = struct {63 const S = struct {
70 fn doTheTest() !void {64 fn doTheTest() !void {
71 // .{a, b, expected a-b}65 // .{a, b, expected a-b}
...@@ -81,17 +75,14 @@ test "@subWithSaturation" {...@@ -81,17 +75,14 @@ test "@subWithSaturation" {
81 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });75 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });
8276
83 const u8x3 = std.meta.Vector(3, u8);77 const u8x3 = std.meta.Vector(3, u8);
84 try expectEqual(u8x3{ 0, 0, 0 }, @subWithSaturation(78 try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }));
85 u8x3{ 0, 0, 0 },
86 u8x3{ 255, 255, 255 },
87 ));
88 }79 }
89 };80 };
90 try S.doTheTest();81 try S.doTheTest();
91 comptime try S.doTheTest();82 comptime try S.doTheTest();
92}83}
9384
94test "@mulWithSaturation" {85test "saturating multiplication" {
95 // TODO: once #9660 has been solved, remove this line86 // TODO: once #9660 has been solved, remove this line
96 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;87 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;
9788
...@@ -112,10 +103,7 @@ test "@mulWithSaturation" {...@@ -112,10 +103,7 @@ test "@mulWithSaturation" {
112 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });103 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });
113104
114 const u8x3 = std.meta.Vector(3, u8);105 const u8x3 = std.meta.Vector(3, u8);
115 try expectEqual(u8x3{ 255, 255, 255 }, @mulWithSaturation(106 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }));
116 u8x3{ 2, 2, 2 },
117 u8x3{ 255, 255, 255 },
118 ));
119 }107 }
120 };108 };
121109
...@@ -123,7 +111,7 @@ test "@mulWithSaturation" {...@@ -123,7 +111,7 @@ test "@mulWithSaturation" {
123 comptime try S.doTheTest();111 comptime try S.doTheTest();
124}112}
125113
126test "@shlWithSaturation" {114test "saturating shift-left" {
127 const S = struct {115 const S = struct {
128 fn doTheTest() !void {116 fn doTheTest() !void {
129 // .{a, b, expected a<<b}117 // .{a, b, expected a<<b}
...@@ -140,10 +128,7 @@ test "@shlWithSaturation" {...@@ -140,10 +128,7 @@ test "@shlWithSaturation" {
140 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });128 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });
141129
142 const u8x3 = std.meta.Vector(3, u8);130 const u8x3 = std.meta.Vector(3, u8);
143 try expectEqual(u8x3{ 255, 255, 255 }, @shlWithSaturation(131 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }));
144 u8x3{ 255, 255, 255 },
145 u8x3{ 1, 1, 1 },
146 ));
147 }132 }
148 };133 };
149 try S.doTheTest();134 try S.doTheTest();