authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-28 22:21:15-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-09-28 22:21:15-04:00
logcf90cb7218d1baa56586477bab50e88fdb6be0bb
treea3495ecdbbca9a963f514938f20003f1aeb69b64
parent79bc5891c1c4cde0592fe1b10b6c9a85914155cf
parent54675824449d16029fdf6a1873e78cb8f2147f60
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9679 from travisstaloch/sat-arith-operators

sat-arithmetic: add operator support

31 files changed, 1319 insertions(+), 473 deletions(-)

doc/docgen.zig+8
......@@ -1058,15 +1058,21 @@ fn tokenizeAndPrintRaw(
10581058 .plus_equal,
10591059 .plus_percent,
10601060 .plus_percent_equal,
1061 .plus_pipe,
1062 .plus_pipe_equal,
10611063 .minus,
10621064 .minus_equal,
10631065 .minus_percent,
10641066 .minus_percent_equal,
1067 .minus_pipe,
1068 .minus_pipe_equal,
10651069 .asterisk,
10661070 .asterisk_equal,
10671071 .asterisk_asterisk,
10681072 .asterisk_percent,
10691073 .asterisk_percent_equal,
1074 .asterisk_pipe,
1075 .asterisk_pipe_equal,
10701076 .arrow,
10711077 .colon,
10721078 .slash,
......@@ -1079,6 +1085,8 @@ fn tokenizeAndPrintRaw(
10791085 .angle_bracket_left_equal,
10801086 .angle_bracket_angle_bracket_left,
10811087 .angle_bracket_angle_bracket_left_equal,
1088 .angle_bracket_angle_bracket_left_pipe,
1089 .angle_bracket_angle_bracket_left_pipe_equal,
10821090 .angle_bracket_right,
10831091 .angle_bracket_right_equal,
10841092 .angle_bracket_angle_bracket_right,
doc/langref.html.in+95-63
......@@ -1244,8 +1244,9 @@ fn divide(a: i32, b: i32) i32 {
12441244 </p>
12451245 <p>
12461246 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
1247 integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and
1248 {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.
1247 integer overflow. Alternative operators are provided for wrapping and saturating arithmetic on all targets.
1248 {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} perform wrapping arithmetic
1249 while {#syntax#}+|{#endsyntax#} and {#syntax#}-|{#endsyntax#} perform saturating arithmetic.
12491250 </p>
12501251 <p>
12511252 Zig supports arbitrary bit-width integers, referenced by using
......@@ -1395,6 +1396,23 @@ a +%= b{#endsyntax#}</pre></th>
13951396 <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +% 1 == 0{#endsyntax#}</pre>
13961397 </td>
13971398 </tr>
1399 <tr>
1400 <th scope="row"><pre>{#syntax#}a +| b
1401a +|= b{#endsyntax#}</pre></th>
1402 <td>
1403 <ul>
1404 <li>{#link|Integers#}</li>
1405 </ul>
1406 </td>
1407 <td>Saturating Addition.
1408 <ul>
1409 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1410 </ul>
1411 </td>
1412 <td>
1413 <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +| 1 == @as(u32, std.math.maxInt(u32)){#endsyntax#}</pre>
1414 </td>
1415 </tr>
13981416 <tr>
13991417 <th scope="row"><pre>{#syntax#}a - b
14001418a -= b{#endsyntax#}</pre></th>
......@@ -1434,6 +1452,23 @@ a -%= b{#endsyntax#}</pre></th>
14341452 <pre>{#syntax#}@as(u32, 0) -% 1 == std.math.maxInt(u32){#endsyntax#}</pre>
14351453 </td>
14361454 </tr>
1455 <tr>
1456 <th scope="row"><pre>{#syntax#}a -| b
1457a -|= b{#endsyntax#}</pre></th>
1458 <td>
1459 <ul>
1460 <li>{#link|Integers#}</li>
1461 </ul>
1462 </td>
1463 <td>Saturating Subtraction.
1464 <ul>
1465 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1466 </ul>
1467 </td>
1468 <td>
1469 <pre>{#syntax#}@as(u32, 0) -| 1 == 0{#endsyntax#}</pre>
1470 </td>
1471 </tr>
14371472 <tr>
14381473 <th scope="row"><pre>{#syntax#}-a{#endsyntax#}</pre></th>
14391474 <td>
......@@ -1508,6 +1543,23 @@ a *%= b{#endsyntax#}</pre></th>
15081543 <pre>{#syntax#}@as(u8, 200) *% 2 == 144{#endsyntax#}</pre>
15091544 </td>
15101545 </tr>
1546 <tr>
1547 <th scope="row"><pre>{#syntax#}a *| b
1548a *|= b{#endsyntax#}</pre></th>
1549 <td>
1550 <ul>
1551 <li>{#link|Integers#}</li>
1552 </ul>
1553 </td>
1554 <td>Saturating Multiplication.
1555 <ul>
1556 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1557 </ul>
1558 </td>
1559 <td>
1560 <pre>{#syntax#}@as(u8, 200) *| 2 == 255{#endsyntax#}</pre>
1561 </td>
1562 </tr>
15111563 <tr>
15121564 <th scope="row"><pre>{#syntax#}a / b
15131565a /= b{#endsyntax#}</pre></th>
......@@ -1577,6 +1629,24 @@ a <<= b{#endsyntax#}</pre></th>
15771629 <pre>{#syntax#}1 << 8 == 256{#endsyntax#}</pre>
15781630 </td>
15791631 </tr>
1632 <tr>
1633 <th scope="row"><pre>{#syntax#}a <<| b
1634a <<|= b{#endsyntax#}</pre></th>
1635 <td>
1636 <ul>
1637 <li>{#link|Integers#}</li>
1638 </ul>
1639 </td>
1640 <td>Saturating Bit Shift Left.
1641 <ul>
1642 <li>See also {#link|@shlExact#}.</li>
1643 <li>See also {#link|@shlWithOverflow#}.</li>
1644 </ul>
1645 </td>
1646 <td>
1647 <pre>{#syntax#}@as(u8, 1) <<| 8 == 255{#endsyntax#}</pre>
1648 </td>
1649 </tr>
15801650 <tr>
15811651 <th scope="row"><pre>{#syntax#}a >> b
15821652a >>= b{#endsyntax#}</pre></th>
......@@ -1968,14 +2038,14 @@ const B = error{Two};
19682038a!b
19692039x{}
19702040!x -x -%x ~x &x ?x
1971* / % ** *% ||
1972+ - ++ +% -%
1973<< >>
2041* / % ** *% *| ||
2042+ - ++ +% -% +| -|
2043<< >> <<|
19742044& ^ | orelse catch
19752045== != < > <= >=
19762046and
19772047or
1978= *= /= %= += -= <<= >>= &= ^= |={#endsyntax#}</pre>
2048= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}</pre>
19792049 {#header_close#}
19802050 {#header_close#}
19812051 {#header_open|Arrays#}
......@@ -7162,16 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
71627232 If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
71637233 </p>
71647234 {#header_close#}
7165 {#header_open|@addWithSaturation#}
7166 <pre>{#syntax#}@addWithSaturation(a: T, b: T) T{#endsyntax#}</pre>
7167 <p>
7168 Returns {#syntax#}a + b{#endsyntax#}. The result will be clamped between the type maximum and minimum.
7169 </p>
7170 <p>
7171 Once <a href="https://github.com/ziglang/zig/issues/1284">Saturating arithmetic</a>.
7172 is completed, the syntax {#syntax#}a +| b{#endsyntax#} will be equivalent to calling {#syntax#}@addWithSaturation(a, b){#endsyntax#}.
7173 </p>
7174 {#header_close#}
71757235 {#header_open|@alignCast#}
71767236 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>
71777237 <p>
......@@ -8293,22 +8353,6 @@ test "@wasmMemoryGrow" {
82938353 </p>
82948354 {#header_close#}
82958355
8296 {#header_open|@mulWithSaturation#}
8297 <pre>{#syntax#}@mulWithSaturation(a: T, b: T) T{#endsyntax#}</pre>
8298 <p>
8299 Returns {#syntax#}a * b{#endsyntax#}. The result will be clamped between the type maximum and minimum.
8300 </p>
8301 <p>
8302 Once <a href="https://github.com/ziglang/zig/issues/1284">Saturating arithmetic</a>.
8303 is completed, the syntax {#syntax#}a *| b{#endsyntax#} will be equivalent to calling {#syntax#}@mulWithSaturation(a, b){#endsyntax#}.
8304 </p>
8305 <p>
8306 NOTE: Currently there is a bug in the llvm.smul.fix.sat intrinsic which affects {#syntax#}@mulWithSaturation{#endsyntax#} of signed integers.
8307 This may result in an incorrect sign bit when there is overflow. This will be fixed in zig's 0.9.0 release.
8308 Check <a href="https://github.com/ziglang/zig/issues/9643">this issue</a> for more information.
8309 </p>
8310 {#header_close#}
8311
83128356 {#header_open|@panic#}
83138357 <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre>
83148358 <p>
......@@ -8526,14 +8570,16 @@ test "@setRuntimeSafety" {
85268570 {#header_open|@shlExact#}
85278571 <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
85288572 <p>
8529 Performs the left shift operation ({#syntax#}<<{#endsyntax#}). Caller guarantees
8530 that the shift will not shift any 1 bits out.
8573 Performs the left shift operation ({#syntax#}<<{#endsyntax#}).
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.
85318577 </p>
85328578 <p>
85338579 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
85348580 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
85358581 </p>
8536 {#see_also|@shrExact|@shlWithOverflow|@shlWithSaturation#}
8582 {#see_also|@shrExact|@shlWithOverflow#}
85378583 {#header_close#}
85388584
85398585 {#header_open|@shlWithOverflow#}
......@@ -8547,24 +8593,9 @@ test "@setRuntimeSafety" {
85478593 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
85488594 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
85498595 </p>
8550 {#see_also|@shlExact|@shrExact|@shlWithSaturation#}
8596 {#see_also|@shlExact|@shrExact#}
85518597 {#header_close#}
85528598
8553 {#header_open|@shlWithSaturation#}
8554 <pre>{#syntax#}@shlWithSaturation(a: T, shift_amt: T) T{#endsyntax#}</pre>
8555 <p>
8556 Returns {#syntax#}a << b{#endsyntax#}. The result will be clamped between type minimum and maximum.
8557 </p>
8558 <p>
8559 Once <a href="https://github.com/ziglang/zig/issues/1284">Saturating arithmetic</a>.
8560 is completed, the syntax {#syntax#}a <<| b{#endsyntax#} will be equivalent to calling {#syntax#}@shlWithSaturation(a, b){#endsyntax#}.
8561 </p>
8562 <p>
8563 Unlike other @shl builtins, shift_amt doesn't need to be a Log2T as saturated overshifting is well defined.
8564 </p>
8565 {#see_also|@shlExact|@shrExact|@shlWithOverflow#}
8566 {#header_close#}
8567
85688599 {#header_open|@shrExact#}
85698600 <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
85708601 <p>
......@@ -8575,7 +8606,7 @@ test "@setRuntimeSafety" {
85758606 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
85768607 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
85778608 </p>
8578 {#see_also|@shlExact|@shlWithOverflow|@shlWithSaturation#}
8609 {#see_also|@shlExact|@shlWithOverflow#}
85798610 {#header_close#}
85808611
85818612 {#header_open|@shuffle#}
......@@ -8875,17 +8906,6 @@ fn doTheTest() !void {
88758906 </p>
88768907 {#header_close#}
88778908
8878 {#header_open|@subWithSaturation#}
8879 <pre>{#syntax#}@subWithSaturation(a: T, b: T) T{#endsyntax#}</pre>
8880 <p>
8881 Returns {#syntax#}a - b{#endsyntax#}. The result will be clamped between the type maximum and minimum.
8882 </p>
8883 <p>
8884 Once <a href="https://github.com/ziglang/zig/issues/1284">Saturating arithmetic</a>.
8885 is completed, the syntax {#syntax#}a -| b{#endsyntax#} will be equivalent to calling {#syntax#}@subWithSaturation(a, b){#endsyntax#}.
8886 </p>
8887 {#header_close#}
8888
88898909 {#header_open|@tagName#}
88908910 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>
88918911 <p>
......@@ -11839,6 +11859,7 @@ AssignOp
1183911859 / PLUSEQUAL
1184011860 / MINUSEQUAL
1184111861 / LARROW2EQUAL
11862 / LARROW2PIPEEQUAL
1184211863 / RARROW2EQUAL
1184311864 / AMPERSANDEQUAL
1184411865 / CARETEQUAL
......@@ -11873,6 +11894,8 @@ AdditionOp
1187311894 / PLUS2
1187411895 / PLUSPERCENT
1187511896 / MINUSPERCENT
11897 / PLUSPIPE
11898 / MINUSPIPE
1187611899
1187711900MultiplyOp
1187811901 &lt;- PIPE2
......@@ -11881,6 +11904,7 @@ MultiplyOp
1188111904 / PERCENT
1188211905 / ASTERISK2
1188311906 / ASTERISKPERCENT
11907 / ASTERISKPIPE
1188411908
1188511909PrefixOp
1188611910 &lt;- EXCLAMATIONMARK
......@@ -12044,6 +12068,8 @@ ASTERISK2 &lt;- '**' skip
1204412068ASTERISKEQUAL &lt;- '*=' skip
1204512069ASTERISKPERCENT &lt;- '*%' ![=] skip
1204612070ASTERISKPERCENTEQUAL &lt;- '*%=' skip
12071ASTERISKPIPE &lt;- '*|' ![=] skip
12072ASTERISKPIPEEQUAL &lt;- '*|=' skip
1204712073CARET &lt;- '^' ![=] skip
1204812074CARETEQUAL &lt;- '^=' skip
1204912075COLON &lt;- ':' skip
......@@ -12060,6 +12086,8 @@ EXCLAMATIONMARK &lt;- '!' ![=] skip
1206012086EXCLAMATIONMARKEQUAL &lt;- '!=' skip
1206112087LARROW &lt;- '&lt;' ![&lt;=] skip
1206212088LARROW2 &lt;- '&lt;&lt;' ![=] skip
12089LARROW2PIPE &lt;- '&lt;&lt;|' ![=] skip
12090LARROW2PIPEEQUAL &lt;- '&lt;&lt;|=' ![=] skip
1206312091LARROW2EQUAL &lt;- '&lt;&lt;=' skip
1206412092LARROWEQUAL &lt;- '&lt;=' skip
1206512093LBRACE &lt;- '{' skip
......@@ -12069,6 +12097,8 @@ MINUS &lt;- '-' ![%=&gt;] skip
1206912097MINUSEQUAL &lt;- '-=' skip
1207012098MINUSPERCENT &lt;- '-%' ![=] skip
1207112099MINUSPERCENTEQUAL &lt;- '-%=' skip
12100MINUSPIPE &lt;- '-|' ![=] skip
12101MINUSPIPEEQUAL &lt;- '-|=' skip
1207212102MINUSRARROW &lt;- '-&gt;' skip
1207312103PERCENT &lt;- '%' ![=] skip
1207412104PERCENTEQUAL &lt;- '%=' skip
......@@ -12080,6 +12110,8 @@ PLUS2 &lt;- '++' skip
1208012110PLUSEQUAL &lt;- '+=' skip
1208112111PLUSPERCENT &lt;- '+%' ![=] skip
1208212112PLUSPERCENTEQUAL &lt;- '+%=' skip
12113PLUSPIPE &lt;- '+|' ![=] skip
12114PLUSPIPEEQUAL &lt;- '+|=' skip
1208312115LETTERC &lt;- 'c' skip
1208412116QUESTIONMARK &lt;- '?' skip
1208512117RARROW &lt;- '&gt;' ![&gt;=] skip
lib/std/zig/Ast.zig+44-12
......@@ -395,14 +395,18 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
395395 .assign_mod,
396396 .assign_add,
397397 .assign_sub,
398 .assign_bit_shift_left,
399 .assign_bit_shift_right,
398 .assign_shl,
399 .assign_shl_sat,
400 .assign_shr,
400401 .assign_bit_and,
401402 .assign_bit_xor,
402403 .assign_bit_or,
403404 .assign_mul_wrap,
404405 .assign_add_wrap,
405406 .assign_sub_wrap,
407 .assign_mul_sat,
408 .assign_add_sat,
409 .assign_sub_sat,
406410 .assign,
407411 .merge_error_sets,
408412 .mul,
......@@ -410,13 +414,17 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
410414 .mod,
411415 .array_mult,
412416 .mul_wrap,
417 .mul_sat,
413418 .add,
414419 .sub,
415420 .array_cat,
416421 .add_wrap,
417422 .sub_wrap,
418 .bit_shift_left,
419 .bit_shift_right,
423 .add_sat,
424 .sub_sat,
425 .shl,
426 .shl_sat,
427 .shr,
420428 .bit_and,
421429 .bit_xor,
422430 .bit_or,
......@@ -651,14 +659,18 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
651659 .assign_mod,
652660 .assign_add,
653661 .assign_sub,
654 .assign_bit_shift_left,
655 .assign_bit_shift_right,
662 .assign_shl,
663 .assign_shl_sat,
664 .assign_shr,
656665 .assign_bit_and,
657666 .assign_bit_xor,
658667 .assign_bit_or,
659668 .assign_mul_wrap,
660669 .assign_add_wrap,
661670 .assign_sub_wrap,
671 .assign_mul_sat,
672 .assign_add_sat,
673 .assign_sub_sat,
662674 .assign,
663675 .merge_error_sets,
664676 .mul,
......@@ -666,13 +678,17 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
666678 .mod,
667679 .array_mult,
668680 .mul_wrap,
681 .mul_sat,
669682 .add,
670683 .sub,
671684 .array_cat,
672685 .add_wrap,
673686 .sub_wrap,
674 .bit_shift_left,
675 .bit_shift_right,
687 .add_sat,
688 .sub_sat,
689 .shl,
690 .shl_sat,
691 .shr,
676692 .bit_and,
677693 .bit_xor,
678694 .bit_or,
......@@ -2524,9 +2540,11 @@ pub const Node = struct {
25242540 /// `lhs -= rhs`. main_token is op.
25252541 assign_sub,
25262542 /// `lhs <<= rhs`. main_token is op.
2527 assign_bit_shift_left,
2543 assign_shl,
2544 /// `lhs <<|= rhs`. main_token is op.
2545 assign_shl_sat,
25282546 /// `lhs >>= rhs`. main_token is op.
2529 assign_bit_shift_right,
2547 assign_shr,
25302548 /// `lhs &= rhs`. main_token is op.
25312549 assign_bit_and,
25322550 /// `lhs ^= rhs`. main_token is op.
......@@ -2539,6 +2557,12 @@ pub const Node = struct {
25392557 assign_add_wrap,
25402558 /// `lhs -%= rhs`. main_token is op.
25412559 assign_sub_wrap,
2560 /// `lhs *|= rhs`. main_token is op.
2561 assign_mul_sat,
2562 /// `lhs +|= rhs`. main_token is op.
2563 assign_add_sat,
2564 /// `lhs -|= rhs`. main_token is op.
2565 assign_sub_sat,
25422566 /// `lhs = rhs`. main_token is op.
25432567 assign,
25442568 /// `lhs || rhs`. main_token is the `||`.
......@@ -2553,6 +2577,8 @@ pub const Node = struct {
25532577 array_mult,
25542578 /// `lhs *% rhs`. main_token is the `*%`.
25552579 mul_wrap,
2580 /// `lhs *| rhs`. main_token is the `*|`.
2581 mul_sat,
25562582 /// `lhs + rhs`. main_token is the `+`.
25572583 add,
25582584 /// `lhs - rhs`. main_token is the `-`.
......@@ -2563,10 +2589,16 @@ pub const Node = struct {
25632589 add_wrap,
25642590 /// `lhs -% rhs`. main_token is the `-%`.
25652591 sub_wrap,
2592 /// `lhs +| rhs`. main_token is the `+|`.
2593 add_sat,
2594 /// `lhs -| rhs`. main_token is the `-|`.
2595 sub_sat,
25662596 /// `lhs << rhs`. main_token is the `<<`.
2567 bit_shift_left,
2597 shl,
2598 /// `lhs <<| rhs`. main_token is the `<<|`.
2599 shl_sat,
25682600 /// `lhs >> rhs`. main_token is the `>>`.
2569 bit_shift_right,
2601 shr,
25702602 /// `lhs & rhs`. main_token is the `&`.
25712603 bit_and,
25722604 /// `lhs ^ rhs`. main_token is the `^`.
lib/std/zig/parse.zig+12-4
......@@ -1268,14 +1268,18 @@ const Parser = struct {
12681268 .percent_equal => .assign_mod,
12691269 .plus_equal => .assign_add,
12701270 .minus_equal => .assign_sub,
1271 .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,
1272 .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,
1271 .angle_bracket_angle_bracket_left_equal => .assign_shl,
1272 .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat,
1273 .angle_bracket_angle_bracket_right_equal => .assign_shr,
12731274 .ampersand_equal => .assign_bit_and,
12741275 .caret_equal => .assign_bit_xor,
12751276 .pipe_equal => .assign_bit_or,
12761277 .asterisk_percent_equal => .assign_mul_wrap,
12771278 .plus_percent_equal => .assign_add_wrap,
12781279 .minus_percent_equal => .assign_sub_wrap,
1280 .asterisk_pipe_equal => .assign_mul_sat,
1281 .plus_pipe_equal => .assign_add_sat,
1282 .minus_pipe_equal => .assign_sub_sat,
12791283 .equal => .assign,
12801284 else => return expr,
12811285 };
......@@ -1342,14 +1346,17 @@ const Parser = struct {
13421346 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },
13431347 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
13441348
1345 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left },
1346 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right },
1349 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl },
1350 .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat },
1351 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr },
13471352
13481353 .plus = .{ .prec = 60, .tag = .add },
13491354 .minus = .{ .prec = 60, .tag = .sub },
13501355 .plus_plus = .{ .prec = 60, .tag = .array_cat },
13511356 .plus_percent = .{ .prec = 60, .tag = .add_wrap },
13521357 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },
1358 .plus_pipe = .{ .prec = 60, .tag = .add_sat },
1359 .minus_pipe = .{ .prec = 60, .tag = .sub_sat },
13531360
13541361 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },
13551362 .asterisk = .{ .prec = 70, .tag = .mul },
......@@ -1357,6 +1364,7 @@ const Parser = struct {
13571364 .percent = .{ .prec = 70, .tag = .mod },
13581365 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },
13591366 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },
1367 .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat },
13601368 });
13611369
13621370 fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {
lib/std/zig/parser_test.zig+20
......@@ -4739,6 +4739,26 @@ test "zig fmt: assignment with inline for and inline while" {
47394739 );
47404740}
47414741
4742test "zig fmt: saturating arithmetic" {
4743 try testCanonical(
4744 \\test {
4745 \\ const actual = switch (op) {
4746 \\ .add => a +| b,
4747 \\ .sub => a -| b,
4748 \\ .mul => a *| b,
4749 \\ .shl => a <<| b,
4750 \\ };
4751 \\ switch (op) {
4752 \\ .add => actual +|= b,
4753 \\ .sub => actual -|= b,
4754 \\ .mul => actual *|= b,
4755 \\ .shl => actual <<|= b,
4756 \\ }
4757 \\}
4758 \\
4759 );
4760}
4761
47424762test "zig fmt: insert trailing comma if there are comments between switch values" {
47434763 try testTransform(
47444764 \\const a = switch (b) {
lib/std/zig/render.zig+16-8
......@@ -333,27 +333,33 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
333333
334334 .add,
335335 .add_wrap,
336 .add_sat,
336337 .array_cat,
337338 .array_mult,
338339 .assign,
339340 .assign_bit_and,
340341 .assign_bit_or,
341 .assign_bit_shift_left,
342 .assign_bit_shift_right,
342 .assign_shl,
343 .assign_shl_sat,
344 .assign_shr,
343345 .assign_bit_xor,
344346 .assign_div,
345347 .assign_sub,
346348 .assign_sub_wrap,
349 .assign_sub_sat,
347350 .assign_mod,
348351 .assign_add,
349352 .assign_add_wrap,
353 .assign_add_sat,
350354 .assign_mul,
351355 .assign_mul_wrap,
356 .assign_mul_sat,
352357 .bang_equal,
353358 .bit_and,
354359 .bit_or,
355 .bit_shift_left,
356 .bit_shift_right,
360 .shl,
361 .shl_sat,
362 .shr,
357363 .bit_xor,
358364 .bool_and,
359365 .bool_or,
......@@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
367373 .mod,
368374 .mul,
369375 .mul_wrap,
376 .mul_sat,
370377 .sub,
371378 .sub_wrap,
379 .sub_sat,
372380 .@"orelse",
373381 => {
374382 const infix = datas[node];
......@@ -2520,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
25202528 .assign,
25212529 .assign_bit_and,
25222530 .assign_bit_or,
2523 .assign_bit_shift_left,
2524 .assign_bit_shift_right,
2531 .assign_shl,
2532 .assign_shr,
25252533 .assign_bit_xor,
25262534 .assign_div,
25272535 .assign_sub,
......@@ -2534,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
25342542 .bang_equal,
25352543 .bit_and,
25362544 .bit_or,
2537 .bit_shift_left,
2538 .bit_shift_right,
2545 .shl,
2546 .shr,
25392547 .bit_xor,
25402548 .bool_and,
25412549 .bool_or,
lib/std/zig/tokenizer.zig+97
......@@ -103,15 +103,21 @@ pub const Token = struct {
103103 plus_equal,
104104 plus_percent,
105105 plus_percent_equal,
106 plus_pipe,
107 plus_pipe_equal,
106108 minus,
107109 minus_equal,
108110 minus_percent,
109111 minus_percent_equal,
112 minus_pipe,
113 minus_pipe_equal,
110114 asterisk,
111115 asterisk_equal,
112116 asterisk_asterisk,
113117 asterisk_percent,
114118 asterisk_percent_equal,
119 asterisk_pipe,
120 asterisk_pipe_equal,
115121 arrow,
116122 colon,
117123 slash,
......@@ -124,6 +130,8 @@ pub const Token = struct {
124130 angle_bracket_left_equal,
125131 angle_bracket_angle_bracket_left,
126132 angle_bracket_angle_bracket_left_equal,
133 angle_bracket_angle_bracket_left_pipe,
134 angle_bracket_angle_bracket_left_pipe_equal,
127135 angle_bracket_right,
128136 angle_bracket_right_equal,
129137 angle_bracket_angle_bracket_right,
......@@ -227,15 +235,21 @@ pub const Token = struct {
227235 .plus_equal => "+=",
228236 .plus_percent => "+%",
229237 .plus_percent_equal => "+%=",
238 .plus_pipe => "+|",
239 .plus_pipe_equal => "+|=",
230240 .minus => "-",
231241 .minus_equal => "-=",
232242 .minus_percent => "-%",
233243 .minus_percent_equal => "-%=",
244 .minus_pipe => "-|",
245 .minus_pipe_equal => "-|=",
234246 .asterisk => "*",
235247 .asterisk_equal => "*=",
236248 .asterisk_asterisk => "**",
237249 .asterisk_percent => "*%",
238250 .asterisk_percent_equal => "*%=",
251 .asterisk_pipe => "*|",
252 .asterisk_pipe_equal => "*|=",
239253 .arrow => "->",
240254 .colon => ":",
241255 .slash => "/",
......@@ -248,6 +262,8 @@ pub const Token = struct {
248262 .angle_bracket_left_equal => "<=",
249263 .angle_bracket_angle_bracket_left => "<<",
250264 .angle_bracket_angle_bracket_left_equal => "<<=",
265 .angle_bracket_angle_bracket_left_pipe => "<<|",
266 .angle_bracket_angle_bracket_left_pipe_equal => "<<|=",
251267 .angle_bracket_right => ">",
252268 .angle_bracket_right_equal => ">=",
253269 .angle_bracket_angle_bracket_right => ">>",
......@@ -352,8 +368,10 @@ pub const Tokenizer = struct {
352368 pipe,
353369 minus,
354370 minus_percent,
371 minus_pipe,
355372 asterisk,
356373 asterisk_percent,
374 asterisk_pipe,
357375 slash,
358376 line_comment_start,
359377 line_comment,
......@@ -382,8 +400,10 @@ pub const Tokenizer = struct {
382400 percent,
383401 plus,
384402 plus_percent,
403 plus_pipe,
385404 angle_bracket_left,
386405 angle_bracket_angle_bracket_left,
406 angle_bracket_angle_bracket_left_pipe,
387407 angle_bracket_right,
388408 angle_bracket_angle_bracket_right,
389409 period,
......@@ -584,6 +604,9 @@ pub const Tokenizer = struct {
584604 '%' => {
585605 state = .asterisk_percent;
586606 },
607 '|' => {
608 state = .asterisk_pipe;
609 },
587610 else => {
588611 result.tag = .asterisk;
589612 break;
......@@ -602,6 +625,18 @@ pub const Tokenizer = struct {
602625 },
603626 },
604627
628 .asterisk_pipe => switch (c) {
629 '=' => {
630 result.tag = .asterisk_pipe_equal;
631 self.index += 1;
632 break;
633 },
634 else => {
635 result.tag = .asterisk_pipe;
636 break;
637 },
638 },
639
605640 .percent => switch (c) {
606641 '=' => {
607642 result.tag = .percent_equal;
......@@ -628,6 +663,9 @@ pub const Tokenizer = struct {
628663 '%' => {
629664 state = .plus_percent;
630665 },
666 '|' => {
667 state = .plus_pipe;
668 },
631669 else => {
632670 result.tag = .plus;
633671 break;
......@@ -646,6 +684,18 @@ pub const Tokenizer = struct {
646684 },
647685 },
648686
687 .plus_pipe => switch (c) {
688 '=' => {
689 result.tag = .plus_pipe_equal;
690 self.index += 1;
691 break;
692 },
693 else => {
694 result.tag = .plus_pipe;
695 break;
696 },
697 },
698
649699 .caret => switch (c) {
650700 '=' => {
651701 result.tag = .caret_equal;
......@@ -903,6 +953,9 @@ pub const Tokenizer = struct {
903953 '%' => {
904954 state = .minus_percent;
905955 },
956 '|' => {
957 state = .minus_pipe;
958 },
906959 else => {
907960 result.tag = .minus;
908961 break;
......@@ -920,6 +973,17 @@ pub const Tokenizer = struct {
920973 break;
921974 },
922975 },
976 .minus_pipe => switch (c) {
977 '=' => {
978 result.tag = .minus_pipe_equal;
979 self.index += 1;
980 break;
981 },
982 else => {
983 result.tag = .minus_pipe;
984 break;
985 },
986 },
923987
924988 .angle_bracket_left => switch (c) {
925989 '<' => {
......@@ -942,12 +1006,27 @@ pub const Tokenizer = struct {
9421006 self.index += 1;
9431007 break;
9441008 },
1009 '|' => {
1010 state = .angle_bracket_angle_bracket_left_pipe;
1011 },
9451012 else => {
9461013 result.tag = .angle_bracket_angle_bracket_left;
9471014 break;
9481015 },
9491016 },
9501017
1018 .angle_bracket_angle_bracket_left_pipe => switch (c) {
1019 '=' => {
1020 result.tag = .angle_bracket_angle_bracket_left_pipe_equal;
1021 self.index += 1;
1022 break;
1023 },
1024 else => {
1025 result.tag = .angle_bracket_angle_bracket_left_pipe;
1026 break;
1027 },
1028 },
1029
9511030 .angle_bracket_right => switch (c) {
9521031 '>' => {
9531032 state = .angle_bracket_angle_bracket_right;
......@@ -1936,6 +2015,24 @@ test "tokenizer - invalid token with unfinished escape right before eof" {
19362015 try testTokenize("'\\u", &.{.invalid});
19372016}
19382017
2018test "tokenizer - saturating" {
2019 try testTokenize("<<", &.{.angle_bracket_angle_bracket_left});
2020 try testTokenize("<<|", &.{.angle_bracket_angle_bracket_left_pipe});
2021 try testTokenize("<<|=", &.{.angle_bracket_angle_bracket_left_pipe_equal});
2022
2023 try testTokenize("*", &.{.asterisk});
2024 try testTokenize("*|", &.{.asterisk_pipe});
2025 try testTokenize("*|=", &.{.asterisk_pipe_equal});
2026
2027 try testTokenize("+", &.{.plus});
2028 try testTokenize("+|", &.{.plus_pipe});
2029 try testTokenize("+|=", &.{.plus_pipe_equal});
2030
2031 try testTokenize("-", &.{.minus});
2032 try testTokenize("-|", &.{.minus_pipe});
2033 try testTokenize("-|=", &.{.minus_pipe_equal});
2034}
2035
19392036fn testTokenize(source: [:0]const u8, expected_tokens: []const Token.Tag) !void {
19402037 var tokenizer = Tokenizer.init(source);
19412038 for (expected_tokens) |expected_token_id| {
src/Air.zig+28
......@@ -44,6 +44,11 @@ pub const Inst = struct {
4444 /// is the same as both operands.
4545 /// Uses the `bin_op` field.
4646 addwrap,
47 /// Saturating integer addition.
48 /// Both operands are guaranteed to be the same type, and the result type
49 /// is the same as both operands.
50 /// Uses the `bin_op` field.
51 add_sat,
4752 /// Float or integer subtraction. For integers, wrapping is undefined behavior.
4853 /// Both operands are guaranteed to be the same type, and the result type
4954 /// is the same as both operands.
......@@ -54,6 +59,11 @@ pub const Inst = struct {
5459 /// is the same as both operands.
5560 /// Uses the `bin_op` field.
5661 subwrap,
62 /// Saturating integer subtraction.
63 /// Both operands are guaranteed to be the same type, and the result type
64 /// is the same as both operands.
65 /// Uses the `bin_op` field.
66 sub_sat,
5767 /// Float or integer multiplication. For integers, wrapping is undefined behavior.
5868 /// Both operands are guaranteed to be the same type, and the result type
5969 /// is the same as both operands.
......@@ -64,6 +74,11 @@ pub const Inst = struct {
6474 /// is the same as both operands.
6575 /// Uses the `bin_op` field.
6676 mulwrap,
77 /// Saturating integer multiplication.
78 /// Both operands are guaranteed to be the same type, and the result type
79 /// is the same as both operands.
80 /// Uses the `bin_op` field.
81 mul_sat,
6782 /// Integer or float division. For integers, wrapping is undefined behavior.
6883 /// Both operands are guaranteed to be the same type, and the result type
6984 /// is the same as both operands.
......@@ -110,6 +125,14 @@ pub const Inst = struct {
110125 /// Shift left. `<<`
111126 /// Uses the `bin_op` field.
112127 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,
133 /// Shift left saturating. `<<|`
134 /// Uses the `bin_op` field.
135 shl_sat,
113136 /// Bitwise XOR. `^`
114137 /// Uses the `bin_op` field.
115138 xor,
......@@ -568,10 +591,13 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
568591
569592 .add,
570593 .addwrap,
594 .add_sat,
571595 .sub,
572596 .subwrap,
597 .sub_sat,
573598 .mul,
574599 .mulwrap,
600 .mul_sat,
575601 .div,
576602 .rem,
577603 .mod,
......@@ -582,6 +608,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
582608 .ptr_sub,
583609 .shr,
584610 .shl,
611 .shl_exact,
612 .shl_sat,
585613 => return air.typeOf(datas[inst].bin_op.lhs),
586614
587615 .cmp_lt,
src/AstGen.zig+97-45
......@@ -317,29 +317,37 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
317317 .assign,
318318 .assign_bit_and,
319319 .assign_bit_or,
320 .assign_bit_shift_left,
321 .assign_bit_shift_right,
320 .assign_shl,
321 .assign_shl_sat,
322 .assign_shr,
322323 .assign_bit_xor,
323324 .assign_div,
324325 .assign_sub,
325326 .assign_sub_wrap,
327 .assign_sub_sat,
326328 .assign_mod,
327329 .assign_add,
328330 .assign_add_wrap,
331 .assign_add_sat,
329332 .assign_mul,
330333 .assign_mul_wrap,
334 .assign_mul_sat,
331335 .add,
332336 .add_wrap,
337 .add_sat,
333338 .sub,
334339 .sub_wrap,
340 .sub_sat,
335341 .mul,
336342 .mul_wrap,
343 .mul_sat,
337344 .div,
338345 .mod,
339346 .bit_and,
340347 .bit_or,
341 .bit_shift_left,
342 .bit_shift_right,
348 .shl,
349 .shl_sat,
350 .shr,
343351 .bit_xor,
344352 .bang_equal,
345353 .equal_equal,
......@@ -522,11 +530,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
522530 return rvalue(gz, rl, .void_value, node);
523531 },
524532
525 .assign_bit_shift_left => {
533 .assign_shl => {
526534 try assignShift(gz, scope, node, .shl);
527535 return rvalue(gz, rl, .void_value, node);
528536 },
529 .assign_bit_shift_right => {
537 .assign_shl_sat => {
538 try assignShiftSat(gz, scope, node);
539 return rvalue(gz, rl, .void_value, node);
540 },
541 .assign_shr => {
530542 try assignShift(gz, scope, node, .shr);
531543 return rvalue(gz, rl, .void_value, node);
532544 },
......@@ -555,6 +567,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
555567 try assignOp(gz, scope, node, .subwrap);
556568 return rvalue(gz, rl, .void_value, node);
557569 },
570 .assign_sub_sat => {
571 try assignOp(gz, scope, node, .sub_sat);
572 return rvalue(gz, rl, .void_value, node);
573 },
558574 .assign_mod => {
559575 try assignOp(gz, scope, node, .mod_rem);
560576 return rvalue(gz, rl, .void_value, node);
......@@ -567,6 +583,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
567583 try assignOp(gz, scope, node, .addwrap);
568584 return rvalue(gz, rl, .void_value, node);
569585 },
586 .assign_add_sat => {
587 try assignOp(gz, scope, node, .add_sat);
588 return rvalue(gz, rl, .void_value, node);
589 },
570590 .assign_mul => {
571591 try assignOp(gz, scope, node, .mul);
572592 return rvalue(gz, rl, .void_value, node);
......@@ -575,19 +595,28 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
575595 try assignOp(gz, scope, node, .mulwrap);
576596 return rvalue(gz, rl, .void_value, node);
577597 },
598 .assign_mul_sat => {
599 try assignOp(gz, scope, node, .mul_sat);
600 return rvalue(gz, rl, .void_value, node);
601 },
578602
579603 // zig fmt: off
580 .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
581 .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
604 .shl => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
605 .shr => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
582606
583607 .add => return simpleBinOp(gz, scope, rl, node, .add),
584608 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
609 .add_sat => return simpleBinOp(gz, scope, rl, node, .add_sat),
585610 .sub => return simpleBinOp(gz, scope, rl, node, .sub),
586611 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
612 .sub_sat => return simpleBinOp(gz, scope, rl, node, .sub_sat),
587613 .mul => return simpleBinOp(gz, scope, rl, node, .mul),
588614 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
615 .mul_sat => return simpleBinOp(gz, scope, rl, node, .mul_sat),
589616 .div => return simpleBinOp(gz, scope, rl, node, .div),
590617 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
618 .shl_sat => return simpleBinOp(gz, scope, rl, node, .shl_sat),
619
591620 .bit_and => {
592621 const current_ampersand_token = main_tokens[node];
593622 if (token_tags[current_ampersand_token + 1] == .ampersand) {
......@@ -1898,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
18981927
18991928 .assign => try assign(gz, scope, statement),
19001929
1901 .assign_bit_shift_left => try assignShift(gz, scope, statement, .shl),
1902 .assign_bit_shift_right => try assignShift(gz, scope, statement, .shr),
1930 .assign_shl => try assignShift(gz, scope, statement, .shl),
1931 .assign_shr => try assignShift(gz, scope, statement, .shr),
19031932
19041933 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
19051934 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),
......@@ -1949,6 +1978,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
19491978 // ZIR instructions that might be a type other than `noreturn` or `void`.
19501979 .add,
19511980 .addwrap,
1981 .add_sat,
19521982 .param,
19531983 .param_comptime,
19541984 .param_anytype,
......@@ -2015,12 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
20152045 .mod_rem,
20162046 .mul,
20172047 .mulwrap,
2048 .mul_sat,
20182049 .ref,
20192050 .shl,
2051 .shl_sat,
20202052 .shr,
20212053 .str,
20222054 .sub,
20232055 .subwrap,
2056 .sub_sat,
20242057 .negate,
20252058 .negate_wrap,
20262059 .typeof,
......@@ -2708,6 +2741,24 @@ fn assignShift(
27082741 _ = try gz.addBin(.store, lhs_ptr, result);
27092742}
27102743
2744fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
2745 try emitDbgNode(gz, infix_node);
2746 const astgen = gz.astgen;
2747 const tree = astgen.tree;
2748 const node_datas = tree.nodes.items(.data);
2749
2750 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
2751 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
2752 // Saturating shift-left allows any integer type for both the LHS and RHS.
2753 const rhs = try expr(gz, scope, .none, node_datas[infix_node].rhs);
2754
2755 const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
2756 .lhs = lhs,
2757 .rhs = rhs,
2758 });
2759 _ = try gz.addBin(.store, lhs_ptr, result);
2760}
2761
27112762fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
27122763 const astgen = gz.astgen;
27132764 const tree = astgen.tree;
......@@ -7483,11 +7534,6 @@ fn builtinCall(
74837534 return rvalue(gz, rl, result, node);
74847535 },
74857536
7486 .add_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .add_with_saturation),
7487 .sub_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .sub_with_saturation),
7488 .mul_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .mul_with_saturation),
7489 .shl_with_saturation => return saturatingArithmetic(gz, scope, rl, node, params, .shl_with_saturation),
7490
74917537 .atomic_load => {
74927538 const int_type = try typeExpr(gz, scope, params[0]);
74937539 // TODO allow this pointer type to be volatile
......@@ -7882,24 +7928,6 @@ fn overflowArithmetic(
78827928 return rvalue(gz, rl, result, node);
78837929}
78847930
7885fn saturatingArithmetic(
7886 gz: *GenZir,
7887 scope: *Scope,
7888 rl: ResultLoc,
7889 node: Ast.Node.Index,
7890 params: []const Ast.Node.Index,
7891 tag: Zir.Inst.Extended,
7892) InnerError!Zir.Inst.Ref {
7893 const lhs = try expr(gz, scope, .none, params[0]);
7894 const rhs = try expr(gz, scope, .none, params[1]);
7895 const result = try gz.addExtendedPayload(tag, Zir.Inst.SaturatingArithmetic{
7896 .node = gz.nodeIndexToRelative(node),
7897 .lhs = lhs,
7898 .rhs = rhs,
7899 });
7900 return rvalue(gz, rl, result, node);
7901}
7902
79037931fn callExpr(
79047932 gz: *GenZir,
79057933 scope: *Scope,
......@@ -8119,27 +8147,33 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
81198147 .asm_simple,
81208148 .add,
81218149 .add_wrap,
8150 .add_sat,
81228151 .array_cat,
81238152 .array_mult,
81248153 .assign,
81258154 .assign_bit_and,
81268155 .assign_bit_or,
8127 .assign_bit_shift_left,
8128 .assign_bit_shift_right,
8156 .assign_shl,
8157 .assign_shl_sat,
8158 .assign_shr,
81298159 .assign_bit_xor,
81308160 .assign_div,
81318161 .assign_sub,
81328162 .assign_sub_wrap,
8163 .assign_sub_sat,
81338164 .assign_mod,
81348165 .assign_add,
81358166 .assign_add_wrap,
8167 .assign_add_sat,
81368168 .assign_mul,
81378169 .assign_mul_wrap,
8170 .assign_mul_sat,
81388171 .bang_equal,
81398172 .bit_and,
81408173 .bit_or,
8141 .bit_shift_left,
8142 .bit_shift_right,
8174 .shl,
8175 .shl_sat,
8176 .shr,
81438177 .bit_xor,
81448178 .bool_and,
81458179 .bool_or,
......@@ -8154,10 +8188,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
81548188 .mod,
81558189 .mul,
81568190 .mul_wrap,
8191 .mul_sat,
81578192 .switch_range,
81588193 .field_access,
81598194 .sub,
81608195 .sub_wrap,
8196 .sub_sat,
81618197 .slice,
81628198 .slice_open,
81638199 .slice_sentinel,
......@@ -8352,27 +8388,33 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
83528388 .tagged_union_enum_tag_trailing,
83538389 .add,
83548390 .add_wrap,
8391 .add_sat,
83558392 .array_cat,
83568393 .array_mult,
83578394 .assign,
83588395 .assign_bit_and,
83598396 .assign_bit_or,
8360 .assign_bit_shift_left,
8361 .assign_bit_shift_right,
8397 .assign_shl,
8398 .assign_shl_sat,
8399 .assign_shr,
83628400 .assign_bit_xor,
83638401 .assign_div,
83648402 .assign_sub,
83658403 .assign_sub_wrap,
8404 .assign_sub_sat,
83668405 .assign_mod,
83678406 .assign_add,
83688407 .assign_add_wrap,
8408 .assign_add_sat,
83698409 .assign_mul,
83708410 .assign_mul_wrap,
8411 .assign_mul_sat,
83718412 .bang_equal,
83728413 .bit_and,
83738414 .bit_or,
8374 .bit_shift_left,
8375 .bit_shift_right,
8415 .shl,
8416 .shl_sat,
8417 .shr,
83768418 .bit_xor,
83778419 .bool_and,
83788420 .bool_or,
......@@ -8387,9 +8429,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
83878429 .mod,
83888430 .mul,
83898431 .mul_wrap,
8432 .mul_sat,
83908433 .switch_range,
83918434 .sub,
83928435 .sub_wrap,
8436 .sub_sat,
83938437 .slice,
83948438 .slice_open,
83958439 .slice_sentinel,
......@@ -8524,27 +8568,33 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
85248568 .asm_simple,
85258569 .add,
85268570 .add_wrap,
8571 .add_sat,
85278572 .array_cat,
85288573 .array_mult,
85298574 .assign,
85308575 .assign_bit_and,
85318576 .assign_bit_or,
8532 .assign_bit_shift_left,
8533 .assign_bit_shift_right,
8577 .assign_shl,
8578 .assign_shl_sat,
8579 .assign_shr,
85348580 .assign_bit_xor,
85358581 .assign_div,
85368582 .assign_sub,
85378583 .assign_sub_wrap,
8584 .assign_sub_sat,
85388585 .assign_mod,
85398586 .assign_add,
85408587 .assign_add_wrap,
8588 .assign_add_sat,
85418589 .assign_mul,
85428590 .assign_mul_wrap,
8591 .assign_mul_sat,
85438592 .bang_equal,
85448593 .bit_and,
85458594 .bit_or,
8546 .bit_shift_left,
8547 .bit_shift_right,
8595 .shl,
8596 .shl_sat,
8597 .shr,
85488598 .bit_xor,
85498599 .bool_and,
85508600 .bool_or,
......@@ -8559,10 +8609,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
85598609 .mod,
85608610 .mul,
85618611 .mul_wrap,
8612 .mul_sat,
85628613 .switch_range,
85638614 .field_access,
85648615 .sub,
85658616 .sub_wrap,
8617 .sub_sat,
85668618 .slice,
85678619 .slice_open,
85688620 .slice_sentinel,
src/BuiltinFn.zig-32
......@@ -2,7 +2,6 @@ const std = @import("std");
22
33pub const Tag = enum {
44 add_with_overflow,
5 add_with_saturation,
65 align_cast,
76 align_of,
87 as,
......@@ -66,7 +65,6 @@ pub const Tag = enum {
6665 wasm_memory_grow,
6766 mod,
6867 mul_with_overflow,
69 mul_with_saturation,
7068 panic,
7169 pop_count,
7270 ptr_cast,
......@@ -81,12 +79,10 @@ pub const Tag = enum {
8179 set_runtime_safety,
8280 shl_exact,
8381 shl_with_overflow,
84 shl_with_saturation,
8582 shr_exact,
8683 shuffle,
8784 size_of,
8885 splat,
89 sub_with_saturation,
9086 reduce,
9187 src,
9288 sqrt,
......@@ -531,34 +527,6 @@ pub const list = list: {
531527 .param_count = 2,
532528 },
533529 },
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 },
562530 .{
563531 "@memcpy",
564532 .{
src/Liveness.zig+5
......@@ -226,10 +226,13 @@ fn analyzeInst(
226226 switch (inst_tags[inst]) {
227227 .add,
228228 .addwrap,
229 .add_sat,
229230 .sub,
230231 .subwrap,
232 .sub_sat,
231233 .mul,
232234 .mulwrap,
235 .mul_sat,
233236 .div,
234237 .rem,
235238 .mod,
......@@ -252,6 +255,8 @@ fn analyzeInst(
252255 .ptr_elem_val,
253256 .ptr_ptr_elem_val,
254257 .shl,
258 .shl_exact,
259 .shl_sat,
255260 .shr,
256261 .atomic_store_unordered,
257262 .atomic_store_monotonic,
src/Sema.zig+113-34
......@@ -246,7 +246,6 @@ pub fn analyzeBody(
246246 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
247247 .ref => try sema.zirRef(block, inst),
248248 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),
249 .shl => try sema.zirShl(block, inst),
250249 .shr => try sema.zirShr(block, inst),
251250 .slice_end => try sema.zirSliceEnd(block, inst),
252251 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
......@@ -319,7 +318,6 @@ pub fn analyzeBody(
319318 .div_exact => try sema.zirDivExact(block, inst),
320319 .div_floor => try sema.zirDivFloor(block, inst),
321320 .div_trunc => try sema.zirDivTrunc(block, inst),
322 .shl_exact => try sema.zirShlExact(block, inst),
323321 .shr_exact => try sema.zirShrExact(block, inst),
324322 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),
325323 .offset_of => try sema.zirOffsetOf(block, inst),
......@@ -363,14 +361,21 @@ pub fn analyzeBody(
363361
364362 .add => try sema.zirArithmetic(block, inst, .add),
365363 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
364 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
366365 .div => try sema.zirArithmetic(block, inst, .div),
367366 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
368367 .mod => try sema.zirArithmetic(block, inst, .mod),
369368 .rem => try sema.zirArithmetic(block, inst, .rem),
370369 .mul => try sema.zirArithmetic(block, inst, .mul),
371370 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
371 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
372372 .sub => try sema.zirArithmetic(block, inst, .sub),
373373 .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
375380 // Instructions that we know to *always* be noreturn based solely on their tag.
376381 // These functions match the return type of analyzeBody so that we can
......@@ -694,10 +699,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
694699 .c_define => return sema.zirCDefine( block, extended),
695700 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
696701 .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),
697 .add_with_saturation=> return sema.zirSatArithmetic( block, extended),
698 .sub_with_saturation=> return sema.zirSatArithmetic( block, extended),
699 .mul_with_saturation=> return sema.zirSatArithmetic( block, extended),
700 .shl_with_saturation=> return sema.zirSatArithmetic( block, extended),
701702 // zig fmt: on
702703 }
703704}
......@@ -5874,7 +5875,12 @@ fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
58745875 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});
58755876}
58765877
5877fn 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 {
58785884 const tracy = trace(@src());
58795885 defer tracy.end();
58805886
......@@ -5885,6 +5891,8 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
58855891 const lhs = sema.resolveInst(extra.lhs);
58865892 const rhs = sema.resolveInst(extra.rhs);
58875893
5894 // TODO coerce rhs if air_tag is not shl_sat
5895
58885896 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
58895897 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);
58905898
......@@ -5900,6 +5908,12 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
59005908 return sema.addConstant(lhs_ty, lhs_val);
59015909 }
59025910 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 }
59035917 return sema.addConstant(lhs_ty, val);
59045918 } else rs: {
59055919 if (maybe_rhs_val) |rhs_val| {
......@@ -5908,8 +5922,10 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
59085922 break :rs lhs_src;
59095923 };
59105924
5925 // TODO: insert runtime safety check for shl_exact
5926
59115927 try sema.requireRuntimeBlock(block, runtime_src);
5912 return block.addBinOp(.shl, lhs, rhs);
5928 return block.addBinOp(air_tag, lhs, rhs);
59135929}
59145930
59155931fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -6200,19 +6216,6 @@ fn zirOverflowArithmetic(
62006216 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});
62016217}
62026218
6203fn zirSatArithmetic(
6204 sema: *Sema,
6205 block: *Scope.Block,
6206 extended: Zir.Inst.Extended.InstData,
6207) CompileError!Air.Inst.Ref {
6208 const tracy = trace(@src());
6209 defer tracy.end();
6210
6211 const extra = sema.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data;
6212 const src: LazySrcLoc = .{ .node_offset = extra.node };
6213 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirSatArithmetic", .{});
6214}
6215
62166219fn analyzeArithmetic(
62176220 sema: *Sema,
62186221 block: *Scope.Block,
......@@ -6354,8 +6357,7 @@ fn analyzeArithmetic(
63546357 },
63556358 .addwrap => {
63566359 // Integers only; floats are checked above.
6357 // If either of the operands are zero, then the other operand is
6358 // returned, even if it is undefined.
6360 // If either of the operands are zero, the other operand is returned.
63596361 // If either of the operands are undefined, the result is undefined.
63606362 if (maybe_lhs_val) |lhs_val| {
63616363 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
......@@ -6377,6 +6379,30 @@ fn analyzeArithmetic(
63776379 } else break :rs .{ .src = lhs_src, .air_tag = .addwrap };
63786380 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
63796381 },
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 },
63806406 .sub => {
63816407 // For integers:
63826408 // If the rhs is zero, then the other operand is
......@@ -6444,6 +6470,30 @@ fn analyzeArithmetic(
64446470 } else break :rs .{ .src = rhs_src, .air_tag = .subwrap };
64456471 } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };
64466472 },
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 },
64476497 .div => {
64486498 // For integers:
64496499 // If the lhs is zero, then zero is returned regardless of rhs.
......@@ -6562,10 +6612,9 @@ fn analyzeArithmetic(
65626612 },
65636613 .mulwrap => {
65646614 // Integers only; floats are handled above.
6565 // If either of the operands are zero, the result is zero.
6566 // If either of the operands are one, the result is the other
6567 // operand, even if it is undefined.
6568 // If either of the operands are undefined, the result is undefined.
6615 // If either of the operands are zero, result is zero.
6616 // If either of the operands are one, result is the other operand.
6617 // If either of the operands are undefined, result is undefined.
65696618 if (maybe_lhs_val) |lhs_val| {
65706619 if (!lhs_val.isUndef()) {
65716620 if (lhs_val.compareWithZero(.eq)) {
......@@ -6597,6 +6646,42 @@ fn analyzeArithmetic(
65976646 } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap };
65986647 } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };
65996648 },
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 },
66006685 .mod_rem => {
66016686 // For integers:
66026687 // Either operand being undef is a compile error because there exists
......@@ -7846,7 +7931,7 @@ fn analyzeRet(
78467931fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
78477932 // extend this swich as additional operators are implemented
78487933 return switch (tag) {
7849 .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,
78507935 else => false,
78517936 };
78527937}
......@@ -8513,12 +8598,6 @@ fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
85138598 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});
85148599}
85158600
8516fn zirShlExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8517 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
8518 const src = inst_data.src();
8519 return sema.mod.fail(&block.base, src, "TODO: Sema.zirShlExact", .{});
8520}
8521
85228601fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
85238602 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
85248603 const src = inst_data.src();
src/Zir.zig+71-72
......@@ -126,6 +126,64 @@ pub const Inst = struct {
126126 /// Twos complement wrapping integer addition.
127127 /// Uses the `pl_node` union field. Payload is `Bin`.
128128 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
129187 /// Declares a parameter of the current function. Used for:
130188 /// * debug info
131189 /// * checking shadowing against declarations in the current namespace
......@@ -471,12 +529,6 @@ pub const Inst = struct {
471529 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
472530 /// Uses the `str` union field.
473531 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,
480532 /// Arithmetic negation. Asserts no integer overflow.
481533 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
482534 /// Uses `un_node`.
......@@ -802,46 +854,6 @@ pub const Inst = struct {
802854 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.
803855 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
845857 /// Implements the `@bitOffsetOf` builtin.
846858 /// Uses the `pl_node` union field with payload `Bin`.
847859 bit_offset_of,
......@@ -961,6 +973,7 @@ pub const Inst = struct {
961973 .param_anytype_comptime,
962974 .add,
963975 .addwrap,
976 .add_sat,
964977 .alloc,
965978 .alloc_mut,
966979 .alloc_comptime,
......@@ -1035,8 +1048,10 @@ pub const Inst = struct {
10351048 .mod_rem,
10361049 .mul,
10371050 .mulwrap,
1051 .mul_sat,
10381052 .ref,
10391053 .shl,
1054 .shl_sat,
10401055 .shr,
10411056 .store,
10421057 .store_node,
......@@ -1045,6 +1060,7 @@ pub const Inst = struct {
10451060 .str,
10461061 .sub,
10471062 .subwrap,
1063 .sub_sat,
10481064 .negate,
10491065 .negate_wrap,
10501066 .typeof,
......@@ -1218,6 +1234,14 @@ pub const Inst = struct {
12181234 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
12191235 .add = .pl_node,
12201236 .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
12211245 .param = .pl_tok,
12221246 .param_comptime = .pl_tok,
12231247 .param_anytype = .str_tok,
......@@ -1297,8 +1321,6 @@ pub const Inst = struct {
12971321 .repeat_inline = .node,
12981322 .merge_error_sets = .pl_node,
12991323 .mod_rem = .pl_node,
1300 .mul = .pl_node,
1301 .mulwrap = .pl_node,
13021324 .ref = .un_tok,
13031325 .ret_node = .un_node,
13041326 .ret_load = .un_node,
......@@ -1315,8 +1337,6 @@ pub const Inst = struct {
13151337 .store_to_block_ptr = .bin,
13161338 .store_to_inferred_ptr = .bin,
13171339 .str = .str,
1318 .sub = .pl_node,
1319 .subwrap = .pl_node,
13201340 .negate = .un_node,
13211341 .negate_wrap = .un_node,
13221342 .typeof = .un_node,
......@@ -1437,6 +1457,7 @@ pub const Inst = struct {
14371457
14381458 .shl = .pl_node,
14391459 .shl_exact = .pl_node,
1460 .shl_sat = .pl_node,
14401461 .shr = .pl_node,
14411462 .shr_exact = .pl_node,
14421463
......@@ -1593,22 +1614,6 @@ pub const Inst = struct {
15931614 wasm_memory_size,
15941615 /// `operand` is payload index to `BinNode`.
15951616 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
16131618 pub const InstData = struct {
16141619 opcode: Extended,
......@@ -2788,12 +2793,6 @@ pub const Inst = struct {
27882793 ptr: Ref,
27892794 };
27902795
2791 pub const SaturatingArithmetic = struct {
2792 node: i32,
2793 lhs: Ref,
2794 rhs: Ref,
2795 };
2796
27972796 pub const Cmpxchg = struct {
27982797 ptr: Ref,
27992798 expected_value: Ref,
src/codegen.zig+46-10
......@@ -824,15 +824,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
824824
825825 switch (air_tags[inst]) {
826826 // zig fmt: off
827 .add, .ptr_add => try self.airAdd(inst),
828 .addwrap => try self.airAddWrap(inst),
829 .sub, .ptr_sub => try self.airSub(inst),
830 .subwrap => try self.airSubWrap(inst),
831 .mul => try self.airMul(inst),
832 .mulwrap => try self.airMulWrap(inst),
833 .div => try self.airDiv(inst),
834 .rem => try self.airRem(inst),
835 .mod => try self.airMod(inst),
827 .add, .ptr_add => try self.airAdd(inst),
828 .addwrap => try self.airAddWrap(inst),
829 .add_sat => try self.airAddSat(inst),
830 .sub, .ptr_sub => try self.airSub(inst),
831 .subwrap => try self.airSubWrap(inst),
832 .sub_sat => try self.airSubSat(inst),
833 .mul => try self.airMul(inst),
834 .mulwrap => try self.airMulWrap(inst),
835 .mul_sat => try self.airMulSat(inst),
836 .div => try self.airDiv(inst),
837 .rem => try self.airRem(inst),
838 .mod => try self.airMod(inst),
839 .shl, .shl_exact => try self.airShl(inst),
840 .shl_sat => try self.airShlSat(inst),
836841
837842 .cmp_lt => try self.airCmp(inst, .lt),
838843 .cmp_lte => try self.airCmp(inst, .lte),
......@@ -847,7 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
847852 .bit_or => try self.airBitOr(inst),
848853 .xor => try self.airXor(inst),
849854 .shr => try self.airShr(inst),
850 .shl => try self.airShl(inst),
851855
852856 .alloc => try self.airAlloc(inst),
853857 .arg => try self.airArg(inst),
......@@ -1302,6 +1306,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13021306 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13031307 }
13041308
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
13051317 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
13061318 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13071319 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
......@@ -1320,6 +1332,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13201332 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13211333 }
13221334
1335 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
1336 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1337 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
1338 else => return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}),
1339 };
1340 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1341 }
1342
13231343 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
13241344 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13251345 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
......@@ -1338,6 +1358,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
13381358 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13391359 }
13401360
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
13411369 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
13421370 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13431371 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
......@@ -1400,6 +1428,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14001428 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
14011429 }
14021430
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
14031439 fn airShr(self: *Self, inst: Air.Inst.Index) !void {
14041440 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
14051441 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
src/codegen/c.zig+129-18
......@@ -883,22 +883,27 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
883883
884884 // TODO use a different strategy for add that communicates to the optimizer
885885 // that wrapping is UB.
886 .add, .ptr_add => try airBinOp( f, inst, " + "),
887 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
886 .add, .ptr_add => try airBinOp (f, inst, " + "),
888887 // TODO use a different strategy for sub that communicates to the optimizer
889888 // that wrapping is UB.
890 .sub, .ptr_sub => try airBinOp( f, inst, " - "),
891 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
889 .sub, .ptr_sub => try airBinOp (f, inst, " - "),
892890 // TODO use a different strategy for mul that communicates to the optimizer
893891 // that wrapping is UB.
894 .mul => try airBinOp( f, inst, " * "),
895 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
892 .mul => try airBinOp (f, inst, " * "),
896893 // TODO use a different strategy for div that communicates to the optimizer
897894 // that wrapping is UB.
898895 .div => try airBinOp( f, inst, " / "),
899896 .rem => try airBinOp( f, inst, " % "),
900 // TODO implement modulus division
901 .mod => try airBinOp( f, inst, " mod "),
897 .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
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_"),
902907
903908 .cmp_eq => try airBinOp(f, inst, " == "),
904909 .cmp_gt => try airBinOp(f, inst, " > "),
......@@ -908,16 +913,14 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
908913 .cmp_neq => try airBinOp(f, inst, " != "),
909914
910915 // bool_and and bool_or are non-short-circuit operations
911 .bool_and => try airBinOp(f, inst, " & "),
912 .bool_or => try airBinOp(f, inst, " | "),
913 .bit_and => try airBinOp(f, inst, " & "),
914 .bit_or => try airBinOp(f, inst, " | "),
915 .xor => try airBinOp(f, inst, " ^ "),
916
917 .shr => try airBinOp(f, inst, " >> "),
918 .shl => try airBinOp(f, inst, " << "),
919
920 .not => try airNot( f, inst),
916 .bool_and => try airBinOp(f, inst, " & "),
917 .bool_or => try airBinOp(f, inst, " | "),
918 .bit_and => try airBinOp(f, inst, " & "),
919 .bit_or => try airBinOp(f, inst, " | "),
920 .xor => try airBinOp(f, inst, " ^ "),
921 .shr => try airBinOp(f, inst, " >> "),
922 .shl, .shl_exact => try airBinOp(f, inst, " << "),
923 .not => try airNot (f, inst),
921924
922925 .optional_payload => try airOptionalPayload(f, inst),
923926 .optional_payload_ptr => try airOptionalPayload(f, inst),
......@@ -1309,6 +1312,114 @@ fn airWrapOp(
13091312 return ret;
13101313}
13111314
1315fn airSatOp(f: *Function, inst: Air.Inst.Index, fn_op: [*:0]const u8) !CValue {
1316 if (f.liveness.isUnused(inst))
1317 return CValue.none;
1318
1319 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1320 const inst_ty = f.air.typeOfIndex(inst);
1321 const int_info = inst_ty.intInfo(f.object.dg.module.getTarget());
1322 const bits = int_info.bits;
1323
1324 switch (bits) {
1325 8, 16, 32, 64, 128 => {},
1326 else => return f.object.dg.fail("TODO: C backend: airSatOp for non power of 2 integers", .{}),
1327 }
1328
1329 // if it's an unsigned int with non-arbitrary bit size then we can just add
1330 if (bits > 64) {
1331 return f.object.dg.fail("TODO: C backend: airSatOp for large integers", .{});
1332 }
1333
1334 var min_buf: [80]u8 = undefined;
1335 const min = switch (int_info.signedness) {
1336 .unsigned => "0",
1337 else => switch (inst_ty.tag()) {
1338 .c_short => "SHRT_MIN",
1339 .c_int => "INT_MIN",
1340 .c_long => "LONG_MIN",
1341 .c_longlong => "LLONG_MIN",
1342 .isize => "INTPTR_MIN",
1343 else => blk: {
1344 // compute the type minimum based on the bitcount (bits)
1345 const val = -1 * std.math.pow(i65, 2, @intCast(i65, bits - 1));
1346 break :blk std.fmt.bufPrint(&min_buf, "{d}", .{val}) catch |err| switch (err) {
1347 error.NoSpaceLeft => unreachable,
1348 else => |e| return e,
1349 };
1350 },
1351 },
1352 };
1353
1354 var max_buf: [80]u8 = undefined;
1355 const max = switch (inst_ty.tag()) {
1356 .c_short => "SHRT_MAX",
1357 .c_ushort => "USHRT_MAX",
1358 .c_int => "INT_MAX",
1359 .c_uint => "UINT_MAX",
1360 .c_long => "LONG_MAX",
1361 .c_ulong => "ULONG_MAX",
1362 .c_longlong => "LLONG_MAX",
1363 .c_ulonglong => "ULLONG_MAX",
1364 .isize => "INTPTR_MAX",
1365 .usize => "UINTPTR_MAX",
1366 else => blk: {
1367 const pow_bits = switch (int_info.signedness) {
1368 .signed => bits - 1,
1369 .unsigned => bits,
1370 };
1371 const val = std.math.pow(u65, 2, pow_bits) - 1;
1372 break :blk std.fmt.bufPrint(&max_buf, "{}", .{val}) catch |err| switch (err) {
1373 error.NoSpaceLeft => unreachable,
1374 else => |e| return e,
1375 };
1376 },
1377 };
1378
1379 const lhs = try f.resolveInst(bin_op.lhs);
1380 const rhs = try f.resolveInst(bin_op.rhs);
1381 const w = f.object.writer();
1382
1383 const ret = try f.allocLocal(inst_ty, .Mut);
1384 try w.print(" = zig_{s}", .{fn_op});
1385
1386 switch (inst_ty.tag()) {
1387 .isize => try w.writeAll("isize"),
1388 .c_short => try w.writeAll("short"),
1389 .c_int => try w.writeAll("int"),
1390 .c_long => try w.writeAll("long"),
1391 .c_longlong => try w.writeAll("longlong"),
1392 else => {
1393 const prefix_byte: u8 = switch (int_info.signedness) {
1394 .signed => 'i',
1395 .unsigned => 'u',
1396 };
1397 for ([_]u8{ 8, 16, 32, 64 }) |nbits| {
1398 if (bits <= nbits) {
1399 try w.print("{c}{d}", .{ prefix_byte, nbits });
1400 break;
1401 }
1402 } else {
1403 unreachable;
1404 }
1405 },
1406 }
1407
1408 try w.writeByte('(');
1409 try f.writeCValue(w, lhs);
1410 try w.writeAll(", ");
1411 try f.writeCValue(w, rhs);
1412
1413 if (int_info.signedness == .signed) {
1414 try w.print(", {s}", .{min});
1415 }
1416
1417 try w.print(", {s});", .{max});
1418 try f.object.indent_writer.insertNewline();
1419
1420 return ret;
1421}
1422
13121423fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
13131424 if (f.liveness.isUnused(inst))
13141425 return CValue.none;
src/codegen/llvm.zig+130-30
......@@ -1236,23 +1236,27 @@ pub const FuncGen = struct {
12361236 for (body) |inst| {
12371237 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
12381238 // zig fmt: off
1239 .add => try self.airAdd(inst, false),
1240 .addwrap => try self.airAdd(inst, true),
1241 .sub => try self.airSub(inst, false),
1242 .subwrap => try self.airSub(inst, true),
1243 .mul => try self.airMul(inst, false),
1244 .mulwrap => try self.airMul(inst, true),
1245 .div => try self.airDiv(inst),
1246 .rem => try self.airRem(inst),
1247 .mod => try self.airMod(inst),
1248 .ptr_add => try self.airPtrAdd(inst),
1249 .ptr_sub => try self.airPtrSub(inst),
1239 .add => try self.airAdd(inst),
1240 .addwrap => try self.airAddWrap(inst),
1241 .add_sat => try self.airAddSat(inst),
1242 .sub => try self.airSub(inst),
1243 .subwrap => try self.airSubWrap(inst),
1244 .sub_sat => try self.airSubSat(inst),
1245 .mul => try self.airMul(inst),
1246 .mulwrap => try self.airMulWrap(inst),
1247 .mul_sat => try self.airMulSat(inst),
1248 .div => try self.airDiv(inst),
1249 .rem => try self.airRem(inst),
1250 .mod => try self.airMod(inst),
1251 .ptr_add => try self.airPtrAdd(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),
12501256
12511257 .bit_and, .bool_and => try self.airAnd(inst),
12521258 .bit_or, .bool_or => try self.airOr(inst),
12531259 .xor => try self.airXor(inst),
1254
1255 .shl => try self.airShl(inst),
12561260 .shr => try self.airShr(inst),
12571261
12581262 .cmp_eq => try self.airCmp(inst, .eq),
......@@ -2024,51 +2028,115 @@ pub const FuncGen = struct {
20242028 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
20252029 }
20262030
2027 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
2028 if (self.liveness.isUnused(inst))
2029 return null;
2031 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2032 if (self.liveness.isUnused(inst)) return null;
20302033
20312034 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
20322035 const lhs = try self.resolveInst(bin_op.lhs);
20332036 const rhs = try self.resolveInst(bin_op.rhs);
20342037 const inst_ty = self.air.typeOfIndex(inst);
20352038
2036 if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, "");
2037 if (wrap) return self.builder.buildAdd(lhs, rhs, "");
2039 if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
20382040 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
20392041 return self.builder.buildNUWAdd(lhs, rhs, "");
20402042 }
20412043
2042 fn airSub(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
2043 if (self.liveness.isUnused(inst))
2044 return null;
2044 fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2045 if (self.liveness.isUnused(inst)) 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;
20452070
20462071 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
20472072 const lhs = try self.resolveInst(bin_op.lhs);
20482073 const rhs = try self.resolveInst(bin_op.rhs);
20492074 const inst_ty = self.air.typeOfIndex(inst);
20502075
2051 if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, "");
2052 if (wrap) return self.builder.buildSub(lhs, rhs, "");
2076 if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
20532077 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
20542078 return self.builder.buildNUWSub(lhs, rhs, "");
20552079 }
20562080
2057 fn airMul(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {
2058 if (self.liveness.isUnused(inst))
2059 return null;
2081 fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2082 if (self.liveness.isUnused(inst)) 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;
20602093
20612094 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
20622095 const lhs = try self.resolveInst(bin_op.lhs);
20632096 const rhs = try self.resolveInst(bin_op.rhs);
20642097 const inst_ty = self.air.typeOfIndex(inst);
20652098
2066 if (inst_ty.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, "");
2067 if (wrap) return self.builder.buildMul(lhs, rhs, "");
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;
2106
2107 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2108 const lhs = try self.resolveInst(bin_op.lhs);
2109 const rhs = try self.resolveInst(bin_op.rhs);
2110 const inst_ty = self.air.typeOfIndex(inst);
2111
2112 if (inst_ty.isAnyFloat()) return self.builder.buildFMul(lhs, rhs, "");
20682113 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
20692114 return self.builder.buildNUWMul(lhs, rhs, "");
20702115 }
20712116
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
20722140 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
20732141 if (self.liveness.isUnused(inst))
20742142 return null;
......@@ -2174,9 +2242,25 @@ pub const FuncGen = struct {
21742242 return self.builder.buildXor(lhs, rhs, "");
21752243 }
21762244
2245 fn airShlExact(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2246 if (self.liveness.isUnused(inst)) 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
21772261 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2178 if (self.liveness.isUnused(inst))
2179 return null;
2262 if (self.liveness.isUnused(inst)) return null;
2263
21802264 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
21812265 const lhs = try self.resolveInst(bin_op.lhs);
21822266 const rhs = try self.resolveInst(bin_op.rhs);
......@@ -2189,6 +2273,22 @@ pub const FuncGen = struct {
21892273 return self.builder.buildShl(lhs, casted_rhs, "");
21902274 }
21912275
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
21922292 fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
21932293 if (self.liveness.isUnused(inst))
21942294 return null;
src/codegen/llvm/bindings.zig+30
......@@ -397,6 +397,12 @@ pub const Builder = opaque {
397397 pub const buildNUWAdd = LLVMBuildNUWAdd;
398398 extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
399399
400 pub const buildSAddSat = ZigLLVMBuildSAddSat;
401 extern fn ZigLLVMBuildSAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
402
403 pub const buildUAddSat = ZigLLVMBuildUAddSat;
404 extern fn ZigLLVMBuildUAddSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
405
400406 pub const buildFSub = LLVMBuildFSub;
401407 extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
402408
......@@ -409,6 +415,12 @@ pub const Builder = opaque {
409415 pub const buildNUWSub = LLVMBuildNUWSub;
410416 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
411417
418 pub const buildSSubSat = ZigLLVMBuildSSubSat;
419 extern fn ZigLLVMBuildSSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
420
421 pub const buildUSubSat = ZigLLVMBuildUSubSat;
422 extern fn ZigLLVMBuildUSubSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
423
412424 pub const buildFMul = LLVMBuildFMul;
413425 extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
414426
......@@ -421,6 +433,12 @@ pub const Builder = opaque {
421433 pub const buildNUWMul = LLVMBuildNUWMul;
422434 extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
423435
436 pub const buildSMulFixSat = ZigLLVMBuildSMulFixSat;
437 extern fn ZigLLVMBuildSMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
438
439 pub const buildUMulFixSat = ZigLLVMBuildUMulFixSat;
440 extern fn ZigLLVMBuildUMulFixSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
441
424442 pub const buildUDiv = LLVMBuildUDiv;
425443 extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
426444
......@@ -451,6 +469,18 @@ pub const Builder = opaque {
451469 pub const buildShl = LLVMBuildShl;
452470 extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
453471
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
478 pub const buildSShlSat = ZigLLVMBuildSShlSat;
479 extern fn ZigLLVMBuildSShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
480
481 pub const buildUShlSat = ZigLLVMBuildUShlSat;
482 extern fn ZigLLVMBuildUShlSat(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
483
454484 pub const buildOr = LLVMBuildOr;
455485 extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
456486
src/link/C/zig.h+94
......@@ -356,3 +356,97 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon
356356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
357357}
358358
359#define zig_add_sat_u(ZT, T) static inline T zig_adds_##ZT(T x, T y, T max) { \
360 return (x > max - y) ? max : x + y; \
361}
362
363#define zig_add_sat_s(ZT, T, T2) static inline T zig_adds_##ZT(T2 x, T2 y, T2 min, T2 max) { \
364 T2 res = x + y; \
365 return (res < min) ? min : (res > max) ? max : res; \
366}
367
368zig_add_sat_u( u8, uint8_t)
369zig_add_sat_s( i8, int8_t, int16_t)
370zig_add_sat_u(u16, uint16_t)
371zig_add_sat_s(i16, int16_t, int32_t)
372zig_add_sat_u(u32, uint32_t)
373zig_add_sat_s(i32, int32_t, int64_t)
374zig_add_sat_u(u64, uint64_t)
375zig_add_sat_s(i64, int64_t, int128_t)
376zig_add_sat_s(isize, intptr_t, int128_t)
377zig_add_sat_s(short, short, int)
378zig_add_sat_s(int, int, long)
379zig_add_sat_s(long, long, long long)
380
381#define zig_sub_sat_u(ZT, T) static inline T zig_subs_##ZT(T x, T y, T max) { \
382 return (x > max + y) ? max : x - y; \
383}
384
385#define zig_sub_sat_s(ZT, T, T2) static inline T zig_subs_##ZT(T2 x, T2 y, T2 min, T2 max) { \
386 T2 res = x - y; \
387 return (res < min) ? min : (res > max) ? max : res; \
388}
389
390zig_sub_sat_u( u8, uint8_t)
391zig_sub_sat_s( i8, int8_t, int16_t)
392zig_sub_sat_u(u16, uint16_t)
393zig_sub_sat_s(i16, int16_t, int32_t)
394zig_sub_sat_u(u32, uint32_t)
395zig_sub_sat_s(i32, int32_t, int64_t)
396zig_sub_sat_u(u64, uint64_t)
397zig_sub_sat_s(i64, int64_t, int128_t)
398zig_sub_sat_s(isize, intptr_t, int128_t)
399zig_sub_sat_s(short, short, int)
400zig_sub_sat_s(int, int, long)
401zig_sub_sat_s(long, long, long long)
402
403
404#define zig_mul_sat_u(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 max) { \
405 T2 res = x * y; \
406 return (res > max) ? max : res; \
407}
408
409#define zig_mul_sat_s(ZT, T, T2) static inline T zig_muls_##ZT(T2 x, T2 y, T2 min, T2 max) { \
410 T2 res = x * y; \
411 return (res < min) ? min : (res > max) ? max : res; \
412}
413
414zig_mul_sat_u(u8, uint8_t, uint16_t)
415zig_mul_sat_s(i8, int8_t, int16_t)
416zig_mul_sat_u(u16, uint16_t, uint32_t)
417zig_mul_sat_s(i16, int16_t, int32_t)
418zig_mul_sat_u(u32, uint32_t, uint64_t)
419zig_mul_sat_s(i32, int32_t, int64_t)
420zig_mul_sat_u(u64, uint64_t, uint128_t)
421zig_mul_sat_s(i64, int64_t, int128_t)
422zig_mul_sat_s(isize, intptr_t, int128_t)
423zig_mul_sat_s(short, short, int)
424zig_mul_sat_s(int, int, long)
425zig_mul_sat_s(long, long, long long)
426
427#define zig_shl_sat_u(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T max) { \
428 if(x == 0) return 0; \
429 T bits_set = 64 - __builtin_clzll(x); \
430 return (bits_set + y > bits) ? max : x << y; \
431}
432
433#define zig_shl_sat_s(ZT, T, bits) static inline T zig_shls_##ZT(T x, T y, T min, T max) { \
434 if(x == 0) return 0; \
435 T x_twos_comp = x < 0 ? -x : x; \
436 T bits_set = 64 - __builtin_clzll(x_twos_comp); \
437 T min_or_max = (x < 0) ? min : max; \
438 return (y + bits_set > bits ) ? min_or_max : x << y; \
439}
440
441zig_shl_sat_u(u8, uint8_t, 8)
442zig_shl_sat_s(i8, int8_t, 7)
443zig_shl_sat_u(u16, uint16_t, 16)
444zig_shl_sat_s(i16, int16_t, 15)
445zig_shl_sat_u(u32, uint32_t, 32)
446zig_shl_sat_s(i32, int32_t, 31)
447zig_shl_sat_u(u64, uint64_t, 64)
448zig_shl_sat_s(i64, int64_t, 63)
449zig_shl_sat_s(isize, intptr_t, ((sizeof(intptr_t)) * CHAR_BIT - 1))
450zig_shl_sat_s(short, short, ((sizeof(short )) * CHAR_BIT - 1))
451zig_shl_sat_s(int, int, ((sizeof(int )) * CHAR_BIT - 1))
452zig_shl_sat_s(long, long, ((sizeof(long )) * CHAR_BIT - 1))
src/print_air.zig+5
......@@ -104,10 +104,13 @@ const Writer = struct {
104104
105105 .add,
106106 .addwrap,
107 .add_sat,
107108 .sub,
108109 .subwrap,
110 .sub_sat,
109111 .mul,
110112 .mulwrap,
113 .mul_sat,
111114 .div,
112115 .rem,
113116 .mod,
......@@ -130,6 +133,8 @@ const Writer = struct {
130133 .ptr_elem_val,
131134 .ptr_ptr_elem_val,
132135 .shl,
136 .shl_exact,
137 .shl_sat,
133138 .shr,
134139 .set_union_tag,
135140 => try w.writeBinOp(s, inst),
src/print_zir.zig+4-18
......@@ -229,12 +229,15 @@ const Writer = struct {
229229
230230 .add,
231231 .addwrap,
232 .add_sat,
232233 .array_cat,
233234 .array_mul,
234235 .mul,
235236 .mulwrap,
237 .mul_sat,
236238 .sub,
237239 .subwrap,
240 .sub_sat,
238241 .cmp_lt,
239242 .cmp_lte,
240243 .cmp_eq,
......@@ -247,6 +250,7 @@ const Writer = struct {
247250 .mod_rem,
248251 .shl,
249252 .shl_exact,
253 .shl_sat,
250254 .shr,
251255 .shr_exact,
252256 .xor,
......@@ -400,12 +404,6 @@ const Writer = struct {
400404 .shl_with_overflow,
401405 => 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
409407 .struct_decl => try self.writeStructDecl(stream, extended),
410408 .union_decl => try self.writeUnionDecl(stream, extended),
411409 .enum_decl => try self.writeEnumDecl(stream, extended),
......@@ -854,18 +852,6 @@ const Writer = struct {
854852 try self.writeSrc(stream, src);
855853 }
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
869855 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
870856 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
871857 const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index);
src/stage1/all_types.hpp+12-8
......@@ -812,14 +812,18 @@ enum BinOpType {
812812 BinOpTypeInvalid,
813813 BinOpTypeAssign,
814814 BinOpTypeAssignTimes,
815 BinOpTypeAssignTimesSat,
815816 BinOpTypeAssignTimesWrap,
816817 BinOpTypeAssignDiv,
817818 BinOpTypeAssignMod,
818819 BinOpTypeAssignPlus,
820 BinOpTypeAssignPlusSat,
819821 BinOpTypeAssignPlusWrap,
820822 BinOpTypeAssignMinus,
823 BinOpTypeAssignMinusSat,
821824 BinOpTypeAssignMinusWrap,
822825 BinOpTypeAssignBitShiftLeft,
826 BinOpTypeAssignBitShiftLeftSat,
823827 BinOpTypeAssignBitShiftRight,
824828 BinOpTypeAssignBitAnd,
825829 BinOpTypeAssignBitXor,
......@@ -836,12 +840,16 @@ enum BinOpType {
836840 BinOpTypeBinXor,
837841 BinOpTypeBinAnd,
838842 BinOpTypeBitShiftLeft,
843 BinOpTypeBitShiftLeftSat,
839844 BinOpTypeBitShiftRight,
840845 BinOpTypeAdd,
846 BinOpTypeAddSat,
841847 BinOpTypeAddWrap,
842848 BinOpTypeSub,
849 BinOpTypeSubSat,
843850 BinOpTypeSubWrap,
844851 BinOpTypeMult,
852 BinOpTypeMultSat,
845853 BinOpTypeMultWrap,
846854 BinOpTypeDiv,
847855 BinOpTypeMod,
......@@ -1810,10 +1818,6 @@ enum BuiltinFnId {
18101818 BuiltinFnIdReduce,
18111819 BuiltinFnIdMaximum,
18121820 BuiltinFnIdMinimum,
1813 BuiltinFnIdSatAdd,
1814 BuiltinFnIdSatSub,
1815 BuiltinFnIdSatMul,
1816 BuiltinFnIdSatShl,
18171821};
18181822
18191823struct BuiltinFnEntry {
......@@ -2958,10 +2962,10 @@ enum IrBinOp {
29582962 IrBinOpArrayMult,
29592963 IrBinOpMaximum,
29602964 IrBinOpMinimum,
2961 IrBinOpSatAdd,
2962 IrBinOpSatSub,
2963 IrBinOpSatMul,
2964 IrBinOpSatShl,
2965 IrBinOpAddSat,
2966 IrBinOpSubSat,
2967 IrBinOpMultSat,
2968 IrBinOpShlSat,
29652969};
29662970
29672971struct Stage1ZirInstBinOp {
src/stage1/astgen.cpp+16-60
......@@ -3672,6 +3672,8 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
36723672 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc);
36733673 case BinOpTypeAssignTimesWrap:
36743674 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultWrap), lval, result_loc);
3675 case BinOpTypeAssignTimesSat:
3676 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultSat), lval, result_loc);
36753677 case BinOpTypeAssignDiv:
36763678 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
36773679 case BinOpTypeAssignMod:
......@@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
36803682 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc);
36813683 case BinOpTypeAssignPlusWrap:
36823684 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddWrap), lval, result_loc);
3685 case BinOpTypeAssignPlusSat:
3686 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddSat), lval, result_loc);
36833687 case BinOpTypeAssignMinus:
36843688 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc);
36853689 case BinOpTypeAssignMinusWrap:
36863690 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubWrap), lval, result_loc);
3691 case BinOpTypeAssignMinusSat:
3692 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubSat), lval, result_loc);
36873693 case BinOpTypeAssignBitShiftLeft:
36883694 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
3695 case BinOpTypeAssignBitShiftLeftSat:
3696 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpShlSat), lval, result_loc);
36893697 case BinOpTypeAssignBitShiftRight:
36903698 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
36913699 case BinOpTypeAssignBitAnd:
......@@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
37183726 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc);
37193727 case BinOpTypeBitShiftLeft:
37203728 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);
3729 case BinOpTypeBitShiftLeftSat:
3730 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpShlSat), lval, result_loc);
37213731 case BinOpTypeBitShiftRight:
37223732 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
37233733 case BinOpTypeAdd:
37243734 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc);
37253735 case BinOpTypeAddWrap:
37263736 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddWrap), lval, result_loc);
3737 case BinOpTypeAddSat:
3738 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddSat), lval, result_loc);
37273739 case BinOpTypeSub:
37283740 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc);
37293741 case BinOpTypeSubWrap:
37303742 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubWrap), lval, result_loc);
3743 case BinOpTypeSubSat:
3744 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubSat), lval, result_loc);
37313745 case BinOpTypeMult:
37323746 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc);
37333747 case BinOpTypeMultWrap:
37343748 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultWrap), lval, result_loc);
3749 case BinOpTypeMultSat:
3750 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultSat), lval, result_loc);
37353751 case BinOpTypeDiv:
37363752 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
37373753 case BinOpTypeMod:
......@@ -4704,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
47044720 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);
47054721 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
47064722 }
4707 case BuiltinFnIdSatAdd:
4708 {
4709 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4710 Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4711 if (arg0_value == ag->codegen->invalid_inst_src)
4712 return arg0_value;
4713
4714 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4715 Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4716 if (arg1_value == ag->codegen->invalid_inst_src)
4717 return arg1_value;
4718
4719 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatAdd, arg0_value, arg1_value, true);
4720 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4721 }
4722 case BuiltinFnIdSatSub:
4723 {
4724 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4725 Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4726 if (arg0_value == ag->codegen->invalid_inst_src)
4727 return arg0_value;
4728
4729 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4730 Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4731 if (arg1_value == ag->codegen->invalid_inst_src)
4732 return arg1_value;
4733
4734 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatSub, arg0_value, arg1_value, true);
4735 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4736 }
4737 case BuiltinFnIdSatMul:
4738 {
4739 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4740 Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4741 if (arg0_value == ag->codegen->invalid_inst_src)
4742 return arg0_value;
4743
4744 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4745 Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4746 if (arg1_value == ag->codegen->invalid_inst_src)
4747 return arg1_value;
4748
4749 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatMul, arg0_value, arg1_value, true);
4750 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4751 }
4752 case BuiltinFnIdSatShl:
4753 {
4754 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4755 Stage1ZirInst *arg0_value = astgen_node(ag, arg0_node, scope);
4756 if (arg0_value == ag->codegen->invalid_inst_src)
4757 return arg0_value;
4758
4759 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4760 Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
4761 if (arg1_value == ag->codegen->invalid_inst_src)
4762 return arg1_value;
4763
4764 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpSatShl, arg0_value, arg1_value, true);
4765 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4766 }
47674723 case BuiltinFnIdMemcpy:
47684724 {
47694725 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
src/stage1/codegen.cpp+4-8
......@@ -3333,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33333333 } else {
33343334 zig_unreachable();
33353335 }
3336 case IrBinOpSatAdd:
3336 case IrBinOpAddSat:
33373337 if (scalar_type->id == ZigTypeIdInt) {
33383338 if (scalar_type->data.integral.is_signed) {
33393339 return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, "");
......@@ -3343,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33433343 } else {
33443344 zig_unreachable();
33453345 }
3346 case IrBinOpSatSub:
3346 case IrBinOpSubSat:
33473347 if (scalar_type->id == ZigTypeIdInt) {
33483348 if (scalar_type->data.integral.is_signed) {
33493349 return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, "");
......@@ -3353,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33533353 } else {
33543354 zig_unreachable();
33553355 }
3356 case IrBinOpSatMul:
3356 case IrBinOpMultSat:
33573357 if (scalar_type->id == ZigTypeIdInt) {
33583358 if (scalar_type->data.integral.is_signed) {
33593359 return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, "");
......@@ -3363,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
33633363 } else {
33643364 zig_unreachable();
33653365 }
3366 case IrBinOpSatShl:
3366 case IrBinOpShlSat:
33673367 if (scalar_type->id == ZigTypeIdInt) {
33683368 if (scalar_type->data.integral.is_signed) {
33693369 return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "");
......@@ -9134,10 +9134,6 @@ static void define_builtin_fns(CodeGen *g) {
91349134 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
91359135 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
91369136 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);
91419137}
91429138
91439139static const char *bool_to_str(bool b) {
src/stage1/ir.cpp+12-12
......@@ -9820,28 +9820,28 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
98209820 float_min(out_val, op1_val, op2_val);
98219821 }
98229822 break;
9823 case IrBinOpSatAdd:
9823 case IrBinOpAddSat:
98249824 if (is_int) {
98259825 bigint_add_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
98269826 } else {
98279827 zig_unreachable();
98289828 }
98299829 break;
9830 case IrBinOpSatSub:
9830 case IrBinOpSubSat:
98319831 if (is_int) {
98329832 bigint_sub_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
98339833 } else {
98349834 zig_unreachable();
98359835 }
98369836 break;
9837 case IrBinOpSatMul:
9837 case IrBinOpMultSat:
98389838 if (is_int) {
98399839 bigint_mul_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
98409840 } else {
98419841 zig_unreachable();
98429842 }
98439843 break;
9844 case IrBinOpSatShl:
9844 case IrBinOpShlSat:
98459845 if (is_int) {
98469846 bigint_shl_sat(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, type_entry->data.integral.bit_count, type_entry->data.integral.is_signed);
98479847 } else {
......@@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) {
1006910069 case IrBinOpBitShiftRightExact:
1007010070 case IrBinOpAddWrap:
1007110071 case IrBinOpSubWrap:
10072 case IrBinOpSatAdd:
10073 case IrBinOpSatSub:
10074 case IrBinOpSatMul:
10075 case IrBinOpSatShl:
10072 case IrBinOpAddSat:
10073 case IrBinOpSubSat:
10074 case IrBinOpMultSat:
10075 case IrBinOpShlSat:
1007610076 case IrBinOpMultWrap:
1007710077 case IrBinOpArrayCat:
1007810078 case IrBinOpArrayMult:
......@@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
1104611046 case IrBinOpRemMod:
1104711047 case IrBinOpMaximum:
1104811048 case IrBinOpMinimum:
11049 case IrBinOpSatAdd:
11050 case IrBinOpSatSub:
11051 case IrBinOpSatMul:
11052 case IrBinOpSatShl:
11049 case IrBinOpAddSat:
11050 case IrBinOpSubSat:
11051 case IrBinOpMultSat:
11052 case IrBinOpShlSat:
1105311053 return ir_analyze_bin_op_math(ira, bin_op_instruction);
1105411054 case IrBinOpArrayCat:
1105511055 return ir_analyze_array_cat(ira, bin_op_instruction);
src/stage1/ir_print.cpp+4-4
......@@ -737,13 +737,13 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
737737 return "@maximum";
738738 case IrBinOpMinimum:
739739 return "@minimum";
740 case IrBinOpSatAdd:
740 case IrBinOpAddSat:
741741 return "@addWithSaturation";
742 case IrBinOpSatSub:
742 case IrBinOpSubSat:
743743 return "@subWithSaturation";
744 case IrBinOpSatMul:
744 case IrBinOpMultSat:
745745 return "@mulWithSaturation";
746 case IrBinOpSatShl:
746 case IrBinOpShlSat:
747747 return "@shlWithSaturation";
748748 }
749749 zig_unreachable();
src/stage1/parser.cpp+16
......@@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
23812381// / PLUSEQUAL
23822382// / MINUSEQUAL
23832383// / LARROW2EQUAL
2384// / LARROW2PIPEEQUAL
23842385// / RARROW2EQUAL
23852386// / AMPERSANDEQUAL
23862387// / CARETEQUAL
......@@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
23882389// / ASTERISKPERCENTEQUAL
23892390// / PLUSPERCENTEQUAL
23902391// / MINUSPERCENTEQUAL
2392// / ASTERISKPIPEEQUAL
2393// / PLUSPIPEEQUAL
2394// / MINUSPIPEEQUAL
23912395// / EQUAL
23922396static AstNode *ast_parse_assign_op(ParseContext *pc) {
23932397 // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't
......@@ -2396,17 +2400,21 @@ static AstNode *ast_parse_assign_op(ParseContext *pc) {
23962400 table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;
23972401 table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;
23982402 table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;
2403 table[TokenIdBitShiftLeftPipeEq] = BinOpTypeAssignBitShiftLeftSat;
23992404 table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;
24002405 table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;
24012406 table[TokenIdDivEq] = BinOpTypeAssignDiv;
24022407 table[TokenIdEq] = BinOpTypeAssign;
24032408 table[TokenIdMinusEq] = BinOpTypeAssignMinus;
24042409 table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;
2410 table[TokenIdMinusPipeEq] = BinOpTypeAssignMinusSat;
24052411 table[TokenIdModEq] = BinOpTypeAssignMod;
24062412 table[TokenIdPlusEq] = BinOpTypeAssignPlus;
24072413 table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;
2414 table[TokenIdPlusPipeEq] = BinOpTypeAssignPlusSat;
24082415 table[TokenIdTimesEq] = BinOpTypeAssignTimes;
24092416 table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;
2417 table[TokenIdTimesPipeEq] = BinOpTypeAssignTimesSat;
24102418
24112419 BinOpType op = table[pc->token_ids[pc->current_token]];
24122420 if (op != BinOpTypeInvalid) {
......@@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {
24832491
24842492// BitShiftOp
24852493// <- LARROW2
2494// / LARROW2PIPE
24862495// / RARROW2
24872496static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
24882497 BinOpType table[TokenIdCount] = {};
24892498 table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;
2499 table[TokenIdBitShiftLeftPipe] = BinOpTypeBitShiftLeftSat;
24902500 table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;
24912501
24922502 BinOpType op = table[pc->token_ids[pc->current_token]];
......@@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
25062516// / PLUS2
25072517// / PLUSPERCENT
25082518// / MINUSPERCENT
2519// / PLUSPIPE
2520// / MINUSPIPE
25092521static AstNode *ast_parse_addition_op(ParseContext *pc) {
25102522 BinOpType table[TokenIdCount] = {};
25112523 table[TokenIdPlus] = BinOpTypeAdd;
......@@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
25132525 table[TokenIdPlusPlus] = BinOpTypeArrayCat;
25142526 table[TokenIdPlusPercent] = BinOpTypeAddWrap;
25152527 table[TokenIdMinusPercent] = BinOpTypeSubWrap;
2528 table[TokenIdPlusPipe] = BinOpTypeAddSat;
2529 table[TokenIdMinusPipe] = BinOpTypeSubSat;
25162530
25172531 BinOpType op = table[pc->token_ids[pc->current_token]];
25182532 if (op != BinOpTypeInvalid) {
......@@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
25322546// / PERCENT
25332547// / ASTERISK2
25342548// / ASTERISKPERCENT
2549// / ASTERISKPIPE
25352550static AstNode *ast_parse_multiply_op(ParseContext *pc) {
25362551 BinOpType table[TokenIdCount] = {};
25372552 table[TokenIdBarBar] = BinOpTypeMergeErrorSets;
......@@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {
25402555 table[TokenIdPercent] = BinOpTypeMod;
25412556 table[TokenIdStarStar] = BinOpTypeArrayMult;
25422557 table[TokenIdTimesPercent] = BinOpTypeMultWrap;
2558 table[TokenIdTimesPipe] = BinOpTypeMultSat;
25432559
25442560 BinOpType op = table[pc->token_ids[pc->current_token]];
25452561 if (op != BinOpTypeInvalid) {
src/stage1/tokenizer.cpp+84
......@@ -226,8 +226,10 @@ enum TokenizeState {
226226 TokenizeState_pipe,
227227 TokenizeState_minus,
228228 TokenizeState_minus_percent,
229 TokenizeState_minus_pipe,
229230 TokenizeState_asterisk,
230231 TokenizeState_asterisk_percent,
232 TokenizeState_asterisk_pipe,
231233 TokenizeState_slash,
232234 TokenizeState_line_comment_start,
233235 TokenizeState_line_comment,
......@@ -257,8 +259,10 @@ enum TokenizeState {
257259 TokenizeState_percent,
258260 TokenizeState_plus,
259261 TokenizeState_plus_percent,
262 TokenizeState_plus_pipe,
260263 TokenizeState_angle_bracket_left,
261264 TokenizeState_angle_bracket_angle_bracket_left,
265 TokenizeState_angle_bracket_angle_bracket_left_pipe,
262266 TokenizeState_angle_bracket_right,
263267 TokenizeState_angle_bracket_angle_bracket_right,
264268 TokenizeState_period,
......@@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) {
548552 case '%':
549553 t.state = TokenizeState_asterisk_percent;
550554 break;
555 case '|':
556 t.state = TokenizeState_asterisk_pipe;
557 break;
551558 default:
552559 t.state = TokenizeState_start;
553560 continue;
......@@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) {
568575 continue;
569576 }
570577 break;
578 case TokenizeState_asterisk_pipe:
579 switch (c) {
580 case 0:
581 t.out->ids.last() = TokenIdTimesPipe;
582 goto eof;
583 case '=':
584 t.out->ids.last() = TokenIdTimesPipeEq;
585 t.state = TokenizeState_start;
586 break;
587 default:
588 t.out->ids.last() = TokenIdTimesPipe;
589 t.state = TokenizeState_start;
590 continue;
591 }
592 break;
571593 case TokenizeState_percent:
572594 switch (c) {
573595 case 0:
......@@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) {
596618 case '%':
597619 t.state = TokenizeState_plus_percent;
598620 break;
621 case '|':
622 t.state = TokenizeState_plus_pipe;
623 break;
599624 default:
600625 t.state = TokenizeState_start;
601626 continue;
......@@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) {
616641 continue;
617642 }
618643 break;
644 case TokenizeState_plus_pipe:
645 switch (c) {
646 case 0:
647 t.out->ids.last() = TokenIdPlusPipe;
648 goto eof;
649 case '=':
650 t.out->ids.last() = TokenIdPlusPipeEq;
651 t.state = TokenizeState_start;
652 break;
653 default:
654 t.out->ids.last() = TokenIdPlusPipe;
655 t.state = TokenizeState_start;
656 continue;
657 }
658 break;
619659 case TokenizeState_caret:
620660 switch (c) {
621661 case 0:
......@@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) {
891931 case '%':
892932 t.state = TokenizeState_minus_percent;
893933 break;
934 case '|':
935 t.state = TokenizeState_minus_pipe;
936 break;
894937 default:
895938 t.state = TokenizeState_start;
896939 continue;
......@@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) {
911954 continue;
912955 }
913956 break;
957 case TokenizeState_minus_pipe:
958 switch (c) {
959 case 0:
960 t.out->ids.last() = TokenIdMinusPipe;
961 goto eof;
962 case '=':
963 t.out->ids.last() = TokenIdMinusPipeEq;
964 t.state = TokenizeState_start;
965 break;
966 default:
967 t.out->ids.last() = TokenIdMinusPipe;
968 t.state = TokenizeState_start;
969 continue;
970 }
971 break;
914972 case TokenizeState_angle_bracket_left:
915973 switch (c) {
916974 case 0:
......@@ -936,12 +994,30 @@ void tokenize(const char *source, Tokenization *out) {
936994 t.out->ids.last() = TokenIdBitShiftLeftEq;
937995 t.state = TokenizeState_start;
938996 break;
997 case '|':
998 t.state = TokenizeState_angle_bracket_angle_bracket_left_pipe;
999 break;
9391000 default:
9401001 t.out->ids.last() = TokenIdBitShiftLeft;
9411002 t.state = TokenizeState_start;
9421003 continue;
9431004 }
9441005 break;
1006 case TokenizeState_angle_bracket_angle_bracket_left_pipe:
1007 switch (c) {
1008 case 0:
1009 t.out->ids.last() = TokenIdBitShiftLeftPipe;
1010 goto eof;
1011 case '=':
1012 t.out->ids.last() = TokenIdBitShiftLeftPipeEq;
1013 t.state = TokenizeState_start;
1014 break;
1015 default:
1016 t.out->ids.last() = TokenIdBitShiftLeftPipe;
1017 t.state = TokenizeState_start;
1018 continue;
1019 }
1020 break;
9451021 case TokenizeState_angle_bracket_right:
9461022 switch (c) {
9471023 case 0:
......@@ -1437,6 +1513,8 @@ const char * token_name(TokenId id) {
14371513 case TokenIdBitOrEq: return "|=";
14381514 case TokenIdBitShiftLeft: return "<<";
14391515 case TokenIdBitShiftLeftEq: return "<<=";
1516 case TokenIdBitShiftLeftPipe: return "<<|";
1517 case TokenIdBitShiftLeftPipeEq: return "<<|=";
14401518 case TokenIdBitShiftRight: return ">>";
14411519 case TokenIdBitShiftRightEq: return ">>=";
14421520 case TokenIdBitXorEq: return "^=";
......@@ -1521,12 +1599,16 @@ const char * token_name(TokenId id) {
15211599 case TokenIdMinusEq: return "-=";
15221600 case TokenIdMinusPercent: return "-%";
15231601 case TokenIdMinusPercentEq: return "-%=";
1602 case TokenIdMinusPipe: return "-|";
1603 case TokenIdMinusPipeEq: return "-|=";
15241604 case TokenIdModEq: return "%=";
15251605 case TokenIdPercent: return "%";
15261606 case TokenIdPlus: return "+";
15271607 case TokenIdPlusEq: return "+=";
15281608 case TokenIdPlusPercent: return "+%";
15291609 case TokenIdPlusPercentEq: return "+%=";
1610 case TokenIdPlusPipe: return "+|";
1611 case TokenIdPlusPipeEq: return "+|=";
15301612 case TokenIdPlusPlus: return "++";
15311613 case TokenIdRBrace: return "}";
15321614 case TokenIdRBracket: return "]";
......@@ -1542,6 +1624,8 @@ const char * token_name(TokenId id) {
15421624 case TokenIdTimesEq: return "*=";
15431625 case TokenIdTimesPercent: return "*%";
15441626 case TokenIdTimesPercentEq: return "*%=";
1627 case TokenIdTimesPipe: return "*|";
1628 case TokenIdTimesPipeEq: return "*|=";
15451629 case TokenIdBuiltin: return "Builtin";
15461630 case TokenIdCount:
15471631 zig_unreachable();
src/stage1/tokenizer.hpp+8
......@@ -23,6 +23,8 @@ enum TokenId : uint8_t {
2323 TokenIdBitOrEq,
2424 TokenIdBitShiftLeft,
2525 TokenIdBitShiftLeftEq,
26 TokenIdBitShiftLeftPipe,
27 TokenIdBitShiftLeftPipeEq,
2628 TokenIdBitShiftRight,
2729 TokenIdBitShiftRightEq,
2830 TokenIdBitXorEq,
......@@ -108,12 +110,16 @@ enum TokenId : uint8_t {
108110 TokenIdMinusEq,
109111 TokenIdMinusPercent,
110112 TokenIdMinusPercentEq,
113 TokenIdMinusPipe,
114 TokenIdMinusPipeEq,
111115 TokenIdModEq,
112116 TokenIdPercent,
113117 TokenIdPlus,
114118 TokenIdPlusEq,
115119 TokenIdPlusPercent,
116120 TokenIdPlusPercentEq,
121 TokenIdPlusPipe,
122 TokenIdPlusPipeEq,
117123 TokenIdPlusPlus,
118124 TokenIdRBrace,
119125 TokenIdRBracket,
......@@ -129,6 +135,8 @@ enum TokenId : uint8_t {
129135 TokenIdTimesEq,
130136 TokenIdTimesPercent,
131137 TokenIdTimesPercentEq,
138 TokenIdTimesPipe,
139 TokenIdTimesPipeEq,
132140
133141 TokenIdCount,
134142};
src/translate_c/ast.zig+4-4
......@@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
14621462 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
14631463 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
14641464 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
1465 .shl => return renderBinOpGrouped(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"),
1466 .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="),
1467 .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"),
1468 .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="),
1465 .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
1466 .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
1467 .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
1468 .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
14691469 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
14701470 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
14711471 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
src/value.zig+87
......@@ -1588,6 +1588,35 @@ pub const Value = extern union {
15881588 return result;
15891589 }
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
15911620 /// Supports both floats and ints; handles undefined.
15921621 pub fn numberSubWrap(
15931622 lhs: Value,
......@@ -1616,6 +1645,35 @@ pub const Value = extern union {
16161645 return result;
16171646 }
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
16191677 /// Supports both floats and ints; handles undefined.
16201678 pub fn numberMulWrap(
16211679 lhs: Value,
......@@ -1644,6 +1702,35 @@ pub const Value = extern union {
16441702 return result;
16451703 }
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
16471734 /// Supports both floats and ints; handles undefined.
16481735 pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {
16491736 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
test/behavior/saturating_arithmetic.zig+28-31
......@@ -11,16 +11,28 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {
1111 const a = test_data[0];
1212 const b = test_data[1];
1313 const expected = test_data[2];
14 const actual = switch (op) {
15 .add => @addWithSaturation(a, b),
16 .sub => @subWithSaturation(a, b),
17 .mul => @mulWithSaturation(a, b),
18 .shl => @shlWithSaturation(a, b),
19 };
20 try expectEqual(expected, actual);
14 {
15 const actual = switch (op) {
16 .add => a +| b,
17 .sub => a -| b,
18 .mul => a *| b,
19 .shl => a <<| b,
20 };
21 try expectEqual(expected, actual);
22 }
23 {
24 var actual = a;
25 switch (op) {
26 .add => actual +|= b,
27 .sub => actual -|= b,
28 .mul => actual *|= b,
29 .shl => actual <<|= b,
30 }
31 try expectEqual(expected, actual);
32 }
2133}
2234
23test "@addWithSaturation" {
35test "saturating add" {
2436 const S = struct {
2537 fn doTheTest() !void {
2638 // .{a, b, expected a+b}
......@@ -38,22 +50,16 @@ test "@addWithSaturation" {
3850 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });
3951
4052 const u8x3 = std.meta.Vector(3, u8);
41 try expectEqual(u8x3{ 255, 255, 255 }, @addWithSaturation(
42 u8x3{ 255, 254, 1 },
43 u8x3{ 1, 2, 255 },
44 ));
53 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }));
4554 const i8x3 = std.meta.Vector(3, i8);
46 try expectEqual(i8x3{ 127, 127, 127 }, @addWithSaturation(
47 i8x3{ 127, 126, 1 },
48 i8x3{ 1, 2, 127 },
49 ));
55 try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }));
5056 }
5157 };
5258 try S.doTheTest();
5359 comptime try S.doTheTest();
5460}
5561
56test "@subWithSaturation" {
62test "saturating subtraction" {
5763 const S = struct {
5864 fn doTheTest() !void {
5965 // .{a, b, expected a-b}
......@@ -69,17 +75,14 @@ test "@subWithSaturation" {
6975 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });
7076
7177 const u8x3 = std.meta.Vector(3, u8);
72 try expectEqual(u8x3{ 0, 0, 0 }, @subWithSaturation(
73 u8x3{ 0, 0, 0 },
74 u8x3{ 255, 255, 255 },
75 ));
78 try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }));
7679 }
7780 };
7881 try S.doTheTest();
7982 comptime try S.doTheTest();
8083}
8184
82test "@mulWithSaturation" {
85test "saturating multiplication" {
8386 // TODO: once #9660 has been solved, remove this line
8487 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;
8588
......@@ -100,10 +103,7 @@ test "@mulWithSaturation" {
100103 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });
101104
102105 const u8x3 = std.meta.Vector(3, u8);
103 try expectEqual(u8x3{ 255, 255, 255 }, @mulWithSaturation(
104 u8x3{ 2, 2, 2 },
105 u8x3{ 255, 255, 255 },
106 ));
106 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }));
107107 }
108108 };
109109
......@@ -111,7 +111,7 @@ test "@mulWithSaturation" {
111111 comptime try S.doTheTest();
112112}
113113
114test "@shlWithSaturation" {
114test "saturating shift-left" {
115115 const S = struct {
116116 fn doTheTest() !void {
117117 // .{a, b, expected a<<b}
......@@ -128,10 +128,7 @@ test "@shlWithSaturation" {
128128 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });
129129
130130 const u8x3 = std.meta.Vector(3, u8);
131 try expectEqual(u8x3{ 255, 255, 255 }, @shlWithSaturation(
132 u8x3{ 255, 255, 255 },
133 u8x3{ 1, 1, 1 },
134 ));
131 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }));
135132 }
136133 };
137134 try S.doTheTest();