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(...@@ -1058,15 +1058,21 @@ fn tokenizeAndPrintRaw(
1058 .plus_equal,1058 .plus_equal,
1059 .plus_percent,1059 .plus_percent,
1060 .plus_percent_equal,1060 .plus_percent_equal,
1061 .plus_pipe,
1062 .plus_pipe_equal,
1061 .minus,1063 .minus,
1062 .minus_equal,1064 .minus_equal,
1063 .minus_percent,1065 .minus_percent,
1064 .minus_percent_equal,1066 .minus_percent_equal,
1067 .minus_pipe,
1068 .minus_pipe_equal,
1065 .asterisk,1069 .asterisk,
1066 .asterisk_equal,1070 .asterisk_equal,
1067 .asterisk_asterisk,1071 .asterisk_asterisk,
1068 .asterisk_percent,1072 .asterisk_percent,
1069 .asterisk_percent_equal,1073 .asterisk_percent_equal,
1074 .asterisk_pipe,
1075 .asterisk_pipe_equal,
1070 .arrow,1076 .arrow,
1071 .colon,1077 .colon,
1072 .slash,1078 .slash,
...@@ -1079,6 +1085,8 @@ fn tokenizeAndPrintRaw(...@@ -1079,6 +1085,8 @@ fn tokenizeAndPrintRaw(
1079 .angle_bracket_left_equal,1085 .angle_bracket_left_equal,
1080 .angle_bracket_angle_bracket_left,1086 .angle_bracket_angle_bracket_left,
1081 .angle_bracket_angle_bracket_left_equal,1087 .angle_bracket_angle_bracket_left_equal,
1088 .angle_bracket_angle_bracket_left_pipe,
1089 .angle_bracket_angle_bracket_left_pipe_equal,
1082 .angle_bracket_right,1090 .angle_bracket_right,
1083 .angle_bracket_right_equal,1091 .angle_bracket_right_equal,
1084 .angle_bracket_angle_bracket_right,1092 .angle_bracket_angle_bracket_right,
doc/langref.html.in+95-63
...@@ -1244,8 +1244,9 @@ fn divide(a: i32, b: i32) i32 {...@@ -1244,8 +1244,9 @@ fn divide(a: i32, b: i32) i32 {
1244 </p>1244 </p>
1245 <p>1245 <p>
1246 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on1246 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
1247 integer overflow. Also available are operations such as {#syntax#}+%{#endsyntax#} and1247 integer overflow. Alternative operators are provided for wrapping and saturating arithmetic on all targets.
1248 {#syntax#}-%{#endsyntax#} which are defined to have wrapping arithmetic on all targets.1248 {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} perform wrapping arithmetic
1249 while {#syntax#}+|{#endsyntax#} and {#syntax#}-|{#endsyntax#} perform saturating arithmetic.
1249 </p>1250 </p>
1250 <p>1251 <p>
1251 Zig supports arbitrary bit-width integers, referenced by using1252 Zig supports arbitrary bit-width integers, referenced by using
...@@ -1395,6 +1396,23 @@ a +%= b{#endsyntax#}</pre></th>...@@ -1395,6 +1396,23 @@ a +%= b{#endsyntax#}</pre></th>
1395 <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +% 1 == 0{#endsyntax#}</pre>1396 <pre>{#syntax#}@as(u32, std.math.maxInt(u32)) +% 1 == 0{#endsyntax#}</pre>
1396 </td>1397 </td>
1397 </tr>1398 </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>
1398 <tr>1416 <tr>
1399 <th scope="row"><pre>{#syntax#}a - b1417 <th scope="row"><pre>{#syntax#}a - b
1400a -= b{#endsyntax#}</pre></th>1418a -= b{#endsyntax#}</pre></th>
...@@ -1434,6 +1452,23 @@ a -%= b{#endsyntax#}</pre></th>...@@ -1434,6 +1452,23 @@ a -%= b{#endsyntax#}</pre></th>
1434 <pre>{#syntax#}@as(u32, 0) -% 1 == std.math.maxInt(u32){#endsyntax#}</pre>1452 <pre>{#syntax#}@as(u32, 0) -% 1 == std.math.maxInt(u32){#endsyntax#}</pre>
1435 </td>1453 </td>
1436 </tr>1454 </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>
1437 <tr>1472 <tr>
1438 <th scope="row"><pre>{#syntax#}-a{#endsyntax#}</pre></th>1473 <th scope="row"><pre>{#syntax#}-a{#endsyntax#}</pre></th>
1439 <td>1474 <td>
...@@ -1508,6 +1543,23 @@ a *%= b{#endsyntax#}</pre></th>...@@ -1508,6 +1543,23 @@ a *%= b{#endsyntax#}</pre></th>
1508 <pre>{#syntax#}@as(u8, 200) *% 2 == 144{#endsyntax#}</pre>1543 <pre>{#syntax#}@as(u8, 200) *% 2 == 144{#endsyntax#}</pre>
1509 </td>1544 </td>
1510 </tr>1545 </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>
1511 <tr>1563 <tr>
1512 <th scope="row"><pre>{#syntax#}a / b1564 <th scope="row"><pre>{#syntax#}a / b
1513a /= b{#endsyntax#}</pre></th>1565a /= b{#endsyntax#}</pre></th>
...@@ -1577,6 +1629,24 @@ a <<= b{#endsyntax#}</pre></th>...@@ -1577,6 +1629,24 @@ a <<= b{#endsyntax#}</pre></th>
1577 <pre>{#syntax#}1 << 8 == 256{#endsyntax#}</pre>1629 <pre>{#syntax#}1 << 8 == 256{#endsyntax#}</pre>
1578 </td>1630 </td>
1579 </tr>1631 </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>
1580 <tr>1650 <tr>
1581 <th scope="row"><pre>{#syntax#}a >> b1651 <th scope="row"><pre>{#syntax#}a >> b
1582a >>= b{#endsyntax#}</pre></th>1652a >>= b{#endsyntax#}</pre></th>
...@@ -1968,14 +2038,14 @@ const B = error{Two};...@@ -1968,14 +2038,14 @@ const B = error{Two};
1968a!b2038a!b
1969x{}2039x{}
1970!x -x -%x ~x &x ?x2040!x -x -%x ~x &x ?x
1971* / % ** *% ||2041* / % ** *% *| ||
1972+ - ++ +% -%2042+ - ++ +% -% +| -|
1973<< >>2043<< >> <<|
1974& ^ | orelse catch2044& ^ | orelse catch
1975== != < > <= >=2045== != < > <= >=
1976and2046and
1977or2047or
1978= *= /= %= += -= <<= >>= &= ^= |={#endsyntax#}</pre>2048= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}</pre>
1979 {#header_close#}2049 {#header_close#}
1980 {#header_close#}2050 {#header_close#}
1981 {#header_open|Arrays#}2051 {#header_open|Arrays#}
...@@ -7162,16 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {...@@ -7162,16 +7232,6 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
7162 If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.7232 If no overflow or underflow occurs, returns {#syntax#}false{#endsyntax#}.
7163 </p>7233 </p>
7164 {#header_close#}7234 {#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#}
7175 {#header_open|@alignCast#}7235 {#header_open|@alignCast#}
7176 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>7236 <pre>{#syntax#}@alignCast(comptime alignment: u29, ptr: anytype) anytype{#endsyntax#}</pre>
7177 <p>7237 <p>
...@@ -8293,22 +8353,6 @@ test "@wasmMemoryGrow" {...@@ -8293,22 +8353,6 @@ test "@wasmMemoryGrow" {
8293 </p>8353 </p>
8294 {#header_close#}8354 {#header_close#}
8295 8355
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
8312 {#header_open|@panic#}8356 {#header_open|@panic#}
8313 <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre>8357 <pre>{#syntax#}@panic(message: []const u8) noreturn{#endsyntax#}</pre>
8314 <p>8358 <p>
...@@ -8526,14 +8570,16 @@ test "@setRuntimeSafety" {...@@ -8526,14 +8570,16 @@ test "@setRuntimeSafety" {
8526 {#header_open|@shlExact#}8570 {#header_open|@shlExact#}
8527 <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>8571 <pre>{#syntax#}@shlExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
8528 <p>8572 <p>
8529 Performs the left shift operation ({#syntax#}<<{#endsyntax#}). Caller guarantees8573 Performs the left shift operation ({#syntax#}<<{#endsyntax#}).
8530 that the shift will not shift any 1 bits out.8574 For unsigned integers, the result is {#link|undefined#} if any 1 bits
8575 are shifted out. For signed integers, the result is {#link|undefined#} if
8576 any bits that disagree with the resultant sign bit are shifted out.
8531 </p>8577 </p>
8532 <p>8578 <p>
8533 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8579 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8534 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8580 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8535 </p>8581 </p>
8536 {#see_also|@shrExact|@shlWithOverflow|@shlWithSaturation#}8582 {#see_also|@shrExact|@shlWithOverflow#}
8537 {#header_close#}8583 {#header_close#}
85388584
8539 {#header_open|@shlWithOverflow#}8585 {#header_open|@shlWithOverflow#}
...@@ -8547,24 +8593,9 @@ test "@setRuntimeSafety" {...@@ -8547,24 +8593,9 @@ test "@setRuntimeSafety" {
8547 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8593 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8548 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8594 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8549 </p>8595 </p>
8550 {#see_also|@shlExact|@shrExact|@shlWithSaturation#}8596 {#see_also|@shlExact|@shrExact#}
8551 {#header_close#}8597 {#header_close#}
8552 8598
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
8568 {#header_open|@shrExact#}8599 {#header_open|@shrExact#}
8569 <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>8600 <pre>{#syntax#}@shrExact(value: T, shift_amt: Log2T) T{#endsyntax#}</pre>
8570 <p>8601 <p>
...@@ -8575,7 +8606,7 @@ test "@setRuntimeSafety" {...@@ -8575,7 +8606,7 @@ test "@setRuntimeSafety" {
8575 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.8606 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(T.bit_count){#endsyntax#} bits.
8576 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.8607 This is because {#syntax#}shift_amt >= T.bit_count{#endsyntax#} is undefined behavior.
8577 </p>8608 </p>
8578 {#see_also|@shlExact|@shlWithOverflow|@shlWithSaturation#}8609 {#see_also|@shlExact|@shlWithOverflow#}
8579 {#header_close#}8610 {#header_close#}
85808611
8581 {#header_open|@shuffle#}8612 {#header_open|@shuffle#}
...@@ -8875,17 +8906,6 @@ fn doTheTest() !void {...@@ -8875,17 +8906,6 @@ fn doTheTest() !void {
8875 </p>8906 </p>
8876 {#header_close#}8907 {#header_close#}
8877 8908
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
8889 {#header_open|@tagName#}8909 {#header_open|@tagName#}
8890 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>8910 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>
8891 <p>8911 <p>
...@@ -11839,6 +11859,7 @@ AssignOp...@@ -11839,6 +11859,7 @@ AssignOp
11839 / PLUSEQUAL11859 / PLUSEQUAL
11840 / MINUSEQUAL11860 / MINUSEQUAL
11841 / LARROW2EQUAL11861 / LARROW2EQUAL
11862 / LARROW2PIPEEQUAL
11842 / RARROW2EQUAL11863 / RARROW2EQUAL
11843 / AMPERSANDEQUAL11864 / AMPERSANDEQUAL
11844 / CARETEQUAL11865 / CARETEQUAL
...@@ -11873,6 +11894,8 @@ AdditionOp...@@ -11873,6 +11894,8 @@ AdditionOp
11873 / PLUS211894 / PLUS2
11874 / PLUSPERCENT11895 / PLUSPERCENT
11875 / MINUSPERCENT11896 / MINUSPERCENT
11897 / PLUSPIPE
11898 / MINUSPIPE
1187611899
11877MultiplyOp11900MultiplyOp
11878 &lt;- PIPE211901 &lt;- PIPE2
...@@ -11881,6 +11904,7 @@ MultiplyOp...@@ -11881,6 +11904,7 @@ MultiplyOp
11881 / PERCENT11904 / PERCENT
11882 / ASTERISK211905 / ASTERISK2
11883 / ASTERISKPERCENT11906 / ASTERISKPERCENT
11907 / ASTERISKPIPE
1188411908
11885PrefixOp11909PrefixOp
11886 &lt;- EXCLAMATIONMARK11910 &lt;- EXCLAMATIONMARK
...@@ -12044,6 +12068,8 @@ ASTERISK2 &lt;- '**' skip...@@ -12044,6 +12068,8 @@ ASTERISK2 &lt;- '**' skip
12044ASTERISKEQUAL &lt;- '*=' skip12068ASTERISKEQUAL &lt;- '*=' skip
12045ASTERISKPERCENT &lt;- '*%' ![=] skip12069ASTERISKPERCENT &lt;- '*%' ![=] skip
12046ASTERISKPERCENTEQUAL &lt;- '*%=' skip12070ASTERISKPERCENTEQUAL &lt;- '*%=' skip
12071ASTERISKPIPE &lt;- '*|' ![=] skip
12072ASTERISKPIPEEQUAL &lt;- '*|=' skip
12047CARET &lt;- '^' ![=] skip12073CARET &lt;- '^' ![=] skip
12048CARETEQUAL &lt;- '^=' skip12074CARETEQUAL &lt;- '^=' skip
12049COLON &lt;- ':' skip12075COLON &lt;- ':' skip
...@@ -12060,6 +12086,8 @@ EXCLAMATIONMARK &lt;- '!' ![=] skip...@@ -12060,6 +12086,8 @@ EXCLAMATIONMARK &lt;- '!' ![=] skip
12060EXCLAMATIONMARKEQUAL &lt;- '!=' skip12086EXCLAMATIONMARKEQUAL &lt;- '!=' skip
12061LARROW &lt;- '&lt;' ![&lt;=] skip12087LARROW &lt;- '&lt;' ![&lt;=] skip
12062LARROW2 &lt;- '&lt;&lt;' ![=] skip12088LARROW2 &lt;- '&lt;&lt;' ![=] skip
12089LARROW2PIPE &lt;- '&lt;&lt;|' ![=] skip
12090LARROW2PIPEEQUAL &lt;- '&lt;&lt;|=' ![=] skip
12063LARROW2EQUAL &lt;- '&lt;&lt;=' skip12091LARROW2EQUAL &lt;- '&lt;&lt;=' skip
12064LARROWEQUAL &lt;- '&lt;=' skip12092LARROWEQUAL &lt;- '&lt;=' skip
12065LBRACE &lt;- '{' skip12093LBRACE &lt;- '{' skip
...@@ -12069,6 +12097,8 @@ MINUS &lt;- '-' ![%=&gt;] skip...@@ -12069,6 +12097,8 @@ MINUS &lt;- '-' ![%=&gt;] skip
12069MINUSEQUAL &lt;- '-=' skip12097MINUSEQUAL &lt;- '-=' skip
12070MINUSPERCENT &lt;- '-%' ![=] skip12098MINUSPERCENT &lt;- '-%' ![=] skip
12071MINUSPERCENTEQUAL &lt;- '-%=' skip12099MINUSPERCENTEQUAL &lt;- '-%=' skip
12100MINUSPIPE &lt;- '-|' ![=] skip
12101MINUSPIPEEQUAL &lt;- '-|=' skip
12072MINUSRARROW &lt;- '-&gt;' skip12102MINUSRARROW &lt;- '-&gt;' skip
12073PERCENT &lt;- '%' ![=] skip12103PERCENT &lt;- '%' ![=] skip
12074PERCENTEQUAL &lt;- '%=' skip12104PERCENTEQUAL &lt;- '%=' skip
...@@ -12080,6 +12110,8 @@ PLUS2 &lt;- '++' skip...@@ -12080,6 +12110,8 @@ PLUS2 &lt;- '++' skip
12080PLUSEQUAL &lt;- '+=' skip12110PLUSEQUAL &lt;- '+=' skip
12081PLUSPERCENT &lt;- '+%' ![=] skip12111PLUSPERCENT &lt;- '+%' ![=] skip
12082PLUSPERCENTEQUAL &lt;- '+%=' skip12112PLUSPERCENTEQUAL &lt;- '+%=' skip
12113PLUSPIPE &lt;- '+|' ![=] skip
12114PLUSPIPEEQUAL &lt;- '+|=' skip
12083LETTERC &lt;- 'c' skip12115LETTERC &lt;- 'c' skip
12084QUESTIONMARK &lt;- '?' skip12116QUESTIONMARK &lt;- '?' skip
12085RARROW &lt;- '&gt;' ![&gt;=] skip12117RARROW &lt;- '&gt;' ![&gt;=] skip
lib/std/zig/Ast.zig+44-12
...@@ -395,14 +395,18 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -395,14 +395,18 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
395 .assign_mod,395 .assign_mod,
396 .assign_add,396 .assign_add,
397 .assign_sub,397 .assign_sub,
398 .assign_bit_shift_left,398 .assign_shl,
399 .assign_bit_shift_right,399 .assign_shl_sat,
400 .assign_shr,
400 .assign_bit_and,401 .assign_bit_and,
401 .assign_bit_xor,402 .assign_bit_xor,
402 .assign_bit_or,403 .assign_bit_or,
403 .assign_mul_wrap,404 .assign_mul_wrap,
404 .assign_add_wrap,405 .assign_add_wrap,
405 .assign_sub_wrap,406 .assign_sub_wrap,
407 .assign_mul_sat,
408 .assign_add_sat,
409 .assign_sub_sat,
406 .assign,410 .assign,
407 .merge_error_sets,411 .merge_error_sets,
408 .mul,412 .mul,
...@@ -410,13 +414,17 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -410,13 +414,17 @@ pub fn firstToken(tree: Tree, node: Node.Index) TokenIndex {
410 .mod,414 .mod,
411 .array_mult,415 .array_mult,
412 .mul_wrap,416 .mul_wrap,
417 .mul_sat,
413 .add,418 .add,
414 .sub,419 .sub,
415 .array_cat,420 .array_cat,
416 .add_wrap,421 .add_wrap,
417 .sub_wrap,422 .sub_wrap,
418 .bit_shift_left,423 .add_sat,
419 .bit_shift_right,424 .sub_sat,
425 .shl,
426 .shl_sat,
427 .shr,
420 .bit_and,428 .bit_and,
421 .bit_xor,429 .bit_xor,
422 .bit_or,430 .bit_or,
...@@ -651,14 +659,18 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -651,14 +659,18 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
651 .assign_mod,659 .assign_mod,
652 .assign_add,660 .assign_add,
653 .assign_sub,661 .assign_sub,
654 .assign_bit_shift_left,662 .assign_shl,
655 .assign_bit_shift_right,663 .assign_shl_sat,
664 .assign_shr,
656 .assign_bit_and,665 .assign_bit_and,
657 .assign_bit_xor,666 .assign_bit_xor,
658 .assign_bit_or,667 .assign_bit_or,
659 .assign_mul_wrap,668 .assign_mul_wrap,
660 .assign_add_wrap,669 .assign_add_wrap,
661 .assign_sub_wrap,670 .assign_sub_wrap,
671 .assign_mul_sat,
672 .assign_add_sat,
673 .assign_sub_sat,
662 .assign,674 .assign,
663 .merge_error_sets,675 .merge_error_sets,
664 .mul,676 .mul,
...@@ -666,13 +678,17 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {...@@ -666,13 +678,17 @@ pub fn lastToken(tree: Tree, node: Node.Index) TokenIndex {
666 .mod,678 .mod,
667 .array_mult,679 .array_mult,
668 .mul_wrap,680 .mul_wrap,
681 .mul_sat,
669 .add,682 .add,
670 .sub,683 .sub,
671 .array_cat,684 .array_cat,
672 .add_wrap,685 .add_wrap,
673 .sub_wrap,686 .sub_wrap,
674 .bit_shift_left,687 .add_sat,
675 .bit_shift_right,688 .sub_sat,
689 .shl,
690 .shl_sat,
691 .shr,
676 .bit_and,692 .bit_and,
677 .bit_xor,693 .bit_xor,
678 .bit_or,694 .bit_or,
...@@ -2524,9 +2540,11 @@ pub const Node = struct {...@@ -2524,9 +2540,11 @@ pub const Node = struct {
2524 /// `lhs -= rhs`. main_token is op.2540 /// `lhs -= rhs`. main_token is op.
2525 assign_sub,2541 assign_sub,
2526 /// `lhs <<= rhs`. main_token is op.2542 /// `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,
2528 /// `lhs >>= rhs`. main_token is op.2546 /// `lhs >>= rhs`. main_token is op.
2529 assign_bit_shift_right,2547 assign_shr,
2530 /// `lhs &= rhs`. main_token is op.2548 /// `lhs &= rhs`. main_token is op.
2531 assign_bit_and,2549 assign_bit_and,
2532 /// `lhs ^= rhs`. main_token is op.2550 /// `lhs ^= rhs`. main_token is op.
...@@ -2539,6 +2557,12 @@ pub const Node = struct {...@@ -2539,6 +2557,12 @@ pub const Node = struct {
2539 assign_add_wrap,2557 assign_add_wrap,
2540 /// `lhs -%= rhs`. main_token is op.2558 /// `lhs -%= rhs`. main_token is op.
2541 assign_sub_wrap,2559 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,
2542 /// `lhs = rhs`. main_token is op.2566 /// `lhs = rhs`. main_token is op.
2543 assign,2567 assign,
2544 /// `lhs || rhs`. main_token is the `||`.2568 /// `lhs || rhs`. main_token is the `||`.
...@@ -2553,6 +2577,8 @@ pub const Node = struct {...@@ -2553,6 +2577,8 @@ pub const Node = struct {
2553 array_mult,2577 array_mult,
2554 /// `lhs *% rhs`. main_token is the `*%`.2578 /// `lhs *% rhs`. main_token is the `*%`.
2555 mul_wrap,2579 mul_wrap,
2580 /// `lhs *| rhs`. main_token is the `*|`.
2581 mul_sat,
2556 /// `lhs + rhs`. main_token is the `+`.2582 /// `lhs + rhs`. main_token is the `+`.
2557 add,2583 add,
2558 /// `lhs - rhs`. main_token is the `-`.2584 /// `lhs - rhs`. main_token is the `-`.
...@@ -2563,10 +2589,16 @@ pub const Node = struct {...@@ -2563,10 +2589,16 @@ pub const Node = struct {
2563 add_wrap,2589 add_wrap,
2564 /// `lhs -% rhs`. main_token is the `-%`.2590 /// `lhs -% rhs`. main_token is the `-%`.
2565 sub_wrap,2591 sub_wrap,
2592 /// `lhs +| rhs`. main_token is the `+|`.
2593 add_sat,
2594 /// `lhs -| rhs`. main_token is the `-|`.
2595 sub_sat,
2566 /// `lhs << rhs`. main_token is the `<<`.2596 /// `lhs << rhs`. main_token is the `<<`.
2567 bit_shift_left,2597 shl,
2598 /// `lhs <<| rhs`. main_token is the `<<|`.
2599 shl_sat,
2568 /// `lhs >> rhs`. main_token is the `>>`.2600 /// `lhs >> rhs`. main_token is the `>>`.
2569 bit_shift_right,2601 shr,
2570 /// `lhs & rhs`. main_token is the `&`.2602 /// `lhs & rhs`. main_token is the `&`.
2571 bit_and,2603 bit_and,
2572 /// `lhs ^ rhs`. main_token is the `^`.2604 /// `lhs ^ rhs`. main_token is the `^`.
lib/std/zig/parse.zig+12-4
...@@ -1268,14 +1268,18 @@ const Parser = struct {...@@ -1268,14 +1268,18 @@ const Parser = struct {
1268 .percent_equal => .assign_mod,1268 .percent_equal => .assign_mod,
1269 .plus_equal => .assign_add,1269 .plus_equal => .assign_add,
1270 .minus_equal => .assign_sub,1270 .minus_equal => .assign_sub,
1271 .angle_bracket_angle_bracket_left_equal => .assign_bit_shift_left,1271 .angle_bracket_angle_bracket_left_equal => .assign_shl,
1272 .angle_bracket_angle_bracket_right_equal => .assign_bit_shift_right,1272 .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat,
1273 .angle_bracket_angle_bracket_right_equal => .assign_shr,
1273 .ampersand_equal => .assign_bit_and,1274 .ampersand_equal => .assign_bit_and,
1274 .caret_equal => .assign_bit_xor,1275 .caret_equal => .assign_bit_xor,
1275 .pipe_equal => .assign_bit_or,1276 .pipe_equal => .assign_bit_or,
1276 .asterisk_percent_equal => .assign_mul_wrap,1277 .asterisk_percent_equal => .assign_mul_wrap,
1277 .plus_percent_equal => .assign_add_wrap,1278 .plus_percent_equal => .assign_add_wrap,
1278 .minus_percent_equal => .assign_sub_wrap,1279 .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,
1279 .equal => .assign,1283 .equal => .assign,
1280 else => return expr,1284 else => return expr,
1281 };1285 };
...@@ -1342,14 +1346,17 @@ const Parser = struct {...@@ -1342,14 +1346,17 @@ const Parser = struct {
1342 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },1346 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },
1343 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },1347 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
13441348
1345 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .bit_shift_left },1349 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl },
1346 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .bit_shift_right },1350 .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat },
1351 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr },
13471352
1348 .plus = .{ .prec = 60, .tag = .add },1353 .plus = .{ .prec = 60, .tag = .add },
1349 .minus = .{ .prec = 60, .tag = .sub },1354 .minus = .{ .prec = 60, .tag = .sub },
1350 .plus_plus = .{ .prec = 60, .tag = .array_cat },1355 .plus_plus = .{ .prec = 60, .tag = .array_cat },
1351 .plus_percent = .{ .prec = 60, .tag = .add_wrap },1356 .plus_percent = .{ .prec = 60, .tag = .add_wrap },
1352 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },1357 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },
1358 .plus_pipe = .{ .prec = 60, .tag = .add_sat },
1359 .minus_pipe = .{ .prec = 60, .tag = .sub_sat },
13531360
1354 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },1361 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },
1355 .asterisk = .{ .prec = 70, .tag = .mul },1362 .asterisk = .{ .prec = 70, .tag = .mul },
...@@ -1357,6 +1364,7 @@ const Parser = struct {...@@ -1357,6 +1364,7 @@ const Parser = struct {
1357 .percent = .{ .prec = 70, .tag = .mod },1364 .percent = .{ .prec = 70, .tag = .mod },
1358 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },1365 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },
1359 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },1366 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },
1367 .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat },
1360 });1368 });
13611369
1362 fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {1370 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" {...@@ -4739,6 +4739,26 @@ test "zig fmt: assignment with inline for and inline while" {
4739 );4739 );
4740}4740}
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
4742test "zig fmt: insert trailing comma if there are comments between switch values" {4762test "zig fmt: insert trailing comma if there are comments between switch values" {
4743 try testTransform(4763 try testTransform(
4744 \\const a = switch (b) {4764 \\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,...@@ -333,27 +333,33 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
333333
334 .add,334 .add,
335 .add_wrap,335 .add_wrap,
336 .add_sat,
336 .array_cat,337 .array_cat,
337 .array_mult,338 .array_mult,
338 .assign,339 .assign,
339 .assign_bit_and,340 .assign_bit_and,
340 .assign_bit_or,341 .assign_bit_or,
341 .assign_bit_shift_left,342 .assign_shl,
342 .assign_bit_shift_right,343 .assign_shl_sat,
344 .assign_shr,
343 .assign_bit_xor,345 .assign_bit_xor,
344 .assign_div,346 .assign_div,
345 .assign_sub,347 .assign_sub,
346 .assign_sub_wrap,348 .assign_sub_wrap,
349 .assign_sub_sat,
347 .assign_mod,350 .assign_mod,
348 .assign_add,351 .assign_add,
349 .assign_add_wrap,352 .assign_add_wrap,
353 .assign_add_sat,
350 .assign_mul,354 .assign_mul,
351 .assign_mul_wrap,355 .assign_mul_wrap,
356 .assign_mul_sat,
352 .bang_equal,357 .bang_equal,
353 .bit_and,358 .bit_and,
354 .bit_or,359 .bit_or,
355 .bit_shift_left,360 .shl,
356 .bit_shift_right,361 .shl_sat,
362 .shr,
357 .bit_xor,363 .bit_xor,
358 .bool_and,364 .bool_and,
359 .bool_or,365 .bool_or,
...@@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -367,8 +373,10 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
367 .mod,373 .mod,
368 .mul,374 .mul,
369 .mul_wrap,375 .mul_wrap,
376 .mul_sat,
370 .sub,377 .sub,
371 .sub_wrap,378 .sub_wrap,
379 .sub_sat,
372 .@"orelse",380 .@"orelse",
373 => {381 => {
374 const infix = datas[node];382 const infix = datas[node];
...@@ -2520,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {...@@ -2520,8 +2528,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
2520 .assign,2528 .assign,
2521 .assign_bit_and,2529 .assign_bit_and,
2522 .assign_bit_or,2530 .assign_bit_or,
2523 .assign_bit_shift_left,2531 .assign_shl,
2524 .assign_bit_shift_right,2532 .assign_shr,
2525 .assign_bit_xor,2533 .assign_bit_xor,
2526 .assign_div,2534 .assign_div,
2527 .assign_sub,2535 .assign_sub,
...@@ -2534,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {...@@ -2534,8 +2542,8 @@ fn nodeCausesSliceOpSpace(tag: Ast.Node.Tag) bool {
2534 .bang_equal,2542 .bang_equal,
2535 .bit_and,2543 .bit_and,
2536 .bit_or,2544 .bit_or,
2537 .bit_shift_left,2545 .shl,
2538 .bit_shift_right,2546 .shr,
2539 .bit_xor,2547 .bit_xor,
2540 .bool_and,2548 .bool_and,
2541 .bool_or,2549 .bool_or,
lib/std/zig/tokenizer.zig+97
...@@ -103,15 +103,21 @@ pub const Token = struct {...@@ -103,15 +103,21 @@ pub const Token = struct {
103 plus_equal,103 plus_equal,
104 plus_percent,104 plus_percent,
105 plus_percent_equal,105 plus_percent_equal,
106 plus_pipe,
107 plus_pipe_equal,
106 minus,108 minus,
107 minus_equal,109 minus_equal,
108 minus_percent,110 minus_percent,
109 minus_percent_equal,111 minus_percent_equal,
112 minus_pipe,
113 minus_pipe_equal,
110 asterisk,114 asterisk,
111 asterisk_equal,115 asterisk_equal,
112 asterisk_asterisk,116 asterisk_asterisk,
113 asterisk_percent,117 asterisk_percent,
114 asterisk_percent_equal,118 asterisk_percent_equal,
119 asterisk_pipe,
120 asterisk_pipe_equal,
115 arrow,121 arrow,
116 colon,122 colon,
117 slash,123 slash,
...@@ -124,6 +130,8 @@ pub const Token = struct {...@@ -124,6 +130,8 @@ pub const Token = struct {
124 angle_bracket_left_equal,130 angle_bracket_left_equal,
125 angle_bracket_angle_bracket_left,131 angle_bracket_angle_bracket_left,
126 angle_bracket_angle_bracket_left_equal,132 angle_bracket_angle_bracket_left_equal,
133 angle_bracket_angle_bracket_left_pipe,
134 angle_bracket_angle_bracket_left_pipe_equal,
127 angle_bracket_right,135 angle_bracket_right,
128 angle_bracket_right_equal,136 angle_bracket_right_equal,
129 angle_bracket_angle_bracket_right,137 angle_bracket_angle_bracket_right,
...@@ -227,15 +235,21 @@ pub const Token = struct {...@@ -227,15 +235,21 @@ pub const Token = struct {
227 .plus_equal => "+=",235 .plus_equal => "+=",
228 .plus_percent => "+%",236 .plus_percent => "+%",
229 .plus_percent_equal => "+%=",237 .plus_percent_equal => "+%=",
238 .plus_pipe => "+|",
239 .plus_pipe_equal => "+|=",
230 .minus => "-",240 .minus => "-",
231 .minus_equal => "-=",241 .minus_equal => "-=",
232 .minus_percent => "-%",242 .minus_percent => "-%",
233 .minus_percent_equal => "-%=",243 .minus_percent_equal => "-%=",
244 .minus_pipe => "-|",
245 .minus_pipe_equal => "-|=",
234 .asterisk => "*",246 .asterisk => "*",
235 .asterisk_equal => "*=",247 .asterisk_equal => "*=",
236 .asterisk_asterisk => "**",248 .asterisk_asterisk => "**",
237 .asterisk_percent => "*%",249 .asterisk_percent => "*%",
238 .asterisk_percent_equal => "*%=",250 .asterisk_percent_equal => "*%=",
251 .asterisk_pipe => "*|",
252 .asterisk_pipe_equal => "*|=",
239 .arrow => "->",253 .arrow => "->",
240 .colon => ":",254 .colon => ":",
241 .slash => "/",255 .slash => "/",
...@@ -248,6 +262,8 @@ pub const Token = struct {...@@ -248,6 +262,8 @@ pub const Token = struct {
248 .angle_bracket_left_equal => "<=",262 .angle_bracket_left_equal => "<=",
249 .angle_bracket_angle_bracket_left => "<<",263 .angle_bracket_angle_bracket_left => "<<",
250 .angle_bracket_angle_bracket_left_equal => "<<=",264 .angle_bracket_angle_bracket_left_equal => "<<=",
265 .angle_bracket_angle_bracket_left_pipe => "<<|",
266 .angle_bracket_angle_bracket_left_pipe_equal => "<<|=",
251 .angle_bracket_right => ">",267 .angle_bracket_right => ">",
252 .angle_bracket_right_equal => ">=",268 .angle_bracket_right_equal => ">=",
253 .angle_bracket_angle_bracket_right => ">>",269 .angle_bracket_angle_bracket_right => ">>",
...@@ -352,8 +368,10 @@ pub const Tokenizer = struct {...@@ -352,8 +368,10 @@ pub const Tokenizer = struct {
352 pipe,368 pipe,
353 minus,369 minus,
354 minus_percent,370 minus_percent,
371 minus_pipe,
355 asterisk,372 asterisk,
356 asterisk_percent,373 asterisk_percent,
374 asterisk_pipe,
357 slash,375 slash,
358 line_comment_start,376 line_comment_start,
359 line_comment,377 line_comment,
...@@ -382,8 +400,10 @@ pub const Tokenizer = struct {...@@ -382,8 +400,10 @@ pub const Tokenizer = struct {
382 percent,400 percent,
383 plus,401 plus,
384 plus_percent,402 plus_percent,
403 plus_pipe,
385 angle_bracket_left,404 angle_bracket_left,
386 angle_bracket_angle_bracket_left,405 angle_bracket_angle_bracket_left,
406 angle_bracket_angle_bracket_left_pipe,
387 angle_bracket_right,407 angle_bracket_right,
388 angle_bracket_angle_bracket_right,408 angle_bracket_angle_bracket_right,
389 period,409 period,
...@@ -584,6 +604,9 @@ pub const Tokenizer = struct {...@@ -584,6 +604,9 @@ pub const Tokenizer = struct {
584 '%' => {604 '%' => {
585 state = .asterisk_percent;605 state = .asterisk_percent;
586 },606 },
607 '|' => {
608 state = .asterisk_pipe;
609 },
587 else => {610 else => {
588 result.tag = .asterisk;611 result.tag = .asterisk;
589 break;612 break;
...@@ -602,6 +625,18 @@ pub const Tokenizer = struct {...@@ -602,6 +625,18 @@ pub const Tokenizer = struct {
602 },625 },
603 },626 },
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
605 .percent => switch (c) {640 .percent => switch (c) {
606 '=' => {641 '=' => {
607 result.tag = .percent_equal;642 result.tag = .percent_equal;
...@@ -628,6 +663,9 @@ pub const Tokenizer = struct {...@@ -628,6 +663,9 @@ pub const Tokenizer = struct {
628 '%' => {663 '%' => {
629 state = .plus_percent;664 state = .plus_percent;
630 },665 },
666 '|' => {
667 state = .plus_pipe;
668 },
631 else => {669 else => {
632 result.tag = .plus;670 result.tag = .plus;
633 break;671 break;
...@@ -646,6 +684,18 @@ pub const Tokenizer = struct {...@@ -646,6 +684,18 @@ pub const Tokenizer = struct {
646 },684 },
647 },685 },
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
649 .caret => switch (c) {699 .caret => switch (c) {
650 '=' => {700 '=' => {
651 result.tag = .caret_equal;701 result.tag = .caret_equal;
...@@ -903,6 +953,9 @@ pub const Tokenizer = struct {...@@ -903,6 +953,9 @@ pub const Tokenizer = struct {
903 '%' => {953 '%' => {
904 state = .minus_percent;954 state = .minus_percent;
905 },955 },
956 '|' => {
957 state = .minus_pipe;
958 },
906 else => {959 else => {
907 result.tag = .minus;960 result.tag = .minus;
908 break;961 break;
...@@ -920,6 +973,17 @@ pub const Tokenizer = struct {...@@ -920,6 +973,17 @@ pub const Tokenizer = struct {
920 break;973 break;
921 },974 },
922 },975 },
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
924 .angle_bracket_left => switch (c) {988 .angle_bracket_left => switch (c) {
925 '<' => {989 '<' => {
...@@ -942,12 +1006,27 @@ pub const Tokenizer = struct {...@@ -942,12 +1006,27 @@ pub const Tokenizer = struct {
942 self.index += 1;1006 self.index += 1;
943 break;1007 break;
944 },1008 },
1009 '|' => {
1010 state = .angle_bracket_angle_bracket_left_pipe;
1011 },
945 else => {1012 else => {
946 result.tag = .angle_bracket_angle_bracket_left;1013 result.tag = .angle_bracket_angle_bracket_left;
947 break;1014 break;
948 },1015 },
949 },1016 },
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
951 .angle_bracket_right => switch (c) {1030 .angle_bracket_right => switch (c) {
952 '>' => {1031 '>' => {
953 state = .angle_bracket_angle_bracket_right;1032 state = .angle_bracket_angle_bracket_right;
...@@ -1936,6 +2015,24 @@ test "tokenizer - invalid token with unfinished escape right before eof" {...@@ -1936,6 +2015,24 @@ test "tokenizer - invalid token with unfinished escape right before eof" {
1936 try testTokenize("'\\u", &.{.invalid});2015 try testTokenize("'\\u", &.{.invalid});
1937}2016}
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
1939fn testTokenize(source: [:0]const u8, expected_tokens: []const Token.Tag) !void {2036fn testTokenize(source: [:0]const u8, expected_tokens: []const Token.Tag) !void {
1940 var tokenizer = Tokenizer.init(source);2037 var tokenizer = Tokenizer.init(source);
1941 for (expected_tokens) |expected_token_id| {2038 for (expected_tokens) |expected_token_id| {
src/Air.zig+28
...@@ -44,6 +44,11 @@ pub const Inst = struct {...@@ -44,6 +44,11 @@ pub const Inst = struct {
44 /// is the same as both operands.44 /// is the same as both operands.
45 /// Uses the `bin_op` field.45 /// Uses the `bin_op` field.
46 addwrap,46 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,
47 /// Float or integer subtraction. For integers, wrapping is undefined behavior.52 /// Float or integer subtraction. For integers, wrapping is undefined behavior.
48 /// Both operands are guaranteed to be the same type, and the result type53 /// Both operands are guaranteed to be the same type, and the result type
49 /// is the same as both operands.54 /// is the same as both operands.
...@@ -54,6 +59,11 @@ pub const Inst = struct {...@@ -54,6 +59,11 @@ pub const Inst = struct {
54 /// is the same as both operands.59 /// is the same as both operands.
55 /// Uses the `bin_op` field.60 /// Uses the `bin_op` field.
56 subwrap,61 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,
57 /// Float or integer multiplication. For integers, wrapping is undefined behavior.67 /// Float or integer multiplication. For integers, wrapping is undefined behavior.
58 /// Both operands are guaranteed to be the same type, and the result type68 /// Both operands are guaranteed to be the same type, and the result type
59 /// is the same as both operands.69 /// is the same as both operands.
...@@ -64,6 +74,11 @@ pub const Inst = struct {...@@ -64,6 +74,11 @@ pub const Inst = struct {
64 /// is the same as both operands.74 /// is the same as both operands.
65 /// Uses the `bin_op` field.75 /// Uses the `bin_op` field.
66 mulwrap,76 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,
67 /// Integer or float division. For integers, wrapping is undefined behavior.82 /// Integer or float division. For integers, wrapping is undefined behavior.
68 /// Both operands are guaranteed to be the same type, and the result type83 /// Both operands are guaranteed to be the same type, and the result type
69 /// is the same as both operands.84 /// is the same as both operands.
...@@ -110,6 +125,14 @@ pub const Inst = struct {...@@ -110,6 +125,14 @@ pub const Inst = struct {
110 /// Shift left. `<<`125 /// Shift left. `<<`
111 /// Uses the `bin_op` field.126 /// Uses the `bin_op` field.
112 shl,127 shl,
128 /// Shift left; For unsigned integers, the shift produces a poison value if it shifts
129 /// out any non-zero bits. For signed integers, the shift produces a poison value if
130 /// it shifts out any bits that disagree with the resultant sign bit.
131 /// Uses the `bin_op` field.
132 shl_exact,
133 /// Shift left saturating. `<<|`
134 /// Uses the `bin_op` field.
135 shl_sat,
113 /// Bitwise XOR. `^`136 /// Bitwise XOR. `^`
114 /// Uses the `bin_op` field.137 /// Uses the `bin_op` field.
115 xor,138 xor,
...@@ -568,10 +591,13 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -568,10 +591,13 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
568591
569 .add,592 .add,
570 .addwrap,593 .addwrap,
594 .add_sat,
571 .sub,595 .sub,
572 .subwrap,596 .subwrap,
597 .sub_sat,
573 .mul,598 .mul,
574 .mulwrap,599 .mulwrap,
600 .mul_sat,
575 .div,601 .div,
576 .rem,602 .rem,
577 .mod,603 .mod,
...@@ -582,6 +608,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {...@@ -582,6 +608,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
582 .ptr_sub,608 .ptr_sub,
583 .shr,609 .shr,
584 .shl,610 .shl,
611 .shl_exact,
612 .shl_sat,
585 => return air.typeOf(datas[inst].bin_op.lhs),613 => return air.typeOf(datas[inst].bin_op.lhs),
586614
587 .cmp_lt,615 .cmp_lt,
src/AstGen.zig+97-45
...@@ -317,29 +317,37 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins...@@ -317,29 +317,37 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Ins
317 .assign,317 .assign,
318 .assign_bit_and,318 .assign_bit_and,
319 .assign_bit_or,319 .assign_bit_or,
320 .assign_bit_shift_left,320 .assign_shl,
321 .assign_bit_shift_right,321 .assign_shl_sat,
322 .assign_shr,
322 .assign_bit_xor,323 .assign_bit_xor,
323 .assign_div,324 .assign_div,
324 .assign_sub,325 .assign_sub,
325 .assign_sub_wrap,326 .assign_sub_wrap,
327 .assign_sub_sat,
326 .assign_mod,328 .assign_mod,
327 .assign_add,329 .assign_add,
328 .assign_add_wrap,330 .assign_add_wrap,
331 .assign_add_sat,
329 .assign_mul,332 .assign_mul,
330 .assign_mul_wrap,333 .assign_mul_wrap,
334 .assign_mul_sat,
331 .add,335 .add,
332 .add_wrap,336 .add_wrap,
337 .add_sat,
333 .sub,338 .sub,
334 .sub_wrap,339 .sub_wrap,
340 .sub_sat,
335 .mul,341 .mul,
336 .mul_wrap,342 .mul_wrap,
343 .mul_sat,
337 .div,344 .div,
338 .mod,345 .mod,
339 .bit_and,346 .bit_and,
340 .bit_or,347 .bit_or,
341 .bit_shift_left,348 .shl,
342 .bit_shift_right,349 .shl_sat,
350 .shr,
343 .bit_xor,351 .bit_xor,
344 .bang_equal,352 .bang_equal,
345 .equal_equal,353 .equal_equal,
...@@ -522,11 +530,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -522,11 +530,15 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
522 return rvalue(gz, rl, .void_value, node);530 return rvalue(gz, rl, .void_value, node);
523 },531 },
524532
525 .assign_bit_shift_left => {533 .assign_shl => {
526 try assignShift(gz, scope, node, .shl);534 try assignShift(gz, scope, node, .shl);
527 return rvalue(gz, rl, .void_value, node);535 return rvalue(gz, rl, .void_value, node);
528 },536 },
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 => {
530 try assignShift(gz, scope, node, .shr);542 try assignShift(gz, scope, node, .shr);
531 return rvalue(gz, rl, .void_value, node);543 return rvalue(gz, rl, .void_value, node);
532 },544 },
...@@ -555,6 +567,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -555,6 +567,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
555 try assignOp(gz, scope, node, .subwrap);567 try assignOp(gz, scope, node, .subwrap);
556 return rvalue(gz, rl, .void_value, node);568 return rvalue(gz, rl, .void_value, node);
557 },569 },
570 .assign_sub_sat => {
571 try assignOp(gz, scope, node, .sub_sat);
572 return rvalue(gz, rl, .void_value, node);
573 },
558 .assign_mod => {574 .assign_mod => {
559 try assignOp(gz, scope, node, .mod_rem);575 try assignOp(gz, scope, node, .mod_rem);
560 return rvalue(gz, rl, .void_value, node);576 return rvalue(gz, rl, .void_value, node);
...@@ -567,6 +583,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -567,6 +583,10 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
567 try assignOp(gz, scope, node, .addwrap);583 try assignOp(gz, scope, node, .addwrap);
568 return rvalue(gz, rl, .void_value, node);584 return rvalue(gz, rl, .void_value, node);
569 },585 },
586 .assign_add_sat => {
587 try assignOp(gz, scope, node, .add_sat);
588 return rvalue(gz, rl, .void_value, node);
589 },
570 .assign_mul => {590 .assign_mul => {
571 try assignOp(gz, scope, node, .mul);591 try assignOp(gz, scope, node, .mul);
572 return rvalue(gz, rl, .void_value, node);592 return rvalue(gz, rl, .void_value, node);
...@@ -575,19 +595,28 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -575,19 +595,28 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
575 try assignOp(gz, scope, node, .mulwrap);595 try assignOp(gz, scope, node, .mulwrap);
576 return rvalue(gz, rl, .void_value, node);596 return rvalue(gz, rl, .void_value, node);
577 },597 },
598 .assign_mul_sat => {
599 try assignOp(gz, scope, node, .mul_sat);
600 return rvalue(gz, rl, .void_value, node);
601 },
578602
579 // zig fmt: off603 // zig fmt: off
580 .bit_shift_left => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),604 .shl => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
581 .bit_shift_right => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),605 .shr => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
582606
583 .add => return simpleBinOp(gz, scope, rl, node, .add),607 .add => return simpleBinOp(gz, scope, rl, node, .add),
584 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),608 .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
609 .add_sat => return simpleBinOp(gz, scope, rl, node, .add_sat),
585 .sub => return simpleBinOp(gz, scope, rl, node, .sub),610 .sub => return simpleBinOp(gz, scope, rl, node, .sub),
586 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),611 .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
612 .sub_sat => return simpleBinOp(gz, scope, rl, node, .sub_sat),
587 .mul => return simpleBinOp(gz, scope, rl, node, .mul),613 .mul => return simpleBinOp(gz, scope, rl, node, .mul),
588 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),614 .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
615 .mul_sat => return simpleBinOp(gz, scope, rl, node, .mul_sat),
589 .div => return simpleBinOp(gz, scope, rl, node, .div),616 .div => return simpleBinOp(gz, scope, rl, node, .div),
590 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),617 .mod => return simpleBinOp(gz, scope, rl, node, .mod_rem),
618 .shl_sat => return simpleBinOp(gz, scope, rl, node, .shl_sat),
619
591 .bit_and => {620 .bit_and => {
592 const current_ampersand_token = main_tokens[node];621 const current_ampersand_token = main_tokens[node];
593 if (token_tags[current_ampersand_token + 1] == .ampersand) {622 if (token_tags[current_ampersand_token + 1] == .ampersand) {
...@@ -1898,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod...@@ -1898,8 +1927,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
18981927
1899 .assign => try assign(gz, scope, statement),1928 .assign => try assign(gz, scope, statement),
19001929
1901 .assign_bit_shift_left => try assignShift(gz, scope, statement, .shl),1930 .assign_shl => try assignShift(gz, scope, statement, .shl),
1902 .assign_bit_shift_right => try assignShift(gz, scope, statement, .shr),1931 .assign_shr => try assignShift(gz, scope, statement, .shr),
19031932
1904 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),1933 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
1905 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),1934 .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...@@ -1949,6 +1978,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1949 // ZIR instructions that might be a type other than `noreturn` or `void`.1978 // ZIR instructions that might be a type other than `noreturn` or `void`.
1950 .add,1979 .add,
1951 .addwrap,1980 .addwrap,
1981 .add_sat,
1952 .param,1982 .param,
1953 .param_comptime,1983 .param_comptime,
1954 .param_anytype,1984 .param_anytype,
...@@ -2015,12 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2015,12 +2045,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2015 .mod_rem,2045 .mod_rem,
2016 .mul,2046 .mul,
2017 .mulwrap,2047 .mulwrap,
2048 .mul_sat,
2018 .ref,2049 .ref,
2019 .shl,2050 .shl,
2051 .shl_sat,
2020 .shr,2052 .shr,
2021 .str,2053 .str,
2022 .sub,2054 .sub,
2023 .subwrap,2055 .subwrap,
2056 .sub_sat,
2024 .negate,2057 .negate,
2025 .negate_wrap,2058 .negate_wrap,
2026 .typeof,2059 .typeof,
...@@ -2708,6 +2741,24 @@ fn assignShift(...@@ -2708,6 +2741,24 @@ fn assignShift(
2708 _ = try gz.addBin(.store, lhs_ptr, result);2741 _ = try gz.addBin(.store, lhs_ptr, result);
2709}2742}
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
2711fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {2762fn boolNot(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
2712 const astgen = gz.astgen;2763 const astgen = gz.astgen;
2713 const tree = astgen.tree;2764 const tree = astgen.tree;
...@@ -7483,11 +7534,6 @@ fn builtinCall(...@@ -7483,11 +7534,6 @@ fn builtinCall(
7483 return rvalue(gz, rl, result, node);7534 return rvalue(gz, rl, result, node);
7484 },7535 },
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
7491 .atomic_load => {7537 .atomic_load => {
7492 const int_type = try typeExpr(gz, scope, params[0]);7538 const int_type = try typeExpr(gz, scope, params[0]);
7493 // TODO allow this pointer type to be volatile7539 // TODO allow this pointer type to be volatile
...@@ -7882,24 +7928,6 @@ fn overflowArithmetic(...@@ -7882,24 +7928,6 @@ fn overflowArithmetic(
7882 return rvalue(gz, rl, result, node);7928 return rvalue(gz, rl, result, node);
7883}7929}
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
7903fn callExpr(7931fn callExpr(
7904 gz: *GenZir,7932 gz: *GenZir,
7905 scope: *Scope,7933 scope: *Scope,
...@@ -8119,27 +8147,33 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool...@@ -8119,27 +8147,33 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
8119 .asm_simple,8147 .asm_simple,
8120 .add,8148 .add,
8121 .add_wrap,8149 .add_wrap,
8150 .add_sat,
8122 .array_cat,8151 .array_cat,
8123 .array_mult,8152 .array_mult,
8124 .assign,8153 .assign,
8125 .assign_bit_and,8154 .assign_bit_and,
8126 .assign_bit_or,8155 .assign_bit_or,
8127 .assign_bit_shift_left,8156 .assign_shl,
8128 .assign_bit_shift_right,8157 .assign_shl_sat,
8158 .assign_shr,
8129 .assign_bit_xor,8159 .assign_bit_xor,
8130 .assign_div,8160 .assign_div,
8131 .assign_sub,8161 .assign_sub,
8132 .assign_sub_wrap,8162 .assign_sub_wrap,
8163 .assign_sub_sat,
8133 .assign_mod,8164 .assign_mod,
8134 .assign_add,8165 .assign_add,
8135 .assign_add_wrap,8166 .assign_add_wrap,
8167 .assign_add_sat,
8136 .assign_mul,8168 .assign_mul,
8137 .assign_mul_wrap,8169 .assign_mul_wrap,
8170 .assign_mul_sat,
8138 .bang_equal,8171 .bang_equal,
8139 .bit_and,8172 .bit_and,
8140 .bit_or,8173 .bit_or,
8141 .bit_shift_left,8174 .shl,
8142 .bit_shift_right,8175 .shl_sat,
8176 .shr,
8143 .bit_xor,8177 .bit_xor,
8144 .bool_and,8178 .bool_and,
8145 .bool_or,8179 .bool_or,
...@@ -8154,10 +8188,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool...@@ -8154,10 +8188,12 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index) bool
8154 .mod,8188 .mod,
8155 .mul,8189 .mul,
8156 .mul_wrap,8190 .mul_wrap,
8191 .mul_sat,
8157 .switch_range,8192 .switch_range,
8158 .field_access,8193 .field_access,
8159 .sub,8194 .sub,
8160 .sub_wrap,8195 .sub_wrap,
8196 .sub_sat,
8161 .slice,8197 .slice,
8162 .slice_open,8198 .slice_open,
8163 .slice_sentinel,8199 .slice_sentinel,
...@@ -8352,27 +8388,33 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never...@@ -8352,27 +8388,33 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
8352 .tagged_union_enum_tag_trailing,8388 .tagged_union_enum_tag_trailing,
8353 .add,8389 .add,
8354 .add_wrap,8390 .add_wrap,
8391 .add_sat,
8355 .array_cat,8392 .array_cat,
8356 .array_mult,8393 .array_mult,
8357 .assign,8394 .assign,
8358 .assign_bit_and,8395 .assign_bit_and,
8359 .assign_bit_or,8396 .assign_bit_or,
8360 .assign_bit_shift_left,8397 .assign_shl,
8361 .assign_bit_shift_right,8398 .assign_shl_sat,
8399 .assign_shr,
8362 .assign_bit_xor,8400 .assign_bit_xor,
8363 .assign_div,8401 .assign_div,
8364 .assign_sub,8402 .assign_sub,
8365 .assign_sub_wrap,8403 .assign_sub_wrap,
8404 .assign_sub_sat,
8366 .assign_mod,8405 .assign_mod,
8367 .assign_add,8406 .assign_add,
8368 .assign_add_wrap,8407 .assign_add_wrap,
8408 .assign_add_sat,
8369 .assign_mul,8409 .assign_mul,
8370 .assign_mul_wrap,8410 .assign_mul_wrap,
8411 .assign_mul_sat,
8371 .bang_equal,8412 .bang_equal,
8372 .bit_and,8413 .bit_and,
8373 .bit_or,8414 .bit_or,
8374 .bit_shift_left,8415 .shl,
8375 .bit_shift_right,8416 .shl_sat,
8417 .shr,
8376 .bit_xor,8418 .bit_xor,
8377 .bool_and,8419 .bool_and,
8378 .bool_or,8420 .bool_or,
...@@ -8387,9 +8429,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never...@@ -8387,9 +8429,11 @@ fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) enum { never
8387 .mod,8429 .mod,
8388 .mul,8430 .mul,
8389 .mul_wrap,8431 .mul_wrap,
8432 .mul_sat,
8390 .switch_range,8433 .switch_range,
8391 .sub,8434 .sub,
8392 .sub_wrap,8435 .sub_wrap,
8436 .sub_sat,
8393 .slice,8437 .slice,
8394 .slice_open,8438 .slice_open,
8395 .slice_sentinel,8439 .slice_sentinel,
...@@ -8524,27 +8568,33 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -8524,27 +8568,33 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
8524 .asm_simple,8568 .asm_simple,
8525 .add,8569 .add,
8526 .add_wrap,8570 .add_wrap,
8571 .add_sat,
8527 .array_cat,8572 .array_cat,
8528 .array_mult,8573 .array_mult,
8529 .assign,8574 .assign,
8530 .assign_bit_and,8575 .assign_bit_and,
8531 .assign_bit_or,8576 .assign_bit_or,
8532 .assign_bit_shift_left,8577 .assign_shl,
8533 .assign_bit_shift_right,8578 .assign_shl_sat,
8579 .assign_shr,
8534 .assign_bit_xor,8580 .assign_bit_xor,
8535 .assign_div,8581 .assign_div,
8536 .assign_sub,8582 .assign_sub,
8537 .assign_sub_wrap,8583 .assign_sub_wrap,
8584 .assign_sub_sat,
8538 .assign_mod,8585 .assign_mod,
8539 .assign_add,8586 .assign_add,
8540 .assign_add_wrap,8587 .assign_add_wrap,
8588 .assign_add_sat,
8541 .assign_mul,8589 .assign_mul,
8542 .assign_mul_wrap,8590 .assign_mul_wrap,
8591 .assign_mul_sat,
8543 .bang_equal,8592 .bang_equal,
8544 .bit_and,8593 .bit_and,
8545 .bit_or,8594 .bit_or,
8546 .bit_shift_left,8595 .shl,
8547 .bit_shift_right,8596 .shl_sat,
8597 .shr,
8548 .bit_xor,8598 .bit_xor,
8549 .bool_and,8599 .bool_and,
8550 .bool_or,8600 .bool_or,
...@@ -8559,10 +8609,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {...@@ -8559,10 +8609,12 @@ fn nodeImpliesRuntimeBits(tree: *const Ast, start_node: Ast.Node.Index) bool {
8559 .mod,8609 .mod,
8560 .mul,8610 .mul,
8561 .mul_wrap,8611 .mul_wrap,
8612 .mul_sat,
8562 .switch_range,8613 .switch_range,
8563 .field_access,8614 .field_access,
8564 .sub,8615 .sub,
8565 .sub_wrap,8616 .sub_wrap,
8617 .sub_sat,
8566 .slice,8618 .slice,
8567 .slice_open,8619 .slice_open,
8568 .slice_sentinel,8620 .slice_sentinel,
src/BuiltinFn.zig-32
...@@ -2,7 +2,6 @@ const std = @import("std");...@@ -2,7 +2,6 @@ const std = @import("std");
22
3pub const Tag = enum {3pub const Tag = enum {
4 add_with_overflow,4 add_with_overflow,
5 add_with_saturation,
6 align_cast,5 align_cast,
7 align_of,6 align_of,
8 as,7 as,
...@@ -66,7 +65,6 @@ pub const Tag = enum {...@@ -66,7 +65,6 @@ pub const Tag = enum {
66 wasm_memory_grow,65 wasm_memory_grow,
67 mod,66 mod,
68 mul_with_overflow,67 mul_with_overflow,
69 mul_with_saturation,
70 panic,68 panic,
71 pop_count,69 pop_count,
72 ptr_cast,70 ptr_cast,
...@@ -81,12 +79,10 @@ pub const Tag = enum {...@@ -81,12 +79,10 @@ pub const Tag = enum {
81 set_runtime_safety,79 set_runtime_safety,
82 shl_exact,80 shl_exact,
83 shl_with_overflow,81 shl_with_overflow,
84 shl_with_saturation,
85 shr_exact,82 shr_exact,
86 shuffle,83 shuffle,
87 size_of,84 size_of,
88 splat,85 splat,
89 sub_with_saturation,
90 reduce,86 reduce,
91 src,87 src,
92 sqrt,88 sqrt,
...@@ -531,34 +527,6 @@ pub const list = list: {...@@ -531,34 +527,6 @@ pub const list = list: {
531 .param_count = 2,527 .param_count = 2,
532 },528 },
533 },529 },
534 .{
535 "@addWithSaturation",
536 .{
537 .tag = .add_with_saturation,
538 .param_count = 2,
539 },
540 },
541 .{
542 "@subWithSaturation",
543 .{
544 .tag = .sub_with_saturation,
545 .param_count = 2,
546 },
547 },
548 .{
549 "@mulWithSaturation",
550 .{
551 .tag = .mul_with_saturation,
552 .param_count = 2,
553 },
554 },
555 .{
556 "@shlWithSaturation",
557 .{
558 .tag = .shl_with_saturation,
559 .param_count = 2,
560 },
561 },
562 .{530 .{
563 "@memcpy",531 "@memcpy",
564 .{532 .{
src/Liveness.zig+5
...@@ -226,10 +226,13 @@ fn analyzeInst(...@@ -226,10 +226,13 @@ fn analyzeInst(
226 switch (inst_tags[inst]) {226 switch (inst_tags[inst]) {
227 .add,227 .add,
228 .addwrap,228 .addwrap,
229 .add_sat,
229 .sub,230 .sub,
230 .subwrap,231 .subwrap,
232 .sub_sat,
231 .mul,233 .mul,
232 .mulwrap,234 .mulwrap,
235 .mul_sat,
233 .div,236 .div,
234 .rem,237 .rem,
235 .mod,238 .mod,
...@@ -252,6 +255,8 @@ fn analyzeInst(...@@ -252,6 +255,8 @@ fn analyzeInst(
252 .ptr_elem_val,255 .ptr_elem_val,
253 .ptr_ptr_elem_val,256 .ptr_ptr_elem_val,
254 .shl,257 .shl,
258 .shl_exact,
259 .shl_sat,
255 .shr,260 .shr,
256 .atomic_store_unordered,261 .atomic_store_unordered,
257 .atomic_store_monotonic,262 .atomic_store_monotonic,
src/Sema.zig+113-34
...@@ -246,7 +246,6 @@ pub fn analyzeBody(...@@ -246,7 +246,6 @@ pub fn analyzeBody(
246 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),246 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
247 .ref => try sema.zirRef(block, inst),247 .ref => try sema.zirRef(block, inst),
248 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),248 .ret_err_value_code => try sema.zirRetErrValueCode(block, inst),
249 .shl => try sema.zirShl(block, inst),
250 .shr => try sema.zirShr(block, inst),249 .shr => try sema.zirShr(block, inst),
251 .slice_end => try sema.zirSliceEnd(block, inst),250 .slice_end => try sema.zirSliceEnd(block, inst),
252 .slice_sentinel => try sema.zirSliceSentinel(block, inst),251 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
...@@ -319,7 +318,6 @@ pub fn analyzeBody(...@@ -319,7 +318,6 @@ pub fn analyzeBody(
319 .div_exact => try sema.zirDivExact(block, inst),318 .div_exact => try sema.zirDivExact(block, inst),
320 .div_floor => try sema.zirDivFloor(block, inst),319 .div_floor => try sema.zirDivFloor(block, inst),
321 .div_trunc => try sema.zirDivTrunc(block, inst),320 .div_trunc => try sema.zirDivTrunc(block, inst),
322 .shl_exact => try sema.zirShlExact(block, inst),
323 .shr_exact => try sema.zirShrExact(block, inst),321 .shr_exact => try sema.zirShrExact(block, inst),
324 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),322 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),
325 .offset_of => try sema.zirOffsetOf(block, inst),323 .offset_of => try sema.zirOffsetOf(block, inst),
...@@ -363,14 +361,21 @@ pub fn analyzeBody(...@@ -363,14 +361,21 @@ pub fn analyzeBody(
363361
364 .add => try sema.zirArithmetic(block, inst, .add),362 .add => try sema.zirArithmetic(block, inst, .add),
365 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),363 .addwrap => try sema.zirArithmetic(block, inst, .addwrap),
364 .add_sat => try sema.zirArithmetic(block, inst, .add_sat),
366 .div => try sema.zirArithmetic(block, inst, .div),365 .div => try sema.zirArithmetic(block, inst, .div),
367 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),366 .mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
368 .mod => try sema.zirArithmetic(block, inst, .mod),367 .mod => try sema.zirArithmetic(block, inst, .mod),
369 .rem => try sema.zirArithmetic(block, inst, .rem),368 .rem => try sema.zirArithmetic(block, inst, .rem),
370 .mul => try sema.zirArithmetic(block, inst, .mul),369 .mul => try sema.zirArithmetic(block, inst, .mul),
371 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),370 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap),
371 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat),
372 .sub => try sema.zirArithmetic(block, inst, .sub),372 .sub => try sema.zirArithmetic(block, inst, .sub),
373 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),373 .subwrap => try sema.zirArithmetic(block, inst, .subwrap),
374 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
375
376 .shl => try sema.zirShl(block, inst, .shl),
377 .shl_exact => try sema.zirShl(block, inst, .shl_exact),
378 .shl_sat => try sema.zirShl(block, inst, .shl_sat),
374379
375 // Instructions that we know to *always* be noreturn based solely on their tag.380 // Instructions that we know to *always* be noreturn based solely on their tag.
376 // These functions match the return type of analyzeBody so that we can381 // These functions match the return type of analyzeBody so that we can
...@@ -694,10 +699,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -694,10 +699,6 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
694 .c_define => return sema.zirCDefine( block, extended),699 .c_define => return sema.zirCDefine( block, extended),
695 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),700 .wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
696 .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),701 .wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),
697 .add_with_saturation=> 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),
701 // zig fmt: on702 // zig fmt: on
702 }703 }
703}704}
...@@ -5874,7 +5875,12 @@ fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co...@@ -5874,7 +5875,12 @@ fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
5874 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});5875 return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{});
5875}5876}
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 {
5878 const tracy = trace(@src());5884 const tracy = trace(@src());
5879 defer tracy.end();5885 defer tracy.end();
58805886
...@@ -5885,6 +5891,8 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A...@@ -5885,6 +5891,8 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
5885 const lhs = sema.resolveInst(extra.lhs);5891 const lhs = sema.resolveInst(extra.lhs);
5886 const rhs = sema.resolveInst(extra.rhs);5892 const rhs = sema.resolveInst(extra.rhs);
58875893
5894 // TODO coerce rhs if air_tag is not shl_sat
5895
5888 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);5896 const maybe_lhs_val = try sema.resolveMaybeUndefVal(block, lhs_src, lhs);
5889 const maybe_rhs_val = try sema.resolveMaybeUndefVal(block, rhs_src, rhs);5897 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...@@ -5900,6 +5908,12 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
5900 return sema.addConstant(lhs_ty, lhs_val);5908 return sema.addConstant(lhs_ty, lhs_val);
5901 }5909 }
5902 const val = try lhs_val.shl(rhs_val, sema.arena);5910 const val = try lhs_val.shl(rhs_val, sema.arena);
5911 switch (air_tag) {
5912 .shl_exact => return sema.mod.fail(&block.base, lhs_src, "TODO implement Sema for comptime shl_exact", .{}),
5913 .shl_sat => return sema.mod.fail(&block.base, lhs_src, "TODO implement Sema for comptime shl_sat", .{}),
5914 .shl => {},
5915 else => unreachable,
5916 }
5903 return sema.addConstant(lhs_ty, val);5917 return sema.addConstant(lhs_ty, val);
5904 } else rs: {5918 } else rs: {
5905 if (maybe_rhs_val) |rhs_val| {5919 if (maybe_rhs_val) |rhs_val| {
...@@ -5908,8 +5922,10 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A...@@ -5908,8 +5922,10 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
5908 break :rs lhs_src;5922 break :rs lhs_src;
5909 };5923 };
59105924
5925 // TODO: insert runtime safety check for shl_exact
5926
5911 try sema.requireRuntimeBlock(block, runtime_src);5927 try sema.requireRuntimeBlock(block, runtime_src);
5912 return block.addBinOp(.shl, lhs, rhs);5928 return block.addBinOp(air_tag, lhs, rhs);
5913}5929}
59145930
5915fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {5931fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -6200,19 +6216,6 @@ fn zirOverflowArithmetic(...@@ -6200,19 +6216,6 @@ fn zirOverflowArithmetic(
6200 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});6216 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});
6201}6217}
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
6216fn analyzeArithmetic(6219fn analyzeArithmetic(
6217 sema: *Sema,6220 sema: *Sema,
6218 block: *Scope.Block,6221 block: *Scope.Block,
...@@ -6354,8 +6357,7 @@ fn analyzeArithmetic(...@@ -6354,8 +6357,7 @@ fn analyzeArithmetic(
6354 },6357 },
6355 .addwrap => {6358 .addwrap => {
6356 // Integers only; floats are checked above.6359 // Integers only; floats are checked above.
6357 // If either of the operands are zero, then the other operand is6360 // If either of the operands are zero, the other operand is returned.
6358 // returned, even if it is undefined.
6359 // If either of the operands are undefined, the result is undefined.6361 // If either of the operands are undefined, the result is undefined.
6360 if (maybe_lhs_val) |lhs_val| {6362 if (maybe_lhs_val) |lhs_val| {
6361 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {6363 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
...@@ -6377,6 +6379,30 @@ fn analyzeArithmetic(...@@ -6377,6 +6379,30 @@ fn analyzeArithmetic(
6377 } else break :rs .{ .src = lhs_src, .air_tag = .addwrap };6379 } else break :rs .{ .src = lhs_src, .air_tag = .addwrap };
6378 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };6380 } else break :rs .{ .src = rhs_src, .air_tag = .addwrap };
6379 },6381 },
6382 .add_sat => {
6383 // For both integers and floats:
6384 // If either of the operands are zero, then the other operand is returned.
6385 // If either of the operands are undefined, the result is undefined.
6386 if (maybe_lhs_val) |lhs_val| {
6387 if (!lhs_val.isUndef() and lhs_val.compareWithZero(.eq)) {
6388 return casted_rhs;
6389 }
6390 }
6391 if (maybe_rhs_val) |rhs_val| {
6392 if (rhs_val.isUndef()) {
6393 return sema.addConstUndef(scalar_type);
6394 }
6395 if (rhs_val.compareWithZero(.eq)) {
6396 return casted_lhs;
6397 }
6398 if (maybe_lhs_val) |lhs_val| {
6399 return sema.addConstant(
6400 scalar_type,
6401 try lhs_val.numberAddSat(rhs_val, scalar_type, sema.arena, target),
6402 );
6403 } else break :rs .{ .src = lhs_src, .air_tag = .add_sat };
6404 } else break :rs .{ .src = rhs_src, .air_tag = .add_sat };
6405 },
6380 .sub => {6406 .sub => {
6381 // For integers:6407 // For integers:
6382 // If the rhs is zero, then the other operand is6408 // If the rhs is zero, then the other operand is
...@@ -6444,6 +6470,30 @@ fn analyzeArithmetic(...@@ -6444,6 +6470,30 @@ fn analyzeArithmetic(
6444 } else break :rs .{ .src = rhs_src, .air_tag = .subwrap };6470 } else break :rs .{ .src = rhs_src, .air_tag = .subwrap };
6445 } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };6471 } else break :rs .{ .src = lhs_src, .air_tag = .subwrap };
6446 },6472 },
6473 .sub_sat => {
6474 // For both integers and floats:
6475 // If the RHS is zero, result is LHS.
6476 // If either of the operands are undefined, result is undefined.
6477 if (maybe_rhs_val) |rhs_val| {
6478 if (rhs_val.isUndef()) {
6479 return sema.addConstUndef(scalar_type);
6480 }
6481 if (rhs_val.compareWithZero(.eq)) {
6482 return casted_lhs;
6483 }
6484 }
6485 if (maybe_lhs_val) |lhs_val| {
6486 if (lhs_val.isUndef()) {
6487 return sema.addConstUndef(scalar_type);
6488 }
6489 if (maybe_rhs_val) |rhs_val| {
6490 return sema.addConstant(
6491 scalar_type,
6492 try lhs_val.numberSubSat(rhs_val, scalar_type, sema.arena, target),
6493 );
6494 } else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
6495 } else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
6496 },
6447 .div => {6497 .div => {
6448 // For integers:6498 // For integers:
6449 // If the lhs is zero, then zero is returned regardless of rhs.6499 // If the lhs is zero, then zero is returned regardless of rhs.
...@@ -6562,10 +6612,9 @@ fn analyzeArithmetic(...@@ -6562,10 +6612,9 @@ fn analyzeArithmetic(
6562 },6612 },
6563 .mulwrap => {6613 .mulwrap => {
6564 // Integers only; floats are handled above.6614 // Integers only; floats are handled above.
6565 // If either of the operands are zero, the result is zero.6615 // If either of the operands are zero, result is zero.
6566 // If either of the operands are one, the result is the other6616 // If either of the operands are one, result is the other operand.
6567 // operand, even if it is undefined.6617 // If either of the operands are undefined, result is undefined.
6568 // If either of the operands are undefined, the result is undefined.
6569 if (maybe_lhs_val) |lhs_val| {6618 if (maybe_lhs_val) |lhs_val| {
6570 if (!lhs_val.isUndef()) {6619 if (!lhs_val.isUndef()) {
6571 if (lhs_val.compareWithZero(.eq)) {6620 if (lhs_val.compareWithZero(.eq)) {
...@@ -6597,6 +6646,42 @@ fn analyzeArithmetic(...@@ -6597,6 +6646,42 @@ fn analyzeArithmetic(
6597 } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap };6646 } else break :rs .{ .src = lhs_src, .air_tag = .mulwrap };
6598 } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };6647 } else break :rs .{ .src = rhs_src, .air_tag = .mulwrap };
6599 },6648 },
6649 .mul_sat => {
6650 // For both integers and floats:
6651 // If either of the operands are zero, result is zero.
6652 // If either of the operands are one, result is the other operand.
6653 // If either of the operands are undefined, result is undefined.
6654 if (maybe_lhs_val) |lhs_val| {
6655 if (!lhs_val.isUndef()) {
6656 if (lhs_val.compareWithZero(.eq)) {
6657 return sema.addConstant(scalar_type, Value.zero);
6658 }
6659 if (lhs_val.compare(.eq, Value.one, scalar_type)) {
6660 return casted_rhs;
6661 }
6662 }
6663 }
6664 if (maybe_rhs_val) |rhs_val| {
6665 if (rhs_val.isUndef()) {
6666 return sema.addConstUndef(scalar_type);
6667 }
6668 if (rhs_val.compareWithZero(.eq)) {
6669 return sema.addConstant(scalar_type, Value.zero);
6670 }
6671 if (rhs_val.compare(.eq, Value.one, scalar_type)) {
6672 return casted_lhs;
6673 }
6674 if (maybe_lhs_val) |lhs_val| {
6675 if (lhs_val.isUndef()) {
6676 return sema.addConstUndef(scalar_type);
6677 }
6678 return sema.addConstant(
6679 scalar_type,
6680 try lhs_val.numberMulSat(rhs_val, scalar_type, sema.arena, target),
6681 );
6682 } else break :rs .{ .src = lhs_src, .air_tag = .mul_sat };
6683 } else break :rs .{ .src = rhs_src, .air_tag = .mul_sat };
6684 },
6600 .mod_rem => {6685 .mod_rem => {
6601 // For integers:6686 // For integers:
6602 // Either operand being undef is a compile error because there exists6687 // Either operand being undef is a compile error because there exists
...@@ -7846,7 +7931,7 @@ fn analyzeRet(...@@ -7846,7 +7931,7 @@ fn analyzeRet(
7846fn floatOpAllowed(tag: Zir.Inst.Tag) bool {7931fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
7847 // extend this swich as additional operators are implemented7932 // extend this swich as additional operators are implemented
7848 return switch (tag) {7933 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,
7850 else => false,7935 else => false,
7851 };7936 };
7852}7937}
...@@ -8513,12 +8598,6 @@ fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -8513,12 +8598,6 @@ fn zirDivTrunc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
8513 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});8598 return sema.mod.fail(&block.base, src, "TODO: Sema.zirDivTrunc", .{});
8514}8599}
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
8522fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {8601fn zirShrExact(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8523 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;8602 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
8524 const src = inst_data.src();8603 const src = inst_data.src();
src/Zir.zig+71-72
...@@ -126,6 +126,64 @@ pub const Inst = struct {...@@ -126,6 +126,64 @@ pub const Inst = struct {
126 /// Twos complement wrapping integer addition.126 /// Twos complement wrapping integer addition.
127 /// Uses the `pl_node` union field. Payload is `Bin`.127 /// Uses the `pl_node` union field. Payload is `Bin`.
128 addwrap,128 addwrap,
129 /// Saturating addition.
130 /// Uses the `pl_node` union field. Payload is `Bin`.
131 add_sat,
132 /// Arithmetic subtraction. Asserts no integer overflow.
133 /// Uses the `pl_node` union field. Payload is `Bin`.
134 sub,
135 /// Twos complement wrapping integer subtraction.
136 /// Uses the `pl_node` union field. Payload is `Bin`.
137 subwrap,
138 /// Saturating subtraction.
139 /// Uses the `pl_node` union field. Payload is `Bin`.
140 sub_sat,
141 /// Arithmetic multiplication. Asserts no integer overflow.
142 /// Uses the `pl_node` union field. Payload is `Bin`.
143 mul,
144 /// Twos complement wrapping integer multiplication.
145 /// Uses the `pl_node` union field. Payload is `Bin`.
146 mulwrap,
147 /// Saturating multiplication.
148 /// Uses the `pl_node` union field. Payload is `Bin`.
149 mul_sat,
150 /// Implements the `@divExact` builtin.
151 /// Uses the `pl_node` union field with payload `Bin`.
152 div_exact,
153 /// Implements the `@divFloor` builtin.
154 /// Uses the `pl_node` union field with payload `Bin`.
155 div_floor,
156 /// Implements the `@divTrunc` builtin.
157 /// Uses the `pl_node` union field with payload `Bin`.
158 div_trunc,
159 /// Implements the `@mod` builtin.
160 /// Uses the `pl_node` union field with payload `Bin`.
161 mod,
162 /// Implements the `@rem` builtin.
163 /// Uses the `pl_node` union field with payload `Bin`.
164 rem,
165 /// Ambiguously remainder division or modulus. If the computation would possibly have
166 /// a different value depending on whether the operation is remainder division or modulus,
167 /// a compile error is emitted. Otherwise the computation is performed.
168 /// Uses the `pl_node` union field. Payload is `Bin`.
169 mod_rem,
170 /// Integer shift-left. Zeroes are shifted in from the right hand side.
171 /// Uses the `pl_node` union field. Payload is `Bin`.
172 shl,
173 /// Implements the `@shlExact` builtin.
174 /// Uses the `pl_node` union field with payload `Bin`.
175 shl_exact,
176 /// Saturating shift-left.
177 /// Uses the `pl_node` union field. Payload is `Bin`.
178 shl_sat,
179 /// Integer shift-right. Arithmetic or logical depending on the signedness of
180 /// the integer type.
181 /// Uses the `pl_node` union field. Payload is `Bin`.
182 shr,
183 /// Implements the `@shrExact` builtin.
184 /// Uses the `pl_node` union field with payload `Bin`.
185 shr_exact,
186
129 /// Declares a parameter of the current function. Used for:187 /// Declares a parameter of the current function. Used for:
130 /// * debug info188 /// * debug info
131 /// * checking shadowing against declarations in the current namespace189 /// * checking shadowing against declarations in the current namespace
...@@ -471,12 +529,6 @@ pub const Inst = struct {...@@ -471,12 +529,6 @@ pub const Inst = struct {
471 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.529 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
472 /// Uses the `str` union field.530 /// Uses the `str` union field.
473 str,531 str,
474 /// Arithmetic subtraction. Asserts no integer overflow.
475 /// Uses the `pl_node` union field. Payload is `Bin`.
476 sub,
477 /// Twos complement wrapping integer subtraction.
478 /// Uses the `pl_node` union field. Payload is `Bin`.
479 subwrap,
480 /// Arithmetic negation. Asserts no integer overflow.532 /// Arithmetic negation. Asserts no integer overflow.
481 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.533 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
482 /// Uses `un_node`.534 /// Uses `un_node`.
...@@ -802,46 +854,6 @@ pub const Inst = struct {...@@ -802,46 +854,6 @@ pub const Inst = struct {
802 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.854 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.
803 bit_reverse,855 bit_reverse,
804856
805 /// Implements the `@divExact` builtin.
806 /// Uses the `pl_node` union field with payload `Bin`.
807 div_exact,
808 /// Implements the `@divFloor` builtin.
809 /// Uses the `pl_node` union field with payload `Bin`.
810 div_floor,
811 /// Implements the `@divTrunc` builtin.
812 /// Uses the `pl_node` union field with payload `Bin`.
813 div_trunc,
814 /// Implements the `@mod` builtin.
815 /// Uses the `pl_node` union field with payload `Bin`.
816 mod,
817 /// Implements the `@rem` builtin.
818 /// Uses the `pl_node` union field with payload `Bin`.
819 rem,
820 /// Ambiguously remainder division or modulus. If the computation would possibly have
821 /// a different value depending on whether the operation is remainder division or modulus,
822 /// a compile error is emitted. Otherwise the computation is performed.
823 /// Uses the `pl_node` union field. Payload is `Bin`.
824 mod_rem,
825 /// Arithmetic multiplication. Asserts no integer overflow.
826 /// Uses the `pl_node` union field. Payload is `Bin`.
827 mul,
828 /// Twos complement wrapping integer multiplication.
829 /// Uses the `pl_node` union field. Payload is `Bin`.
830 mulwrap,
831
832 /// Integer shift-left. Zeroes are shifted in from the right hand side.
833 /// Uses the `pl_node` union field. Payload is `Bin`.
834 shl,
835 /// Implements the `@shlExact` builtin.
836 /// Uses the `pl_node` union field with payload `Bin`.
837 shl_exact,
838 /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
839 /// Uses the `pl_node` union field. Payload is `Bin`.
840 shr,
841 /// Implements the `@shrExact` builtin.
842 /// Uses the `pl_node` union field with payload `Bin`.
843 shr_exact,
844
845 /// Implements the `@bitOffsetOf` builtin.857 /// Implements the `@bitOffsetOf` builtin.
846 /// Uses the `pl_node` union field with payload `Bin`.858 /// Uses the `pl_node` union field with payload `Bin`.
847 bit_offset_of,859 bit_offset_of,
...@@ -961,6 +973,7 @@ pub const Inst = struct {...@@ -961,6 +973,7 @@ pub const Inst = struct {
961 .param_anytype_comptime,973 .param_anytype_comptime,
962 .add,974 .add,
963 .addwrap,975 .addwrap,
976 .add_sat,
964 .alloc,977 .alloc,
965 .alloc_mut,978 .alloc_mut,
966 .alloc_comptime,979 .alloc_comptime,
...@@ -1035,8 +1048,10 @@ pub const Inst = struct {...@@ -1035,8 +1048,10 @@ pub const Inst = struct {
1035 .mod_rem,1048 .mod_rem,
1036 .mul,1049 .mul,
1037 .mulwrap,1050 .mulwrap,
1051 .mul_sat,
1038 .ref,1052 .ref,
1039 .shl,1053 .shl,
1054 .shl_sat,
1040 .shr,1055 .shr,
1041 .store,1056 .store,
1042 .store_node,1057 .store_node,
...@@ -1045,6 +1060,7 @@ pub const Inst = struct {...@@ -1045,6 +1060,7 @@ pub const Inst = struct {
1045 .str,1060 .str,
1046 .sub,1061 .sub,
1047 .subwrap,1062 .subwrap,
1063 .sub_sat,
1048 .negate,1064 .negate,
1049 .negate_wrap,1065 .negate_wrap,
1050 .typeof,1066 .typeof,
...@@ -1218,6 +1234,14 @@ pub const Inst = struct {...@@ -1218,6 +1234,14 @@ pub const Inst = struct {
1218 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{1234 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
1219 .add = .pl_node,1235 .add = .pl_node,
1220 .addwrap = .pl_node,1236 .addwrap = .pl_node,
1237 .add_sat = .pl_node,
1238 .sub = .pl_node,
1239 .subwrap = .pl_node,
1240 .sub_sat = .pl_node,
1241 .mul = .pl_node,
1242 .mulwrap = .pl_node,
1243 .mul_sat = .pl_node,
1244
1221 .param = .pl_tok,1245 .param = .pl_tok,
1222 .param_comptime = .pl_tok,1246 .param_comptime = .pl_tok,
1223 .param_anytype = .str_tok,1247 .param_anytype = .str_tok,
...@@ -1297,8 +1321,6 @@ pub const Inst = struct {...@@ -1297,8 +1321,6 @@ pub const Inst = struct {
1297 .repeat_inline = .node,1321 .repeat_inline = .node,
1298 .merge_error_sets = .pl_node,1322 .merge_error_sets = .pl_node,
1299 .mod_rem = .pl_node,1323 .mod_rem = .pl_node,
1300 .mul = .pl_node,
1301 .mulwrap = .pl_node,
1302 .ref = .un_tok,1324 .ref = .un_tok,
1303 .ret_node = .un_node,1325 .ret_node = .un_node,
1304 .ret_load = .un_node,1326 .ret_load = .un_node,
...@@ -1315,8 +1337,6 @@ pub const Inst = struct {...@@ -1315,8 +1337,6 @@ pub const Inst = struct {
1315 .store_to_block_ptr = .bin,1337 .store_to_block_ptr = .bin,
1316 .store_to_inferred_ptr = .bin,1338 .store_to_inferred_ptr = .bin,
1317 .str = .str,1339 .str = .str,
1318 .sub = .pl_node,
1319 .subwrap = .pl_node,
1320 .negate = .un_node,1340 .negate = .un_node,
1321 .negate_wrap = .un_node,1341 .negate_wrap = .un_node,
1322 .typeof = .un_node,1342 .typeof = .un_node,
...@@ -1437,6 +1457,7 @@ pub const Inst = struct {...@@ -1437,6 +1457,7 @@ pub const Inst = struct {
14371457
1438 .shl = .pl_node,1458 .shl = .pl_node,
1439 .shl_exact = .pl_node,1459 .shl_exact = .pl_node,
1460 .shl_sat = .pl_node,
1440 .shr = .pl_node,1461 .shr = .pl_node,
1441 .shr_exact = .pl_node,1462 .shr_exact = .pl_node,
14421463
...@@ -1593,22 +1614,6 @@ pub const Inst = struct {...@@ -1593,22 +1614,6 @@ pub const Inst = struct {
1593 wasm_memory_size,1614 wasm_memory_size,
1594 /// `operand` is payload index to `BinNode`.1615 /// `operand` is payload index to `BinNode`.
1595 wasm_memory_grow,1616 wasm_memory_grow,
1596 /// Implements the `@addWithSaturation` builtin.
1597 /// `operand` is payload index to `SaturatingArithmetic`.
1598 /// `small` is unused.
1599 add_with_saturation,
1600 /// Implements the `@subWithSaturation` builtin.
1601 /// `operand` is payload index to `SaturatingArithmetic`.
1602 /// `small` is unused.
1603 sub_with_saturation,
1604 /// Implements the `@mulWithSaturation` builtin.
1605 /// `operand` is payload index to `SaturatingArithmetic`.
1606 /// `small` is unused.
1607 mul_with_saturation,
1608 /// Implements the `@shlWithSaturation` builtin.
1609 /// `operand` is payload index to `SaturatingArithmetic`.
1610 /// `small` is unused.
1611 shl_with_saturation,
16121617
1613 pub const InstData = struct {1618 pub const InstData = struct {
1614 opcode: Extended,1619 opcode: Extended,
...@@ -2788,12 +2793,6 @@ pub const Inst = struct {...@@ -2788,12 +2793,6 @@ pub const Inst = struct {
2788 ptr: Ref,2793 ptr: Ref,
2789 };2794 };
27902795
2791 pub const SaturatingArithmetic = struct {
2792 node: i32,
2793 lhs: Ref,
2794 rhs: Ref,
2795 };
2796
2797 pub const Cmpxchg = struct {2796 pub const Cmpxchg = struct {
2798 ptr: Ref,2797 ptr: Ref,
2799 expected_value: Ref,2798 expected_value: Ref,
src/codegen.zig+46-10
...@@ -824,15 +824,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -824,15 +824,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
824824
825 switch (air_tags[inst]) {825 switch (air_tags[inst]) {
826 // zig fmt: off826 // zig fmt: off
827 .add, .ptr_add => try self.airAdd(inst),827 .add, .ptr_add => try self.airAdd(inst),
828 .addwrap => try self.airAddWrap(inst),828 .addwrap => try self.airAddWrap(inst),
829 .sub, .ptr_sub => try self.airSub(inst),829 .add_sat => try self.airAddSat(inst),
830 .subwrap => try self.airSubWrap(inst),830 .sub, .ptr_sub => try self.airSub(inst),
831 .mul => try self.airMul(inst),831 .subwrap => try self.airSubWrap(inst),
832 .mulwrap => try self.airMulWrap(inst),832 .sub_sat => try self.airSubSat(inst),
833 .div => try self.airDiv(inst),833 .mul => try self.airMul(inst),
834 .rem => try self.airRem(inst),834 .mulwrap => try self.airMulWrap(inst),
835 .mod => try self.airMod(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
837 .cmp_lt => try self.airCmp(inst, .lt),842 .cmp_lt => try self.airCmp(inst, .lt),
838 .cmp_lte => try self.airCmp(inst, .lte),843 .cmp_lte => try self.airCmp(inst, .lte),
...@@ -847,7 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -847,7 +852,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
847 .bit_or => try self.airBitOr(inst),852 .bit_or => try self.airBitOr(inst),
848 .xor => try self.airXor(inst),853 .xor => try self.airXor(inst),
849 .shr => try self.airShr(inst),854 .shr => try self.airShr(inst),
850 .shl => try self.airShl(inst),
851855
852 .alloc => try self.airAlloc(inst),856 .alloc => try self.airAlloc(inst),
853 .arg => try self.airArg(inst),857 .arg => try self.airArg(inst),
...@@ -1302,6 +1306,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1302,6 +1306,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1302 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1306 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1303 }1307 }
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
1305 fn airSub(self: *Self, inst: Air.Inst.Index) !void {1317 fn airSub(self: *Self, inst: Air.Inst.Index) !void {
1306 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1318 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1307 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1319 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
...@@ -1320,6 +1332,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1320,6 +1332,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1320 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1332 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1321 }1333 }
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
1323 fn airMul(self: *Self, inst: Air.Inst.Index) !void {1343 fn airMul(self: *Self, inst: Air.Inst.Index) !void {
1324 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1344 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1325 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1345 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 {...@@ -1338,6 +1358,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1338 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1358 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1339 }1359 }
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
1341 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {1369 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
1342 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1370 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1343 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1371 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
...@@ -1400,6 +1428,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1400,6 +1428,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1400 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1428 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1401 }1429 }
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
1403 fn airShr(self: *Self, inst: Air.Inst.Index) !void {1439 fn airShr(self: *Self, inst: Air.Inst.Index) !void {
1404 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1440 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1405 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {1441 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
src/codegen/c.zig+129-18
...@@ -883,22 +883,27 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -883,22 +883,27 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
883883
884 // TODO use a different strategy for add that communicates to the optimizer884 // TODO use a different strategy for add that communicates to the optimizer
885 // that wrapping is UB.885 // that wrapping is UB.
886 .add, .ptr_add => try airBinOp( f, inst, " + "),886 .add, .ptr_add => try airBinOp (f, inst, " + "),
887 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
888 // TODO use a different strategy for sub that communicates to the optimizer887 // TODO use a different strategy for sub that communicates to the optimizer
889 // that wrapping is UB.888 // that wrapping is UB.
890 .sub, .ptr_sub => try airBinOp( f, inst, " - "),889 .sub, .ptr_sub => try airBinOp (f, inst, " - "),
891 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
892 // TODO use a different strategy for mul that communicates to the optimizer890 // TODO use a different strategy for mul that communicates to the optimizer
893 // that wrapping is UB.891 // that wrapping is UB.
894 .mul => try airBinOp( f, inst, " * "),892 .mul => try airBinOp (f, inst, " * "),
895 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
896 // TODO use a different strategy for div that communicates to the optimizer893 // TODO use a different strategy for div that communicates to the optimizer
897 // that wrapping is UB.894 // that wrapping is UB.
898 .div => try airBinOp( f, inst, " / "),895 .div => try airBinOp( f, inst, " / "),
899 .rem => try airBinOp( f, inst, " % "),896 .rem => try airBinOp( f, inst, " % "),
900 // TODO implement modulus division897 .mod => try airBinOp( f, inst, " mod "), // TODO implement modulus division
901 .mod => try airBinOp( f, inst, " mod "),898
899 .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
900 .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
901 .mulwrap => try airWrapOp(f, inst, " * ", "mulw_"),
902
903 .add_sat => try airSatOp(f, inst, "adds_"),
904 .sub_sat => try airSatOp(f, inst, "subs_"),
905 .mul_sat => try airSatOp(f, inst, "muls_"),
906 .shl_sat => try airSatOp(f, inst, "shls_"),
902907
903 .cmp_eq => try airBinOp(f, inst, " == "),908 .cmp_eq => try airBinOp(f, inst, " == "),
904 .cmp_gt => try airBinOp(f, inst, " > "),909 .cmp_gt => try airBinOp(f, inst, " > "),
...@@ -908,16 +913,14 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -908,16 +913,14 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
908 .cmp_neq => try airBinOp(f, inst, " != "),913 .cmp_neq => try airBinOp(f, inst, " != "),
909914
910 // bool_and and bool_or are non-short-circuit operations915 // bool_and and bool_or are non-short-circuit operations
911 .bool_and => try airBinOp(f, inst, " & "),916 .bool_and => try airBinOp(f, inst, " & "),
912 .bool_or => try airBinOp(f, inst, " | "),917 .bool_or => try airBinOp(f, inst, " | "),
913 .bit_and => try airBinOp(f, inst, " & "),918 .bit_and => try airBinOp(f, inst, " & "),
914 .bit_or => try airBinOp(f, inst, " | "),919 .bit_or => try airBinOp(f, inst, " | "),
915 .xor => try airBinOp(f, inst, " ^ "),920 .xor => try airBinOp(f, inst, " ^ "),
916921 .shr => try airBinOp(f, inst, " >> "),
917 .shr => try airBinOp(f, inst, " >> "),922 .shl, .shl_exact => try airBinOp(f, inst, " << "),
918 .shl => try airBinOp(f, inst, " << "),923 .not => try airNot (f, inst),
919
920 .not => try airNot( f, inst),
921924
922 .optional_payload => try airOptionalPayload(f, inst),925 .optional_payload => try airOptionalPayload(f, inst),
923 .optional_payload_ptr => try airOptionalPayload(f, inst),926 .optional_payload_ptr => try airOptionalPayload(f, inst),
...@@ -1309,6 +1312,114 @@ fn airWrapOp(...@@ -1309,6 +1312,114 @@ fn airWrapOp(
1309 return ret;1312 return ret;
1310}1313}
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
1312fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {1423fn airNot(f: *Function, inst: Air.Inst.Index) !CValue {
1313 if (f.liveness.isUnused(inst))1424 if (f.liveness.isUnused(inst))
1314 return CValue.none;1425 return CValue.none;
src/codegen/llvm.zig+130-30
...@@ -1236,23 +1236,27 @@ pub const FuncGen = struct {...@@ -1236,23 +1236,27 @@ pub const FuncGen = struct {
1236 for (body) |inst| {1236 for (body) |inst| {
1237 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {1237 const opt_value: ?*const llvm.Value = switch (air_tags[inst]) {
1238 // zig fmt: off1238 // zig fmt: off
1239 .add => try self.airAdd(inst, false),1239 .add => try self.airAdd(inst),
1240 .addwrap => try self.airAdd(inst, true),1240 .addwrap => try self.airAddWrap(inst),
1241 .sub => try self.airSub(inst, false),1241 .add_sat => try self.airAddSat(inst),
1242 .subwrap => try self.airSub(inst, true),1242 .sub => try self.airSub(inst),
1243 .mul => try self.airMul(inst, false),1243 .subwrap => try self.airSubWrap(inst),
1244 .mulwrap => try self.airMul(inst, true),1244 .sub_sat => try self.airSubSat(inst),
1245 .div => try self.airDiv(inst),1245 .mul => try self.airMul(inst),
1246 .rem => try self.airRem(inst),1246 .mulwrap => try self.airMulWrap(inst),
1247 .mod => try self.airMod(inst),1247 .mul_sat => try self.airMulSat(inst),
1248 .ptr_add => try self.airPtrAdd(inst),1248 .div => try self.airDiv(inst),
1249 .ptr_sub => try self.airPtrSub(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
1251 .bit_and, .bool_and => try self.airAnd(inst),1257 .bit_and, .bool_and => try self.airAnd(inst),
1252 .bit_or, .bool_or => try self.airOr(inst),1258 .bit_or, .bool_or => try self.airOr(inst),
1253 .xor => try self.airXor(inst),1259 .xor => try self.airXor(inst),
1254
1255 .shl => try self.airShl(inst),
1256 .shr => try self.airShr(inst),1260 .shr => try self.airShr(inst),
12571261
1258 .cmp_eq => try self.airCmp(inst, .eq),1262 .cmp_eq => try self.airCmp(inst, .eq),
...@@ -2024,51 +2028,115 @@ pub const FuncGen = struct {...@@ -2024,51 +2028,115 @@ pub const FuncGen = struct {
2024 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});2028 return self.todo("implement llvm codegen for 'airWrapErrUnionErr'", .{});
2025 }2029 }
20262030
2027 fn airAdd(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {2031 fn airAdd(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2028 if (self.liveness.isUnused(inst))2032 if (self.liveness.isUnused(inst)) return null;
2029 return null;
20302033
2031 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2034 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2032 const lhs = try self.resolveInst(bin_op.lhs);2035 const lhs = try self.resolveInst(bin_op.lhs);
2033 const rhs = try self.resolveInst(bin_op.rhs);2036 const rhs = try self.resolveInst(bin_op.rhs);
2034 const inst_ty = self.air.typeOfIndex(inst);2037 const inst_ty = self.air.typeOfIndex(inst);
20352038
2036 if (inst_ty.isRuntimeFloat()) return self.builder.buildFAdd(lhs, rhs, "");2039 if (inst_ty.isAnyFloat()) return self.builder.buildFAdd(lhs, rhs, "");
2037 if (wrap) return self.builder.buildAdd(lhs, rhs, "");
2038 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");2040 if (inst_ty.isSignedInt()) return self.builder.buildNSWAdd(lhs, rhs, "");
2039 return self.builder.buildNUWAdd(lhs, rhs, "");2041 return self.builder.buildNUWAdd(lhs, rhs, "");
2040 }2042 }
20412043
2042 fn airSub(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {2044 fn airAddWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2043 if (self.liveness.isUnused(inst))2045 if (self.liveness.isUnused(inst)) return null;
2044 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
2046 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2071 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2047 const lhs = try self.resolveInst(bin_op.lhs);2072 const lhs = try self.resolveInst(bin_op.lhs);
2048 const rhs = try self.resolveInst(bin_op.rhs);2073 const rhs = try self.resolveInst(bin_op.rhs);
2049 const inst_ty = self.air.typeOfIndex(inst);2074 const inst_ty = self.air.typeOfIndex(inst);
20502075
2051 if (inst_ty.isRuntimeFloat()) return self.builder.buildFSub(lhs, rhs, "");2076 if (inst_ty.isAnyFloat()) return self.builder.buildFSub(lhs, rhs, "");
2052 if (wrap) return self.builder.buildSub(lhs, rhs, "");
2053 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");2077 if (inst_ty.isSignedInt()) return self.builder.buildNSWSub(lhs, rhs, "");
2054 return self.builder.buildNUWSub(lhs, rhs, "");2078 return self.builder.buildNUWSub(lhs, rhs, "");
2055 }2079 }
20562080
2057 fn airMul(self: *FuncGen, inst: Air.Inst.Index, wrap: bool) !?*const llvm.Value {2081 fn airSubWrap(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2058 if (self.liveness.isUnused(inst))2082 if (self.liveness.isUnused(inst)) return null;
2059 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
2061 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2094 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2062 const lhs = try self.resolveInst(bin_op.lhs);2095 const lhs = try self.resolveInst(bin_op.lhs);
2063 const rhs = try self.resolveInst(bin_op.rhs);2096 const rhs = try self.resolveInst(bin_op.rhs);
2064 const inst_ty = self.air.typeOfIndex(inst);2097 const inst_ty = self.air.typeOfIndex(inst);
20652098
2066 if (inst_ty.isRuntimeFloat()) return self.builder.buildFMul(lhs, rhs, "");2099 if (inst_ty.isAnyFloat()) return self.todo("saturating float sub", .{});
2067 if (wrap) return self.builder.buildMul(lhs, rhs, "");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, "");
2068 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");2113 if (inst_ty.isSignedInt()) return self.builder.buildNSWMul(lhs, rhs, "");
2069 return self.builder.buildNUWMul(lhs, rhs, "");2114 return self.builder.buildNUWMul(lhs, rhs, "");
2070 }2115 }
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
2072 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2140 fn airDiv(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2073 if (self.liveness.isUnused(inst))2141 if (self.liveness.isUnused(inst))
2074 return null;2142 return null;
...@@ -2174,9 +2242,25 @@ pub const FuncGen = struct {...@@ -2174,9 +2242,25 @@ pub const FuncGen = struct {
2174 return self.builder.buildXor(lhs, rhs, "");2242 return self.builder.buildXor(lhs, rhs, "");
2175 }2243 }
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
2177 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2261 fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2178 if (self.liveness.isUnused(inst))2262 if (self.liveness.isUnused(inst)) return null;
2179 return null;2263
2180 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2264 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2181 const lhs = try self.resolveInst(bin_op.lhs);2265 const lhs = try self.resolveInst(bin_op.lhs);
2182 const rhs = try self.resolveInst(bin_op.rhs);2266 const rhs = try self.resolveInst(bin_op.rhs);
...@@ -2189,6 +2273,22 @@ pub const FuncGen = struct {...@@ -2189,6 +2273,22 @@ pub const FuncGen = struct {
2189 return self.builder.buildShl(lhs, casted_rhs, "");2273 return self.builder.buildShl(lhs, casted_rhs, "");
2190 }2274 }
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
2192 fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {2292 fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2193 if (self.liveness.isUnused(inst))2293 if (self.liveness.isUnused(inst))
2194 return null;2294 return null;
src/codegen/llvm/bindings.zig+30
...@@ -397,6 +397,12 @@ pub const Builder = opaque {...@@ -397,6 +397,12 @@ pub const Builder = opaque {
397 pub const buildNUWAdd = LLVMBuildNUWAdd;397 pub const buildNUWAdd = LLVMBuildNUWAdd;
398 extern fn LLVMBuildNUWAdd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;398 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
400 pub const buildFSub = LLVMBuildFSub;406 pub const buildFSub = LLVMBuildFSub;
401 extern fn LLVMBuildFSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;407 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 {...@@ -409,6 +415,12 @@ pub const Builder = opaque {
409 pub const buildNUWSub = LLVMBuildNUWSub;415 pub const buildNUWSub = LLVMBuildNUWSub;
410 extern fn LLVMBuildNUWSub(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;416 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
412 pub const buildFMul = LLVMBuildFMul;424 pub const buildFMul = LLVMBuildFMul;
413 extern fn LLVMBuildFMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;425 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 {...@@ -421,6 +433,12 @@ pub const Builder = opaque {
421 pub const buildNUWMul = LLVMBuildNUWMul;433 pub const buildNUWMul = LLVMBuildNUWMul;
422 extern fn LLVMBuildNUWMul(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;434 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
424 pub const buildUDiv = LLVMBuildUDiv;442 pub const buildUDiv = LLVMBuildUDiv;
425 extern fn LLVMBuildUDiv(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;443 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 {...@@ -451,6 +469,18 @@ pub const Builder = opaque {
451 pub const buildShl = LLVMBuildShl;469 pub const buildShl = LLVMBuildShl;
452 extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;470 extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
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
454 pub const buildOr = LLVMBuildOr;484 pub const buildOr = LLVMBuildOr;
455 extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;485 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...@@ -356,3 +356,97 @@ static inline long long zig_subw_longlong(long long lhs, long long rhs, long lon
356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));356 return (long long)(((unsigned long long)lhs) - ((unsigned long long)rhs));
357}357}
358358
359#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 {...@@ -104,10 +104,13 @@ const Writer = struct {
104104
105 .add,105 .add,
106 .addwrap,106 .addwrap,
107 .add_sat,
107 .sub,108 .sub,
108 .subwrap,109 .subwrap,
110 .sub_sat,
109 .mul,111 .mul,
110 .mulwrap,112 .mulwrap,
113 .mul_sat,
111 .div,114 .div,
112 .rem,115 .rem,
113 .mod,116 .mod,
...@@ -130,6 +133,8 @@ const Writer = struct {...@@ -130,6 +133,8 @@ const Writer = struct {
130 .ptr_elem_val,133 .ptr_elem_val,
131 .ptr_ptr_elem_val,134 .ptr_ptr_elem_val,
132 .shl,135 .shl,
136 .shl_exact,
137 .shl_sat,
133 .shr,138 .shr,
134 .set_union_tag,139 .set_union_tag,
135 => try w.writeBinOp(s, inst),140 => try w.writeBinOp(s, inst),
src/print_zir.zig+4-18
...@@ -229,12 +229,15 @@ const Writer = struct {...@@ -229,12 +229,15 @@ const Writer = struct {
229229
230 .add,230 .add,
231 .addwrap,231 .addwrap,
232 .add_sat,
232 .array_cat,233 .array_cat,
233 .array_mul,234 .array_mul,
234 .mul,235 .mul,
235 .mulwrap,236 .mulwrap,
237 .mul_sat,
236 .sub,238 .sub,
237 .subwrap,239 .subwrap,
240 .sub_sat,
238 .cmp_lt,241 .cmp_lt,
239 .cmp_lte,242 .cmp_lte,
240 .cmp_eq,243 .cmp_eq,
...@@ -247,6 +250,7 @@ const Writer = struct {...@@ -247,6 +250,7 @@ const Writer = struct {
247 .mod_rem,250 .mod_rem,
248 .shl,251 .shl,
249 .shl_exact,252 .shl_exact,
253 .shl_sat,
250 .shr,254 .shr,
251 .shr_exact,255 .shr_exact,
252 .xor,256 .xor,
...@@ -400,12 +404,6 @@ const Writer = struct {...@@ -400,12 +404,6 @@ const Writer = struct {
400 .shl_with_overflow,404 .shl_with_overflow,
401 => try self.writeOverflowArithmetic(stream, extended),405 => try self.writeOverflowArithmetic(stream, extended),
402406
403 .add_with_saturation,
404 .sub_with_saturation,
405 .mul_with_saturation,
406 .shl_with_saturation,
407 => try self.writeSaturatingArithmetic(stream, extended),
408
409 .struct_decl => try self.writeStructDecl(stream, extended),407 .struct_decl => try self.writeStructDecl(stream, extended),
410 .union_decl => try self.writeUnionDecl(stream, extended),408 .union_decl => try self.writeUnionDecl(stream, extended),
411 .enum_decl => try self.writeEnumDecl(stream, extended),409 .enum_decl => try self.writeEnumDecl(stream, extended),
...@@ -854,18 +852,6 @@ const Writer = struct {...@@ -854,18 +852,6 @@ const Writer = struct {
854 try self.writeSrc(stream, src);852 try self.writeSrc(stream, src);
855 }853 }
856854
857 fn writeSaturatingArithmetic(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
858 const extra = self.code.extraData(Zir.Inst.SaturatingArithmetic, extended.operand).data;
859 const src: LazySrcLoc = .{ .node_offset = extra.node };
860
861 try self.writeInstRef(stream, extra.lhs);
862 try stream.writeAll(", ");
863 try self.writeInstRef(stream, extra.rhs);
864 try stream.writeAll(", ");
865 try stream.writeAll(") ");
866 try self.writeSrc(stream, src);
867 }
868
869 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {855 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
870 const inst_data = self.code.instructions.items(.data)[inst].pl_node;856 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
871 const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index);857 const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index);
src/stage1/all_types.hpp+12-8
...@@ -812,14 +812,18 @@ enum BinOpType {...@@ -812,14 +812,18 @@ enum BinOpType {
812 BinOpTypeInvalid,812 BinOpTypeInvalid,
813 BinOpTypeAssign,813 BinOpTypeAssign,
814 BinOpTypeAssignTimes,814 BinOpTypeAssignTimes,
815 BinOpTypeAssignTimesSat,
815 BinOpTypeAssignTimesWrap,816 BinOpTypeAssignTimesWrap,
816 BinOpTypeAssignDiv,817 BinOpTypeAssignDiv,
817 BinOpTypeAssignMod,818 BinOpTypeAssignMod,
818 BinOpTypeAssignPlus,819 BinOpTypeAssignPlus,
820 BinOpTypeAssignPlusSat,
819 BinOpTypeAssignPlusWrap,821 BinOpTypeAssignPlusWrap,
820 BinOpTypeAssignMinus,822 BinOpTypeAssignMinus,
823 BinOpTypeAssignMinusSat,
821 BinOpTypeAssignMinusWrap,824 BinOpTypeAssignMinusWrap,
822 BinOpTypeAssignBitShiftLeft,825 BinOpTypeAssignBitShiftLeft,
826 BinOpTypeAssignBitShiftLeftSat,
823 BinOpTypeAssignBitShiftRight,827 BinOpTypeAssignBitShiftRight,
824 BinOpTypeAssignBitAnd,828 BinOpTypeAssignBitAnd,
825 BinOpTypeAssignBitXor,829 BinOpTypeAssignBitXor,
...@@ -836,12 +840,16 @@ enum BinOpType {...@@ -836,12 +840,16 @@ enum BinOpType {
836 BinOpTypeBinXor,840 BinOpTypeBinXor,
837 BinOpTypeBinAnd,841 BinOpTypeBinAnd,
838 BinOpTypeBitShiftLeft,842 BinOpTypeBitShiftLeft,
843 BinOpTypeBitShiftLeftSat,
839 BinOpTypeBitShiftRight,844 BinOpTypeBitShiftRight,
840 BinOpTypeAdd,845 BinOpTypeAdd,
846 BinOpTypeAddSat,
841 BinOpTypeAddWrap,847 BinOpTypeAddWrap,
842 BinOpTypeSub,848 BinOpTypeSub,
849 BinOpTypeSubSat,
843 BinOpTypeSubWrap,850 BinOpTypeSubWrap,
844 BinOpTypeMult,851 BinOpTypeMult,
852 BinOpTypeMultSat,
845 BinOpTypeMultWrap,853 BinOpTypeMultWrap,
846 BinOpTypeDiv,854 BinOpTypeDiv,
847 BinOpTypeMod,855 BinOpTypeMod,
...@@ -1810,10 +1818,6 @@ enum BuiltinFnId {...@@ -1810,10 +1818,6 @@ enum BuiltinFnId {
1810 BuiltinFnIdReduce,1818 BuiltinFnIdReduce,
1811 BuiltinFnIdMaximum,1819 BuiltinFnIdMaximum,
1812 BuiltinFnIdMinimum,1820 BuiltinFnIdMinimum,
1813 BuiltinFnIdSatAdd,
1814 BuiltinFnIdSatSub,
1815 BuiltinFnIdSatMul,
1816 BuiltinFnIdSatShl,
1817};1821};
18181822
1819struct BuiltinFnEntry {1823struct BuiltinFnEntry {
...@@ -2958,10 +2962,10 @@ enum IrBinOp {...@@ -2958,10 +2962,10 @@ enum IrBinOp {
2958 IrBinOpArrayMult,2962 IrBinOpArrayMult,
2959 IrBinOpMaximum,2963 IrBinOpMaximum,
2960 IrBinOpMinimum,2964 IrBinOpMinimum,
2961 IrBinOpSatAdd,2965 IrBinOpAddSat,
2962 IrBinOpSatSub,2966 IrBinOpSubSat,
2963 IrBinOpSatMul,2967 IrBinOpMultSat,
2964 IrBinOpSatShl,2968 IrBinOpShlSat,
2965};2969};
29662970
2967struct Stage1ZirInstBinOp {2971struct Stage1ZirInstBinOp {
src/stage1/astgen.cpp+16-60
...@@ -3672,6 +3672,8 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod...@@ -3672,6 +3672,8 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
3672 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc);3672 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMult), lval, result_loc);
3673 case BinOpTypeAssignTimesWrap:3673 case BinOpTypeAssignTimesWrap:
3674 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpMultWrap), lval, result_loc);3674 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);
3675 case BinOpTypeAssignDiv:3677 case BinOpTypeAssignDiv:
3676 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);3678 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
3677 case BinOpTypeAssignMod:3679 case BinOpTypeAssignMod:
...@@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod...@@ -3680,12 +3682,18 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
3680 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc);3682 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAdd), lval, result_loc);
3681 case BinOpTypeAssignPlusWrap:3683 case BinOpTypeAssignPlusWrap:
3682 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpAddWrap), lval, result_loc);3684 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);
3683 case BinOpTypeAssignMinus:3687 case BinOpTypeAssignMinus:
3684 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc);3688 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSub), lval, result_loc);
3685 case BinOpTypeAssignMinusWrap:3689 case BinOpTypeAssignMinusWrap:
3686 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpSubWrap), lval, result_loc);3690 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);
3687 case BinOpTypeAssignBitShiftLeft:3693 case BinOpTypeAssignBitShiftLeft:
3688 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);3694 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);
3689 case BinOpTypeAssignBitShiftRight:3697 case BinOpTypeAssignBitShiftRight:
3690 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);3698 return ir_lval_wrap(ag, scope, astgen_assign_op(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
3691 case BinOpTypeAssignBitAnd:3699 case BinOpTypeAssignBitAnd:
...@@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod...@@ -3718,20 +3726,28 @@ static Stage1ZirInst *astgen_bin_op(Stage1AstGen *ag, Scope *scope, AstNode *nod
3718 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc);3726 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBinAnd), lval, result_loc);
3719 case BinOpTypeBitShiftLeft:3727 case BinOpTypeBitShiftLeft:
3720 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc);3728 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);
3721 case BinOpTypeBitShiftRight:3731 case BinOpTypeBitShiftRight:
3722 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);3732 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc);
3723 case BinOpTypeAdd:3733 case BinOpTypeAdd:
3724 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc);3734 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAdd), lval, result_loc);
3725 case BinOpTypeAddWrap:3735 case BinOpTypeAddWrap:
3726 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpAddWrap), lval, result_loc);3736 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);
3727 case BinOpTypeSub:3739 case BinOpTypeSub:
3728 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc);3740 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSub), lval, result_loc);
3729 case BinOpTypeSubWrap:3741 case BinOpTypeSubWrap:
3730 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpSubWrap), lval, result_loc);3742 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);
3731 case BinOpTypeMult:3745 case BinOpTypeMult:
3732 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc);3746 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMult), lval, result_loc);
3733 case BinOpTypeMultWrap:3747 case BinOpTypeMultWrap:
3734 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpMultWrap), lval, result_loc);3748 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);
3735 case BinOpTypeDiv:3751 case BinOpTypeDiv:
3736 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);3752 return ir_lval_wrap(ag, scope, astgen_bin_op_id(ag, scope, node, IrBinOpDivUnspecified), lval, result_loc);
3737 case BinOpTypeMod:3753 case BinOpTypeMod:
...@@ -4704,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast...@@ -4704,66 +4720,6 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
4704 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);4720 Stage1ZirInst *bin_op = ir_build_bin_op(ag, scope, node, IrBinOpMaximum, arg0_value, arg1_value, true);
4705 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);4721 return ir_lval_wrap(ag, scope, bin_op, lval, result_loc);
4706 }4722 }
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 }
4767 case BuiltinFnIdMemcpy:4723 case BuiltinFnIdMemcpy:
4768 {4724 {
4769 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4725 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
src/stage1/codegen.cpp+4-8
...@@ -3333,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3333,7 +3333,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3333 } else {3333 } else {
3334 zig_unreachable();3334 zig_unreachable();
3335 }3335 }
3336 case IrBinOpSatAdd:3336 case IrBinOpAddSat:
3337 if (scalar_type->id == ZigTypeIdInt) {3337 if (scalar_type->id == ZigTypeIdInt) {
3338 if (scalar_type->data.integral.is_signed) {3338 if (scalar_type->data.integral.is_signed) {
3339 return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, "");3339 return ZigLLVMBuildSAddSat(g->builder, op1_value, op2_value, "");
...@@ -3343,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3343,7 +3343,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3343 } else {3343 } else {
3344 zig_unreachable();3344 zig_unreachable();
3345 }3345 }
3346 case IrBinOpSatSub:3346 case IrBinOpSubSat:
3347 if (scalar_type->id == ZigTypeIdInt) {3347 if (scalar_type->id == ZigTypeIdInt) {
3348 if (scalar_type->data.integral.is_signed) {3348 if (scalar_type->data.integral.is_signed) {
3349 return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, "");3349 return ZigLLVMBuildSSubSat(g->builder, op1_value, op2_value, "");
...@@ -3353,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3353,7 +3353,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3353 } else {3353 } else {
3354 zig_unreachable();3354 zig_unreachable();
3355 }3355 }
3356 case IrBinOpSatMul:3356 case IrBinOpMultSat:
3357 if (scalar_type->id == ZigTypeIdInt) {3357 if (scalar_type->id == ZigTypeIdInt) {
3358 if (scalar_type->data.integral.is_signed) {3358 if (scalar_type->data.integral.is_signed) {
3359 return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, "");3359 return ZigLLVMBuildSMulFixSat(g->builder, op1_value, op2_value, "");
...@@ -3363,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,...@@ -3363,7 +3363,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, Stage1Air *executable,
3363 } else {3363 } else {
3364 zig_unreachable();3364 zig_unreachable();
3365 }3365 }
3366 case IrBinOpSatShl:3366 case IrBinOpShlSat:
3367 if (scalar_type->id == ZigTypeIdInt) {3367 if (scalar_type->id == ZigTypeIdInt) {
3368 if (scalar_type->data.integral.is_signed) {3368 if (scalar_type->data.integral.is_signed) {
3369 return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "");3369 return ZigLLVMBuildSShlSat(g->builder, op1_value, op2_value, "");
...@@ -9134,10 +9134,6 @@ static void define_builtin_fns(CodeGen *g) {...@@ -9134,10 +9134,6 @@ static void define_builtin_fns(CodeGen *g) {
9134 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);9134 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
9135 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);9135 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
9136 create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);9136 create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);
9137 create_builtin_fn(g, BuiltinFnIdSatAdd, "addWithSaturation", 2);
9138 create_builtin_fn(g, BuiltinFnIdSatSub, "subWithSaturation", 2);
9139 create_builtin_fn(g, BuiltinFnIdSatMul, "mulWithSaturation", 2);
9140 create_builtin_fn(g, BuiltinFnIdSatShl, "shlWithSaturation", 2);
9141}9137}
91429138
9143static const char *bool_to_str(bool b) {9139static const char *bool_to_str(bool b) {
src/stage1/ir.cpp+12-12
...@@ -9820,28 +9820,28 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s...@@ -9820,28 +9820,28 @@ static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, Scope *scope, AstNode *s
9820 float_min(out_val, op1_val, op2_val);9820 float_min(out_val, op1_val, op2_val);
9821 }9821 }
9822 break;9822 break;
9823 case IrBinOpSatAdd:9823 case IrBinOpAddSat:
9824 if (is_int) {9824 if (is_int) {
9825 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);9825 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);
9826 } else {9826 } else {
9827 zig_unreachable();9827 zig_unreachable();
9828 }9828 }
9829 break;9829 break;
9830 case IrBinOpSatSub:9830 case IrBinOpSubSat:
9831 if (is_int) {9831 if (is_int) {
9832 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);9832 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);
9833 } else {9833 } else {
9834 zig_unreachable();9834 zig_unreachable();
9835 }9835 }
9836 break;9836 break;
9837 case IrBinOpSatMul:9837 case IrBinOpMultSat:
9838 if (is_int) {9838 if (is_int) {
9839 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);9839 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);
9840 } else {9840 } else {
9841 zig_unreachable();9841 zig_unreachable();
9842 }9842 }
9843 break;9843 break;
9844 case IrBinOpSatShl:9844 case IrBinOpShlSat:
9845 if (is_int) {9845 if (is_int) {
9846 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);9846 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);
9847 } else {9847 } else {
...@@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) {...@@ -10069,10 +10069,10 @@ static bool ok_float_op(IrBinOp op) {
10069 case IrBinOpBitShiftRightExact:10069 case IrBinOpBitShiftRightExact:
10070 case IrBinOpAddWrap:10070 case IrBinOpAddWrap:
10071 case IrBinOpSubWrap:10071 case IrBinOpSubWrap:
10072 case IrBinOpSatAdd:10072 case IrBinOpAddSat:
10073 case IrBinOpSatSub:10073 case IrBinOpSubSat:
10074 case IrBinOpSatMul:10074 case IrBinOpMultSat:
10075 case IrBinOpSatShl:10075 case IrBinOpShlSat:
10076 case IrBinOpMultWrap:10076 case IrBinOpMultWrap:
10077 case IrBinOpArrayCat:10077 case IrBinOpArrayCat:
10078 case IrBinOpArrayMult:10078 case IrBinOpArrayMult:
...@@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns...@@ -11046,10 +11046,10 @@ static Stage1AirInst *ir_analyze_instruction_bin_op(IrAnalyze *ira, Stage1ZirIns
11046 case IrBinOpRemMod:11046 case IrBinOpRemMod:
11047 case IrBinOpMaximum:11047 case IrBinOpMaximum:
11048 case IrBinOpMinimum:11048 case IrBinOpMinimum:
11049 case IrBinOpSatAdd:11049 case IrBinOpAddSat:
11050 case IrBinOpSatSub:11050 case IrBinOpSubSat:
11051 case IrBinOpSatMul:11051 case IrBinOpMultSat:
11052 case IrBinOpSatShl:11052 case IrBinOpShlSat:
11053 return ir_analyze_bin_op_math(ira, bin_op_instruction);11053 return ir_analyze_bin_op_math(ira, bin_op_instruction);
11054 case IrBinOpArrayCat:11054 case IrBinOpArrayCat:
11055 return ir_analyze_array_cat(ira, bin_op_instruction);11055 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) {...@@ -737,13 +737,13 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
737 return "@maximum";737 return "@maximum";
738 case IrBinOpMinimum:738 case IrBinOpMinimum:
739 return "@minimum";739 return "@minimum";
740 case IrBinOpSatAdd:740 case IrBinOpAddSat:
741 return "@addWithSaturation";741 return "@addWithSaturation";
742 case IrBinOpSatSub:742 case IrBinOpSubSat:
743 return "@subWithSaturation";743 return "@subWithSaturation";
744 case IrBinOpSatMul:744 case IrBinOpMultSat:
745 return "@mulWithSaturation";745 return "@mulWithSaturation";
746 case IrBinOpSatShl:746 case IrBinOpShlSat:
747 return "@shlWithSaturation";747 return "@shlWithSaturation";
748 }748 }
749 zig_unreachable();749 zig_unreachable();
src/stage1/parser.cpp+16
...@@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {...@@ -2381,6 +2381,7 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
2381// / PLUSEQUAL2381// / PLUSEQUAL
2382// / MINUSEQUAL2382// / MINUSEQUAL
2383// / LARROW2EQUAL2383// / LARROW2EQUAL
2384// / LARROW2PIPEEQUAL
2384// / RARROW2EQUAL2385// / RARROW2EQUAL
2385// / AMPERSANDEQUAL2386// / AMPERSANDEQUAL
2386// / CARETEQUAL2387// / CARETEQUAL
...@@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {...@@ -2388,6 +2389,9 @@ static AstNode *ast_parse_switch_item(ParseContext *pc) {
2388// / ASTERISKPERCENTEQUAL2389// / ASTERISKPERCENTEQUAL
2389// / PLUSPERCENTEQUAL2390// / PLUSPERCENTEQUAL
2390// / MINUSPERCENTEQUAL2391// / MINUSPERCENTEQUAL
2392// / ASTERISKPIPEEQUAL
2393// / PLUSPIPEEQUAL
2394// / MINUSPIPEEQUAL
2391// / EQUAL2395// / EQUAL
2392static AstNode *ast_parse_assign_op(ParseContext *pc) {2396static AstNode *ast_parse_assign_op(ParseContext *pc) {
2393 // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't2397 // 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) {...@@ -2396,17 +2400,21 @@ static AstNode *ast_parse_assign_op(ParseContext *pc) {
2396 table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;2400 table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd;
2397 table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;2401 table[TokenIdBitOrEq] = BinOpTypeAssignBitOr;
2398 table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;2402 table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft;
2403 table[TokenIdBitShiftLeftPipeEq] = BinOpTypeAssignBitShiftLeftSat;
2399 table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;2404 table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight;
2400 table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;2405 table[TokenIdBitXorEq] = BinOpTypeAssignBitXor;
2401 table[TokenIdDivEq] = BinOpTypeAssignDiv;2406 table[TokenIdDivEq] = BinOpTypeAssignDiv;
2402 table[TokenIdEq] = BinOpTypeAssign;2407 table[TokenIdEq] = BinOpTypeAssign;
2403 table[TokenIdMinusEq] = BinOpTypeAssignMinus;2408 table[TokenIdMinusEq] = BinOpTypeAssignMinus;
2404 table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;2409 table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap;
2410 table[TokenIdMinusPipeEq] = BinOpTypeAssignMinusSat;
2405 table[TokenIdModEq] = BinOpTypeAssignMod;2411 table[TokenIdModEq] = BinOpTypeAssignMod;
2406 table[TokenIdPlusEq] = BinOpTypeAssignPlus;2412 table[TokenIdPlusEq] = BinOpTypeAssignPlus;
2407 table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;2413 table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap;
2414 table[TokenIdPlusPipeEq] = BinOpTypeAssignPlusSat;
2408 table[TokenIdTimesEq] = BinOpTypeAssignTimes;2415 table[TokenIdTimesEq] = BinOpTypeAssignTimes;
2409 table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;2416 table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap;
2417 table[TokenIdTimesPipeEq] = BinOpTypeAssignTimesSat;
24102418
2411 BinOpType op = table[pc->token_ids[pc->current_token]];2419 BinOpType op = table[pc->token_ids[pc->current_token]];
2412 if (op != BinOpTypeInvalid) {2420 if (op != BinOpTypeInvalid) {
...@@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {...@@ -2483,10 +2491,12 @@ static AstNode *ast_parse_bitwise_op(ParseContext *pc) {
24832491
2484// BitShiftOp2492// BitShiftOp
2485// <- LARROW22493// <- LARROW2
2494// / LARROW2PIPE
2486// / RARROW22495// / RARROW2
2487static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {2496static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
2488 BinOpType table[TokenIdCount] = {};2497 BinOpType table[TokenIdCount] = {};
2489 table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;2498 table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft;
2499 table[TokenIdBitShiftLeftPipe] = BinOpTypeBitShiftLeftSat;
2490 table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;2500 table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight;
24912501
2492 BinOpType op = table[pc->token_ids[pc->current_token]];2502 BinOpType op = table[pc->token_ids[pc->current_token]];
...@@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {...@@ -2506,6 +2516,8 @@ static AstNode *ast_parse_bit_shift_op(ParseContext *pc) {
2506// / PLUS22516// / PLUS2
2507// / PLUSPERCENT2517// / PLUSPERCENT
2508// / MINUSPERCENT2518// / MINUSPERCENT
2519// / PLUSPIPE
2520// / MINUSPIPE
2509static AstNode *ast_parse_addition_op(ParseContext *pc) {2521static AstNode *ast_parse_addition_op(ParseContext *pc) {
2510 BinOpType table[TokenIdCount] = {};2522 BinOpType table[TokenIdCount] = {};
2511 table[TokenIdPlus] = BinOpTypeAdd;2523 table[TokenIdPlus] = BinOpTypeAdd;
...@@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {...@@ -2513,6 +2525,8 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
2513 table[TokenIdPlusPlus] = BinOpTypeArrayCat;2525 table[TokenIdPlusPlus] = BinOpTypeArrayCat;
2514 table[TokenIdPlusPercent] = BinOpTypeAddWrap;2526 table[TokenIdPlusPercent] = BinOpTypeAddWrap;
2515 table[TokenIdMinusPercent] = BinOpTypeSubWrap;2527 table[TokenIdMinusPercent] = BinOpTypeSubWrap;
2528 table[TokenIdPlusPipe] = BinOpTypeAddSat;
2529 table[TokenIdMinusPipe] = BinOpTypeSubSat;
25162530
2517 BinOpType op = table[pc->token_ids[pc->current_token]];2531 BinOpType op = table[pc->token_ids[pc->current_token]];
2518 if (op != BinOpTypeInvalid) {2532 if (op != BinOpTypeInvalid) {
...@@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {...@@ -2532,6 +2546,7 @@ static AstNode *ast_parse_addition_op(ParseContext *pc) {
2532// / PERCENT2546// / PERCENT
2533// / ASTERISK22547// / ASTERISK2
2534// / ASTERISKPERCENT2548// / ASTERISKPERCENT
2549// / ASTERISKPIPE
2535static AstNode *ast_parse_multiply_op(ParseContext *pc) {2550static AstNode *ast_parse_multiply_op(ParseContext *pc) {
2536 BinOpType table[TokenIdCount] = {};2551 BinOpType table[TokenIdCount] = {};
2537 table[TokenIdBarBar] = BinOpTypeMergeErrorSets;2552 table[TokenIdBarBar] = BinOpTypeMergeErrorSets;
...@@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {...@@ -2540,6 +2555,7 @@ static AstNode *ast_parse_multiply_op(ParseContext *pc) {
2540 table[TokenIdPercent] = BinOpTypeMod;2555 table[TokenIdPercent] = BinOpTypeMod;
2541 table[TokenIdStarStar] = BinOpTypeArrayMult;2556 table[TokenIdStarStar] = BinOpTypeArrayMult;
2542 table[TokenIdTimesPercent] = BinOpTypeMultWrap;2557 table[TokenIdTimesPercent] = BinOpTypeMultWrap;
2558 table[TokenIdTimesPipe] = BinOpTypeMultSat;
25432559
2544 BinOpType op = table[pc->token_ids[pc->current_token]];2560 BinOpType op = table[pc->token_ids[pc->current_token]];
2545 if (op != BinOpTypeInvalid) {2561 if (op != BinOpTypeInvalid) {
src/stage1/tokenizer.cpp+84
...@@ -226,8 +226,10 @@ enum TokenizeState {...@@ -226,8 +226,10 @@ enum TokenizeState {
226 TokenizeState_pipe,226 TokenizeState_pipe,
227 TokenizeState_minus,227 TokenizeState_minus,
228 TokenizeState_minus_percent,228 TokenizeState_minus_percent,
229 TokenizeState_minus_pipe,
229 TokenizeState_asterisk,230 TokenizeState_asterisk,
230 TokenizeState_asterisk_percent,231 TokenizeState_asterisk_percent,
232 TokenizeState_asterisk_pipe,
231 TokenizeState_slash,233 TokenizeState_slash,
232 TokenizeState_line_comment_start,234 TokenizeState_line_comment_start,
233 TokenizeState_line_comment,235 TokenizeState_line_comment,
...@@ -257,8 +259,10 @@ enum TokenizeState {...@@ -257,8 +259,10 @@ enum TokenizeState {
257 TokenizeState_percent,259 TokenizeState_percent,
258 TokenizeState_plus,260 TokenizeState_plus,
259 TokenizeState_plus_percent,261 TokenizeState_plus_percent,
262 TokenizeState_plus_pipe,
260 TokenizeState_angle_bracket_left,263 TokenizeState_angle_bracket_left,
261 TokenizeState_angle_bracket_angle_bracket_left,264 TokenizeState_angle_bracket_angle_bracket_left,
265 TokenizeState_angle_bracket_angle_bracket_left_pipe,
262 TokenizeState_angle_bracket_right,266 TokenizeState_angle_bracket_right,
263 TokenizeState_angle_bracket_angle_bracket_right,267 TokenizeState_angle_bracket_angle_bracket_right,
264 TokenizeState_period,268 TokenizeState_period,
...@@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) {...@@ -548,6 +552,9 @@ void tokenize(const char *source, Tokenization *out) {
548 case '%':552 case '%':
549 t.state = TokenizeState_asterisk_percent;553 t.state = TokenizeState_asterisk_percent;
550 break;554 break;
555 case '|':
556 t.state = TokenizeState_asterisk_pipe;
557 break;
551 default:558 default:
552 t.state = TokenizeState_start;559 t.state = TokenizeState_start;
553 continue;560 continue;
...@@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) {...@@ -568,6 +575,21 @@ void tokenize(const char *source, Tokenization *out) {
568 continue;575 continue;
569 }576 }
570 break;577 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;
571 case TokenizeState_percent:593 case TokenizeState_percent:
572 switch (c) {594 switch (c) {
573 case 0:595 case 0:
...@@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) {...@@ -596,6 +618,9 @@ void tokenize(const char *source, Tokenization *out) {
596 case '%':618 case '%':
597 t.state = TokenizeState_plus_percent;619 t.state = TokenizeState_plus_percent;
598 break;620 break;
621 case '|':
622 t.state = TokenizeState_plus_pipe;
623 break;
599 default:624 default:
600 t.state = TokenizeState_start;625 t.state = TokenizeState_start;
601 continue;626 continue;
...@@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) {...@@ -616,6 +641,21 @@ void tokenize(const char *source, Tokenization *out) {
616 continue;641 continue;
617 }642 }
618 break;643 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;
619 case TokenizeState_caret:659 case TokenizeState_caret:
620 switch (c) {660 switch (c) {
621 case 0:661 case 0:
...@@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) {...@@ -891,6 +931,9 @@ void tokenize(const char *source, Tokenization *out) {
891 case '%':931 case '%':
892 t.state = TokenizeState_minus_percent;932 t.state = TokenizeState_minus_percent;
893 break;933 break;
934 case '|':
935 t.state = TokenizeState_minus_pipe;
936 break;
894 default:937 default:
895 t.state = TokenizeState_start;938 t.state = TokenizeState_start;
896 continue;939 continue;
...@@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) {...@@ -911,6 +954,21 @@ void tokenize(const char *source, Tokenization *out) {
911 continue;954 continue;
912 }955 }
913 break;956 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;
914 case TokenizeState_angle_bracket_left:972 case TokenizeState_angle_bracket_left:
915 switch (c) {973 switch (c) {
916 case 0:974 case 0:
...@@ -936,12 +994,30 @@ void tokenize(const char *source, Tokenization *out) {...@@ -936,12 +994,30 @@ void tokenize(const char *source, Tokenization *out) {
936 t.out->ids.last() = TokenIdBitShiftLeftEq;994 t.out->ids.last() = TokenIdBitShiftLeftEq;
937 t.state = TokenizeState_start;995 t.state = TokenizeState_start;
938 break;996 break;
997 case '|':
998 t.state = TokenizeState_angle_bracket_angle_bracket_left_pipe;
999 break;
939 default:1000 default:
940 t.out->ids.last() = TokenIdBitShiftLeft;1001 t.out->ids.last() = TokenIdBitShiftLeft;
941 t.state = TokenizeState_start;1002 t.state = TokenizeState_start;
942 continue;1003 continue;
943 }1004 }
944 break;1005 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;
945 case TokenizeState_angle_bracket_right:1021 case TokenizeState_angle_bracket_right:
946 switch (c) {1022 switch (c) {
947 case 0:1023 case 0:
...@@ -1437,6 +1513,8 @@ const char * token_name(TokenId id) {...@@ -1437,6 +1513,8 @@ const char * token_name(TokenId id) {
1437 case TokenIdBitOrEq: return "|=";1513 case TokenIdBitOrEq: return "|=";
1438 case TokenIdBitShiftLeft: return "<<";1514 case TokenIdBitShiftLeft: return "<<";
1439 case TokenIdBitShiftLeftEq: return "<<=";1515 case TokenIdBitShiftLeftEq: return "<<=";
1516 case TokenIdBitShiftLeftPipe: return "<<|";
1517 case TokenIdBitShiftLeftPipeEq: return "<<|=";
1440 case TokenIdBitShiftRight: return ">>";1518 case TokenIdBitShiftRight: return ">>";
1441 case TokenIdBitShiftRightEq: return ">>=";1519 case TokenIdBitShiftRightEq: return ">>=";
1442 case TokenIdBitXorEq: return "^=";1520 case TokenIdBitXorEq: return "^=";
...@@ -1521,12 +1599,16 @@ const char * token_name(TokenId id) {...@@ -1521,12 +1599,16 @@ const char * token_name(TokenId id) {
1521 case TokenIdMinusEq: return "-=";1599 case TokenIdMinusEq: return "-=";
1522 case TokenIdMinusPercent: return "-%";1600 case TokenIdMinusPercent: return "-%";
1523 case TokenIdMinusPercentEq: return "-%=";1601 case TokenIdMinusPercentEq: return "-%=";
1602 case TokenIdMinusPipe: return "-|";
1603 case TokenIdMinusPipeEq: return "-|=";
1524 case TokenIdModEq: return "%=";1604 case TokenIdModEq: return "%=";
1525 case TokenIdPercent: return "%";1605 case TokenIdPercent: return "%";
1526 case TokenIdPlus: return "+";1606 case TokenIdPlus: return "+";
1527 case TokenIdPlusEq: return "+=";1607 case TokenIdPlusEq: return "+=";
1528 case TokenIdPlusPercent: return "+%";1608 case TokenIdPlusPercent: return "+%";
1529 case TokenIdPlusPercentEq: return "+%=";1609 case TokenIdPlusPercentEq: return "+%=";
1610 case TokenIdPlusPipe: return "+|";
1611 case TokenIdPlusPipeEq: return "+|=";
1530 case TokenIdPlusPlus: return "++";1612 case TokenIdPlusPlus: return "++";
1531 case TokenIdRBrace: return "}";1613 case TokenIdRBrace: return "}";
1532 case TokenIdRBracket: return "]";1614 case TokenIdRBracket: return "]";
...@@ -1542,6 +1624,8 @@ const char * token_name(TokenId id) {...@@ -1542,6 +1624,8 @@ const char * token_name(TokenId id) {
1542 case TokenIdTimesEq: return "*=";1624 case TokenIdTimesEq: return "*=";
1543 case TokenIdTimesPercent: return "*%";1625 case TokenIdTimesPercent: return "*%";
1544 case TokenIdTimesPercentEq: return "*%=";1626 case TokenIdTimesPercentEq: return "*%=";
1627 case TokenIdTimesPipe: return "*|";
1628 case TokenIdTimesPipeEq: return "*|=";
1545 case TokenIdBuiltin: return "Builtin";1629 case TokenIdBuiltin: return "Builtin";
1546 case TokenIdCount:1630 case TokenIdCount:
1547 zig_unreachable();1631 zig_unreachable();
src/stage1/tokenizer.hpp+8
...@@ -23,6 +23,8 @@ enum TokenId : uint8_t {...@@ -23,6 +23,8 @@ enum TokenId : uint8_t {
23 TokenIdBitOrEq,23 TokenIdBitOrEq,
24 TokenIdBitShiftLeft,24 TokenIdBitShiftLeft,
25 TokenIdBitShiftLeftEq,25 TokenIdBitShiftLeftEq,
26 TokenIdBitShiftLeftPipe,
27 TokenIdBitShiftLeftPipeEq,
26 TokenIdBitShiftRight,28 TokenIdBitShiftRight,
27 TokenIdBitShiftRightEq,29 TokenIdBitShiftRightEq,
28 TokenIdBitXorEq,30 TokenIdBitXorEq,
...@@ -108,12 +110,16 @@ enum TokenId : uint8_t {...@@ -108,12 +110,16 @@ enum TokenId : uint8_t {
108 TokenIdMinusEq,110 TokenIdMinusEq,
109 TokenIdMinusPercent,111 TokenIdMinusPercent,
110 TokenIdMinusPercentEq,112 TokenIdMinusPercentEq,
113 TokenIdMinusPipe,
114 TokenIdMinusPipeEq,
111 TokenIdModEq,115 TokenIdModEq,
112 TokenIdPercent,116 TokenIdPercent,
113 TokenIdPlus,117 TokenIdPlus,
114 TokenIdPlusEq,118 TokenIdPlusEq,
115 TokenIdPlusPercent,119 TokenIdPlusPercent,
116 TokenIdPlusPercentEq,120 TokenIdPlusPercentEq,
121 TokenIdPlusPipe,
122 TokenIdPlusPipeEq,
117 TokenIdPlusPlus,123 TokenIdPlusPlus,
118 TokenIdRBrace,124 TokenIdRBrace,
119 TokenIdRBracket,125 TokenIdRBracket,
...@@ -129,6 +135,8 @@ enum TokenId : uint8_t {...@@ -129,6 +135,8 @@ enum TokenId : uint8_t {
129 TokenIdTimesEq,135 TokenIdTimesEq,
130 TokenIdTimesPercent,136 TokenIdTimesPercent,
131 TokenIdTimesPercentEq,137 TokenIdTimesPercentEq,
138 TokenIdTimesPipe,
139 TokenIdTimesPipeEq,
132140
133 TokenIdCount,141 TokenIdCount,
134};142};
src/translate_c/ast.zig+4-4
...@@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1462,10 +1462,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1462 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),1462 .mul_wrap_assign => return renderBinOp(c, node, .assign_mul_wrap, .asterisk_percent_equal, "*%="),
1463 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),1463 .div => return renderBinOpGrouped(c, node, .div, .slash, "/"),
1464 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),1464 .div_assign => return renderBinOp(c, node, .assign_div, .slash_equal, "/="),
1465 .shl => return renderBinOpGrouped(c, node, .bit_shift_left, .angle_bracket_angle_bracket_left, "<<"),1465 .shl => return renderBinOpGrouped(c, node, .shl, .angle_bracket_angle_bracket_left, "<<"),
1466 .shl_assign => return renderBinOp(c, node, .assign_bit_shift_left, .angle_bracket_angle_bracket_left_equal, "<<="),1466 .shl_assign => return renderBinOp(c, node, .assign_shl, .angle_bracket_angle_bracket_left_equal, "<<="),
1467 .shr => return renderBinOpGrouped(c, node, .bit_shift_right, .angle_bracket_angle_bracket_right, ">>"),1467 .shr => return renderBinOpGrouped(c, node, .shr, .angle_bracket_angle_bracket_right, ">>"),
1468 .shr_assign => return renderBinOp(c, node, .assign_bit_shift_right, .angle_bracket_angle_bracket_right_equal, ">>="),1468 .shr_assign => return renderBinOp(c, node, .assign_shr, .angle_bracket_angle_bracket_right_equal, ">>="),
1469 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),1469 .mod => return renderBinOpGrouped(c, node, .mod, .percent, "%"),
1470 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),1470 .mod_assign => return renderBinOp(c, node, .assign_mod, .percent_equal, "%="),
1471 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),1471 .@"and" => return renderBinOpGrouped(c, node, .bool_and, .keyword_and, "and"),
src/value.zig+87
...@@ -1588,6 +1588,35 @@ pub const Value = extern union {...@@ -1588,6 +1588,35 @@ pub const Value = extern union {
1588 return result;1588 return result;
1589 }1589 }
15901590
1591 /// Supports both floats and ints; handles undefined.
1592 pub fn numberAddSat(
1593 lhs: Value,
1594 rhs: Value,
1595 ty: Type,
1596 arena: *Allocator,
1597 target: Target,
1598 ) !Value {
1599 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1600
1601 if (ty.isAnyFloat()) {
1602 // TODO: handle outside float range
1603 return floatAdd(lhs, rhs, ty, arena);
1604 }
1605 const result = try intAdd(lhs, rhs, arena);
1606
1607 const max = try ty.maxInt(arena, target);
1608 if (compare(result, .gt, max, ty)) {
1609 return max;
1610 }
1611
1612 const min = try ty.minInt(arena, target);
1613 if (compare(result, .lt, min, ty)) {
1614 return min;
1615 }
1616
1617 return result;
1618 }
1619
1591 /// Supports both floats and ints; handles undefined.1620 /// Supports both floats and ints; handles undefined.
1592 pub fn numberSubWrap(1621 pub fn numberSubWrap(
1593 lhs: Value,1622 lhs: Value,
...@@ -1616,6 +1645,35 @@ pub const Value = extern union {...@@ -1616,6 +1645,35 @@ pub const Value = extern union {
1616 return result;1645 return result;
1617 }1646 }
16181647
1648 /// Supports both floats and ints; handles undefined.
1649 pub fn numberSubSat(
1650 lhs: Value,
1651 rhs: Value,
1652 ty: Type,
1653 arena: *Allocator,
1654 target: Target,
1655 ) !Value {
1656 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1657
1658 if (ty.isAnyFloat()) {
1659 // TODO: handle outside float range
1660 return floatSub(lhs, rhs, ty, arena);
1661 }
1662 const result = try intSub(lhs, rhs, arena);
1663
1664 const max = try ty.maxInt(arena, target);
1665 if (compare(result, .gt, max, ty)) {
1666 return max;
1667 }
1668
1669 const min = try ty.minInt(arena, target);
1670 if (compare(result, .lt, min, ty)) {
1671 return min;
1672 }
1673
1674 return result;
1675 }
1676
1619 /// Supports both floats and ints; handles undefined.1677 /// Supports both floats and ints; handles undefined.
1620 pub fn numberMulWrap(1678 pub fn numberMulWrap(
1621 lhs: Value,1679 lhs: Value,
...@@ -1644,6 +1702,35 @@ pub const Value = extern union {...@@ -1644,6 +1702,35 @@ pub const Value = extern union {
1644 return result;1702 return result;
1645 }1703 }
16461704
1705 /// Supports both floats and ints; handles undefined.
1706 pub fn numberMulSat(
1707 lhs: Value,
1708 rhs: Value,
1709 ty: Type,
1710 arena: *Allocator,
1711 target: Target,
1712 ) !Value {
1713 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
1714
1715 if (ty.isAnyFloat()) {
1716 // TODO: handle outside float range
1717 return floatMul(lhs, rhs, ty, arena);
1718 }
1719 const result = try intMul(lhs, rhs, arena);
1720
1721 const max = try ty.maxInt(arena, target);
1722 if (compare(result, .gt, max, ty)) {
1723 return max;
1724 }
1725
1726 const min = try ty.minInt(arena, target);
1727 if (compare(result, .lt, min, ty)) {
1728 return min;
1729 }
1730
1731 return result;
1732 }
1733
1647 /// Supports both floats and ints; handles undefined.1734 /// Supports both floats and ints; handles undefined.
1648 pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {1735 pub fn numberMax(lhs: Value, rhs: Value, arena: *Allocator) !Value {
1649 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);1736 if (lhs.isUndef() or rhs.isUndef()) return Value.initTag(.undef);
test/behavior/saturating_arithmetic.zig+28-31
...@@ -11,16 +11,28 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {...@@ -11,16 +11,28 @@ fn testSaturatingOp(comptime op: Op, comptime T: type, test_data: [3]T) !void {
11 const a = test_data[0];11 const a = test_data[0];
12 const b = test_data[1];12 const b = test_data[1];
13 const expected = test_data[2];13 const expected = test_data[2];
14 const actual = switch (op) {14 {
15 .add => @addWithSaturation(a, b),15 const actual = switch (op) {
16 .sub => @subWithSaturation(a, b),16 .add => a +| b,
17 .mul => @mulWithSaturation(a, b),17 .sub => a -| b,
18 .shl => @shlWithSaturation(a, b),18 .mul => a *| b,
19 };19 .shl => a <<| b,
20 try expectEqual(expected, actual);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 }
21}33}
2234
23test "@addWithSaturation" {35test "saturating add" {
24 const S = struct {36 const S = struct {
25 fn doTheTest() !void {37 fn doTheTest() !void {
26 // .{a, b, expected a+b}38 // .{a, b, expected a+b}
...@@ -38,22 +50,16 @@ test "@addWithSaturation" {...@@ -38,22 +50,16 @@ test "@addWithSaturation" {
38 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });50 try testSaturatingOp(.add, u128, .{ maxInt(u128), 1, maxInt(u128) });
3951
40 const u8x3 = std.meta.Vector(3, u8);52 const u8x3 = std.meta.Vector(3, u8);
41 try expectEqual(u8x3{ 255, 255, 255 }, @addWithSaturation(53 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 254, 1 } +| u8x3{ 1, 2, 255 }));
42 u8x3{ 255, 254, 1 },
43 u8x3{ 1, 2, 255 },
44 ));
45 const i8x3 = std.meta.Vector(3, i8);54 const i8x3 = std.meta.Vector(3, i8);
46 try expectEqual(i8x3{ 127, 127, 127 }, @addWithSaturation(55 try expectEqual(i8x3{ 127, 127, 127 }, (i8x3{ 127, 126, 1 } +| i8x3{ 1, 2, 127 }));
47 i8x3{ 127, 126, 1 },
48 i8x3{ 1, 2, 127 },
49 ));
50 }56 }
51 };57 };
52 try S.doTheTest();58 try S.doTheTest();
53 comptime try S.doTheTest();59 comptime try S.doTheTest();
54}60}
5561
56test "@subWithSaturation" {62test "saturating subtraction" {
57 const S = struct {63 const S = struct {
58 fn doTheTest() !void {64 fn doTheTest() !void {
59 // .{a, b, expected a-b}65 // .{a, b, expected a-b}
...@@ -69,17 +75,14 @@ test "@subWithSaturation" {...@@ -69,17 +75,14 @@ test "@subWithSaturation" {
69 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });75 try testSaturatingOp(.sub, u128, .{ 0, maxInt(u128), 0 });
7076
71 const u8x3 = std.meta.Vector(3, u8);77 const u8x3 = std.meta.Vector(3, u8);
72 try expectEqual(u8x3{ 0, 0, 0 }, @subWithSaturation(78 try expectEqual(u8x3{ 0, 0, 0 }, (u8x3{ 0, 0, 0 } -| u8x3{ 255, 255, 255 }));
73 u8x3{ 0, 0, 0 },
74 u8x3{ 255, 255, 255 },
75 ));
76 }79 }
77 };80 };
78 try S.doTheTest();81 try S.doTheTest();
79 comptime try S.doTheTest();82 comptime try S.doTheTest();
80}83}
8184
82test "@mulWithSaturation" {85test "saturating multiplication" {
83 // TODO: once #9660 has been solved, remove this line86 // TODO: once #9660 has been solved, remove this line
84 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;87 if (std.builtin.target.cpu.arch == .wasm32) return error.SkipZigTest;
8588
...@@ -100,10 +103,7 @@ test "@mulWithSaturation" {...@@ -100,10 +103,7 @@ test "@mulWithSaturation" {
100 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });103 try testSaturatingOp(.mul, u128, .{ maxInt(u128), maxInt(u128), maxInt(u128) });
101104
102 const u8x3 = std.meta.Vector(3, u8);105 const u8x3 = std.meta.Vector(3, u8);
103 try expectEqual(u8x3{ 255, 255, 255 }, @mulWithSaturation(106 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 2, 2, 2 } *| u8x3{ 255, 255, 255 }));
104 u8x3{ 2, 2, 2 },
105 u8x3{ 255, 255, 255 },
106 ));
107 }107 }
108 };108 };
109109
...@@ -111,7 +111,7 @@ test "@mulWithSaturation" {...@@ -111,7 +111,7 @@ test "@mulWithSaturation" {
111 comptime try S.doTheTest();111 comptime try S.doTheTest();
112}112}
113113
114test "@shlWithSaturation" {114test "saturating shift-left" {
115 const S = struct {115 const S = struct {
116 fn doTheTest() !void {116 fn doTheTest() !void {
117 // .{a, b, expected a<<b}117 // .{a, b, expected a<<b}
...@@ -128,10 +128,7 @@ test "@shlWithSaturation" {...@@ -128,10 +128,7 @@ test "@shlWithSaturation" {
128 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });128 try testSaturatingOp(.shl, u8, .{ 255, 1, 255 });
129129
130 const u8x3 = std.meta.Vector(3, u8);130 const u8x3 = std.meta.Vector(3, u8);
131 try expectEqual(u8x3{ 255, 255, 255 }, @shlWithSaturation(131 try expectEqual(u8x3{ 255, 255, 255 }, (u8x3{ 255, 255, 255 } <<| u8x3{ 1, 1, 1 }));
132 u8x3{ 255, 255, 255 },
133 u8x3{ 1, 1, 1 },
134 ));
135 }132 }
136 };133 };
137 try S.doTheTest();134 try S.doTheTest();